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 | |
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>
-rw-r--r-- | arch/x86/include/asm/kvm.h | 9 | ||||
-rw-r--r-- | arch/x86/kvm/i8254.c | 22 | ||||
-rw-r--r-- | arch/x86/kvm/i8254.h | 3 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 59 | ||||
-rw-r--r-- | include/linux/kvm.h | 6 |
5 files changed, 93 insertions, 6 deletions
diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h index 708b9c32a5da..4a5fe914dc59 100644 --- a/arch/x86/include/asm/kvm.h +++ b/arch/x86/include/asm/kvm.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #define __KVM_HAVE_GUEST_DEBUG | 18 | #define __KVM_HAVE_GUEST_DEBUG |
19 | #define __KVM_HAVE_MSIX | 19 | #define __KVM_HAVE_MSIX |
20 | #define __KVM_HAVE_MCE | 20 | #define __KVM_HAVE_MCE |
21 | #define __KVM_HAVE_PIT_STATE2 | ||
21 | 22 | ||
22 | /* Architectural interrupt line count. */ | 23 | /* Architectural interrupt line count. */ |
23 | #define KVM_NR_INTERRUPTS 256 | 24 | #define KVM_NR_INTERRUPTS 256 |
@@ -237,6 +238,14 @@ struct kvm_pit_state { | |||
237 | struct kvm_pit_channel_state channels[3]; | 238 | struct kvm_pit_channel_state channels[3]; |
238 | }; | 239 | }; |
239 | 240 | ||
241 | #define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 | ||
242 | |||
243 | struct kvm_pit_state2 { | ||
244 | struct kvm_pit_channel_state channels[3]; | ||
245 | __u32 flags; | ||
246 | __u32 reserved[9]; | ||
247 | }; | ||
248 | |||
240 | struct kvm_reinject_control { | 249 | struct kvm_reinject_control { |
241 | __u8 pit_reinject; | 250 | __u8 pit_reinject; |
242 | __u8 reserved[31]; | 251 | __u8 reserved[31]; |
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 8c3ac30ef9bd..9b62c57ba6e4 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c | |||
@@ -332,20 +332,33 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val) | |||
332 | case 1: | 332 | case 1: |
333 | /* FIXME: enhance mode 4 precision */ | 333 | /* FIXME: enhance mode 4 precision */ |
334 | case 4: | 334 | case 4: |
335 | create_pit_timer(ps, val, 0); | 335 | if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)) { |
336 | create_pit_timer(ps, val, 0); | ||
337 | } | ||
336 | break; | 338 | break; |
337 | case 2: | 339 | case 2: |
338 | case 3: | 340 | case 3: |
339 | create_pit_timer(ps, val, 1); | 341 | if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)){ |
342 | create_pit_timer(ps, val, 1); | ||
343 | } | ||
340 | break; | 344 | break; |
341 | default: | 345 | default: |
342 | destroy_pit_timer(&ps->pit_timer); | 346 | destroy_pit_timer(&ps->pit_timer); |
343 | } | 347 | } |
344 | } | 348 | } |
345 | 349 | ||
346 | void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val) | 350 | void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start) |
347 | { | 351 | { |
348 | pit_load_count(kvm, channel, val); | 352 | u8 saved_mode; |
353 | if (hpet_legacy_start) { | ||
354 | /* save existing mode for later reenablement */ | ||
355 | saved_mode = kvm->arch.vpit->pit_state.channels[0].mode; | ||
356 | kvm->arch.vpit->pit_state.channels[0].mode = 0xff; /* disable timer */ | ||
357 | pit_load_count(kvm, channel, val); | ||
358 | kvm->arch.vpit->pit_state.channels[0].mode = saved_mode; | ||
359 | } else { | ||
360 | pit_load_count(kvm, channel, val); | ||
361 | } | ||
349 | } | 362 | } |
350 | 363 | ||
351 | static inline struct kvm_pit *dev_to_pit(struct kvm_io_device *dev) | 364 | static inline struct kvm_pit *dev_to_pit(struct kvm_io_device *dev) |
@@ -554,6 +567,7 @@ void kvm_pit_reset(struct kvm_pit *pit) | |||
554 | struct kvm_kpit_channel_state *c; | 567 | struct kvm_kpit_channel_state *c; |
555 | 568 | ||
556 | mutex_lock(&pit->pit_state.lock); | 569 | mutex_lock(&pit->pit_state.lock); |
570 | pit->pit_state.flags = 0; | ||
557 | for (i = 0; i < 3; i++) { | 571 | for (i = 0; i < 3; i++) { |
558 | c = &pit->pit_state.channels[i]; | 572 | c = &pit->pit_state.channels[i]; |
559 | c->mode = 0xff; | 573 | c->mode = 0xff; |
diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h index b2670180f225..d4c1c7ffdc09 100644 --- a/arch/x86/kvm/i8254.h +++ b/arch/x86/kvm/i8254.h | |||
@@ -21,6 +21,7 @@ struct kvm_kpit_channel_state { | |||
21 | 21 | ||
22 | struct kvm_kpit_state { | 22 | struct kvm_kpit_state { |
23 | struct kvm_kpit_channel_state channels[3]; | 23 | struct kvm_kpit_channel_state channels[3]; |
24 | u32 flags; | ||
24 | struct kvm_timer pit_timer; | 25 | struct kvm_timer pit_timer; |
25 | bool is_periodic; | 26 | bool is_periodic; |
26 | u32 speaker_data_on; | 27 | u32 speaker_data_on; |
@@ -49,7 +50,7 @@ struct kvm_pit { | |||
49 | #define KVM_PIT_CHANNEL_MASK 0x3 | 50 | #define KVM_PIT_CHANNEL_MASK 0x3 |
50 | 51 | ||
51 | void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu); | 52 | void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu); |
52 | void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val); | 53 | void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start); |
53 | struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags); | 54 | struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags); |
54 | void kvm_free_pit(struct kvm *kvm); | 55 | void kvm_free_pit(struct kvm *kvm); |
55 | void kvm_pit_reset(struct kvm_pit *pit); | 56 | void kvm_pit_reset(struct kvm_pit *pit); |
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; |
diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 76c640834ea6..a74a1fcc28e9 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h | |||
@@ -409,6 +409,9 @@ struct kvm_guest_debug { | |||
409 | #define KVM_CAP_PIT2 33 | 409 | #define KVM_CAP_PIT2 33 |
410 | #endif | 410 | #endif |
411 | #define KVM_CAP_SET_BOOT_CPU_ID 34 | 411 | #define KVM_CAP_SET_BOOT_CPU_ID 34 |
412 | #ifdef __KVM_HAVE_PIT_STATE2 | ||
413 | #define KVM_CAP_PIT_STATE2 35 | ||
414 | #endif | ||
412 | 415 | ||
413 | #ifdef KVM_CAP_IRQ_ROUTING | 416 | #ifdef KVM_CAP_IRQ_ROUTING |
414 | 417 | ||
@@ -586,6 +589,9 @@ struct kvm_debug_guest { | |||
586 | #define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) | 589 | #define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *) |
587 | #define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) | 590 | #define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *) |
588 | 591 | ||
592 | #define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2) | ||
593 | #define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2) | ||
594 | |||
589 | #define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) | 595 | #define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02) |
590 | #define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) | 596 | #define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03) |
591 | #define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04) | 597 | #define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04) |