diff options
author | Beth Kon <eak@us.ibm.com> | 2009-07-07 11:50:38 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2009-09-10 01:33:12 -0400 |
commit | e9f4275732add046fed4a548b8dbb98dbe500d2f (patch) | |
tree | 8d9e22bbc6a9557f95712aaa98534d5983034252 /arch/x86/kvm/x86.c | |
parent | 0d1de2d901f4ba0972a3886496a44fb1d3300dbd (diff) |
KVM: PIT support for HPET legacy mode
When kvm is in hpet_legacy_mode, the hpet is providing the timer
interrupt and the pit should not be. So in legacy mode, the pit timer
is destroyed, but the *state* of the pit is maintained. So if kvm or
the guest tries to modify the state of the pit, this modification is
accepted, *except* that the timer isn't actually started. When we exit
hpet_legacy_mode, the current state of the pit (which is up to date
since we've been accepting modifications) is used to restart the pit
timer.
The saved_mode code in kvm_pit_load_count temporarily changes mode to
0xff in order to destroy the timer, but then restores the actual
value, again maintaining "current" state of the pit for possible later
reenablement.
[avi: add some reserved storage in the ioctl; make SET_PIT2 IOW]
[marcelo: fix memory corruption due to reserved storage]
Signed-off-by: Beth Kon <eak@us.ibm.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/x86/kvm/x86.c')
-rw-r--r-- | arch/x86/kvm/x86.c | 59 |
1 files changed, 58 insertions, 1 deletions
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index dfb0e37b3c65..2214384ff610 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -1213,6 +1213,7 @@ int kvm_dev_ioctl_check_extension(long ext) | |||
1213 | case KVM_CAP_ASSIGN_DEV_IRQ: | 1213 | case KVM_CAP_ASSIGN_DEV_IRQ: |
1214 | case KVM_CAP_IRQFD: | 1214 | case KVM_CAP_IRQFD: |
1215 | case KVM_CAP_PIT2: | 1215 | case KVM_CAP_PIT2: |
1216 | case KVM_CAP_PIT_STATE2: | ||
1216 | r = 1; | 1217 | r = 1; |
1217 | break; | 1218 | break; |
1218 | case KVM_CAP_COALESCED_MMIO: | 1219 | case KVM_CAP_COALESCED_MMIO: |
@@ -2078,7 +2079,36 @@ static int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps) | |||
2078 | 2079 | ||
2079 | mutex_lock(&kvm->arch.vpit->pit_state.lock); | 2080 | mutex_lock(&kvm->arch.vpit->pit_state.lock); |
2080 | memcpy(&kvm->arch.vpit->pit_state, ps, sizeof(struct kvm_pit_state)); | 2081 | memcpy(&kvm->arch.vpit->pit_state, ps, sizeof(struct kvm_pit_state)); |
2081 | kvm_pit_load_count(kvm, 0, ps->channels[0].count); | 2082 | kvm_pit_load_count(kvm, 0, ps->channels[0].count, 0); |
2083 | mutex_unlock(&kvm->arch.vpit->pit_state.lock); | ||
2084 | return r; | ||
2085 | } | ||
2086 | |||
2087 | static int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) | ||
2088 | { | ||
2089 | int r = 0; | ||
2090 | |||
2091 | mutex_lock(&kvm->arch.vpit->pit_state.lock); | ||
2092 | memcpy(ps->channels, &kvm->arch.vpit->pit_state.channels, | ||
2093 | sizeof(ps->channels)); | ||
2094 | ps->flags = kvm->arch.vpit->pit_state.flags; | ||
2095 | mutex_unlock(&kvm->arch.vpit->pit_state.lock); | ||
2096 | return r; | ||
2097 | } | ||
2098 | |||
2099 | static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps) | ||
2100 | { | ||
2101 | int r = 0, start = 0; | ||
2102 | u32 prev_legacy, cur_legacy; | ||
2103 | mutex_lock(&kvm->arch.vpit->pit_state.lock); | ||
2104 | prev_legacy = kvm->arch.vpit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY; | ||
2105 | cur_legacy = ps->flags & KVM_PIT_FLAGS_HPET_LEGACY; | ||
2106 | if (!prev_legacy && cur_legacy) | ||
2107 | start = 1; | ||
2108 | memcpy(&kvm->arch.vpit->pit_state.channels, &ps->channels, | ||
2109 | sizeof(kvm->arch.vpit->pit_state.channels)); | ||
2110 | kvm->arch.vpit->pit_state.flags = ps->flags; | ||
2111 | kvm_pit_load_count(kvm, 0, kvm->arch.vpit->pit_state.channels[0].count, start); | ||
2082 | mutex_unlock(&kvm->arch.vpit->pit_state.lock); | 2112 | mutex_unlock(&kvm->arch.vpit->pit_state.lock); |
2083 | return r; | 2113 | return r; |
2084 | } | 2114 | } |
@@ -2140,6 +2170,7 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
2140 | */ | 2170 | */ |
2141 | union { | 2171 | union { |
2142 | struct kvm_pit_state ps; | 2172 | struct kvm_pit_state ps; |
2173 | struct kvm_pit_state2 ps2; | ||
2143 | struct kvm_memory_alias alias; | 2174 | struct kvm_memory_alias alias; |
2144 | struct kvm_pit_config pit_config; | 2175 | struct kvm_pit_config pit_config; |
2145 | } u; | 2176 | } u; |
@@ -2322,6 +2353,32 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
2322 | r = 0; | 2353 | r = 0; |
2323 | break; | 2354 | break; |
2324 | } | 2355 | } |
2356 | case KVM_GET_PIT2: { | ||
2357 | r = -ENXIO; | ||
2358 | if (!kvm->arch.vpit) | ||
2359 | goto out; | ||
2360 | r = kvm_vm_ioctl_get_pit2(kvm, &u.ps2); | ||
2361 | if (r) | ||
2362 | goto out; | ||
2363 | r = -EFAULT; | ||
2364 | if (copy_to_user(argp, &u.ps2, sizeof(u.ps2))) | ||
2365 | goto out; | ||
2366 | r = 0; | ||
2367 | break; | ||
2368 | } | ||
2369 | case KVM_SET_PIT2: { | ||
2370 | r = -EFAULT; | ||
2371 | if (copy_from_user(&u.ps2, argp, sizeof(u.ps2))) | ||
2372 | goto out; | ||
2373 | r = -ENXIO; | ||
2374 | if (!kvm->arch.vpit) | ||
2375 | goto out; | ||
2376 | r = kvm_vm_ioctl_set_pit2(kvm, &u.ps2); | ||
2377 | if (r) | ||
2378 | goto out; | ||
2379 | r = 0; | ||
2380 | break; | ||
2381 | } | ||
2325 | case KVM_REINJECT_CONTROL: { | 2382 | case KVM_REINJECT_CONTROL: { |
2326 | struct kvm_reinject_control control; | 2383 | struct kvm_reinject_control control; |
2327 | r = -EFAULT; | 2384 | r = -EFAULT; |