diff options
author | He, Qing <qing.he@intel.com> | 2007-09-03 10:07:41 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2007-10-13 04:18:26 -0400 |
commit | c5ec153402b6d276fe20029da1059ba42a4b55e5 (patch) | |
tree | a323fd0466f606b66fc7239e78569863d62f6300 /drivers/kvm | |
parent | 932f72adbe76f098922c746737cb0bd75fc21e27 (diff) |
KVM: enable in-kernel APIC INIT/SIPI handling
This patch enables INIT/SIPI handling using in-kernel APIC by
introducing a ->mp_state field to emulate the SMP state transition.
[avi: remove smp_processor_id() warning]
Signed-off-by: Qing He <qing.he@intel.com>
Signed-off-by: Xin Li <xin.b.li@intel.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm')
-rw-r--r-- | drivers/kvm/irq.h | 1 | ||||
-rw-r--r-- | drivers/kvm/kvm.h | 7 | ||||
-rw-r--r-- | drivers/kvm/kvm_main.c | 26 | ||||
-rw-r--r-- | drivers/kvm/lapic.c | 43 | ||||
-rw-r--r-- | drivers/kvm/vmx.c | 24 |
5 files changed, 85 insertions, 16 deletions
diff --git a/drivers/kvm/irq.h b/drivers/kvm/irq.h index ec46a09e2135..11fc014e2b30 100644 --- a/drivers/kvm/irq.h +++ b/drivers/kvm/irq.h | |||
@@ -138,6 +138,7 @@ int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu); | |||
138 | int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu); | 138 | int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu); |
139 | int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu); | 139 | int kvm_get_apic_interrupt(struct kvm_vcpu *vcpu); |
140 | int kvm_create_lapic(struct kvm_vcpu *vcpu); | 140 | int kvm_create_lapic(struct kvm_vcpu *vcpu); |
141 | void kvm_lapic_reset(struct kvm_vcpu *vcpu); | ||
141 | void kvm_free_apic(struct kvm_lapic *apic); | 142 | void kvm_free_apic(struct kvm_lapic *apic); |
142 | u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); | 143 | u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); |
143 | void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); | 144 | void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8); |
diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h index dbb929d8a31e..5e318b6e215f 100644 --- a/drivers/kvm/kvm.h +++ b/drivers/kvm/kvm.h | |||
@@ -326,6 +326,13 @@ struct kvm_vcpu { | |||
326 | u64 shadow_efer; | 326 | u64 shadow_efer; |
327 | u64 apic_base; | 327 | u64 apic_base; |
328 | struct kvm_lapic *apic; /* kernel irqchip context */ | 328 | struct kvm_lapic *apic; /* kernel irqchip context */ |
329 | #define VCPU_MP_STATE_RUNNABLE 0 | ||
330 | #define VCPU_MP_STATE_UNINITIALIZED 1 | ||
331 | #define VCPU_MP_STATE_INIT_RECEIVED 2 | ||
332 | #define VCPU_MP_STATE_SIPI_RECEIVED 3 | ||
333 | #define VCPU_MP_STATE_HALTED 4 | ||
334 | int mp_state; | ||
335 | int sipi_vector; | ||
329 | u64 ia32_misc_enable_msr; | 336 | u64 ia32_misc_enable_msr; |
330 | 337 | ||
331 | struct kvm_mmu mmu; | 338 | struct kvm_mmu mmu; |
diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index 02af24e8350c..d0a5a2b3d599 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c | |||
@@ -249,6 +249,10 @@ int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) | |||
249 | vcpu->mmu.root_hpa = INVALID_PAGE; | 249 | vcpu->mmu.root_hpa = INVALID_PAGE; |
250 | vcpu->kvm = kvm; | 250 | vcpu->kvm = kvm; |
251 | vcpu->vcpu_id = id; | 251 | vcpu->vcpu_id = id; |
252 | if (!irqchip_in_kernel(kvm) || id == 0) | ||
253 | vcpu->mp_state = VCPU_MP_STATE_RUNNABLE; | ||
254 | else | ||
255 | vcpu->mp_state = VCPU_MP_STATE_UNINITIALIZED; | ||
252 | init_waitqueue_head(&vcpu->wq); | 256 | init_waitqueue_head(&vcpu->wq); |
253 | 257 | ||
254 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); | 258 | page = alloc_page(GFP_KERNEL | __GFP_ZERO); |
@@ -1371,7 +1375,7 @@ EXPORT_SYMBOL_GPL(emulate_instruction); | |||
1371 | /* | 1375 | /* |
1372 | * The vCPU has executed a HLT instruction with in-kernel mode enabled. | 1376 | * The vCPU has executed a HLT instruction with in-kernel mode enabled. |
1373 | */ | 1377 | */ |
1374 | static void kvm_vcpu_kernel_halt(struct kvm_vcpu *vcpu) | 1378 | static void kvm_vcpu_block(struct kvm_vcpu *vcpu) |
1375 | { | 1379 | { |
1376 | DECLARE_WAITQUEUE(wait, current); | 1380 | DECLARE_WAITQUEUE(wait, current); |
1377 | 1381 | ||
@@ -1380,24 +1384,28 @@ static void kvm_vcpu_kernel_halt(struct kvm_vcpu *vcpu) | |||
1380 | /* | 1384 | /* |
1381 | * We will block until either an interrupt or a signal wakes us up | 1385 | * We will block until either an interrupt or a signal wakes us up |
1382 | */ | 1386 | */ |
1383 | while(!(irqchip_in_kernel(vcpu->kvm) && kvm_cpu_has_interrupt(vcpu)) | 1387 | while (!kvm_cpu_has_interrupt(vcpu) |
1384 | && !vcpu->irq_summary | 1388 | && !signal_pending(current) |
1385 | && !signal_pending(current)) { | 1389 | && vcpu->mp_state != VCPU_MP_STATE_RUNNABLE |
1390 | && vcpu->mp_state != VCPU_MP_STATE_SIPI_RECEIVED) { | ||
1386 | set_current_state(TASK_INTERRUPTIBLE); | 1391 | set_current_state(TASK_INTERRUPTIBLE); |
1387 | vcpu_put(vcpu); | 1392 | vcpu_put(vcpu); |
1388 | schedule(); | 1393 | schedule(); |
1389 | vcpu_load(vcpu); | 1394 | vcpu_load(vcpu); |
1390 | } | 1395 | } |
1391 | 1396 | ||
1397 | __set_current_state(TASK_RUNNING); | ||
1392 | remove_wait_queue(&vcpu->wq, &wait); | 1398 | remove_wait_queue(&vcpu->wq, &wait); |
1393 | set_current_state(TASK_RUNNING); | ||
1394 | } | 1399 | } |
1395 | 1400 | ||
1396 | int kvm_emulate_halt(struct kvm_vcpu *vcpu) | 1401 | int kvm_emulate_halt(struct kvm_vcpu *vcpu) |
1397 | { | 1402 | { |
1398 | ++vcpu->stat.halt_exits; | 1403 | ++vcpu->stat.halt_exits; |
1399 | if (irqchip_in_kernel(vcpu->kvm)) { | 1404 | if (irqchip_in_kernel(vcpu->kvm)) { |
1400 | kvm_vcpu_kernel_halt(vcpu); | 1405 | vcpu->mp_state = VCPU_MP_STATE_HALTED; |
1406 | kvm_vcpu_block(vcpu); | ||
1407 | if (vcpu->mp_state != VCPU_MP_STATE_RUNNABLE) | ||
1408 | return -EINTR; | ||
1401 | return 1; | 1409 | return 1; |
1402 | } else { | 1410 | } else { |
1403 | vcpu->run->exit_reason = KVM_EXIT_HLT; | 1411 | vcpu->run->exit_reason = KVM_EXIT_HLT; |
@@ -2001,6 +2009,12 @@ static int kvm_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2001 | 2009 | ||
2002 | vcpu_load(vcpu); | 2010 | vcpu_load(vcpu); |
2003 | 2011 | ||
2012 | if (unlikely(vcpu->mp_state == VCPU_MP_STATE_UNINITIALIZED)) { | ||
2013 | kvm_vcpu_block(vcpu); | ||
2014 | vcpu_put(vcpu); | ||
2015 | return -EAGAIN; | ||
2016 | } | ||
2017 | |||
2004 | if (vcpu->sigset_active) | 2018 | if (vcpu->sigset_active) |
2005 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); | 2019 | sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); |
2006 | 2020 | ||
diff --git a/drivers/kvm/lapic.c b/drivers/kvm/lapic.c index ca1db3852ace..a190587cf6a5 100644 --- a/drivers/kvm/lapic.c +++ b/drivers/kvm/lapic.c | |||
@@ -312,8 +312,8 @@ static int apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, | |||
312 | static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | 312 | static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, |
313 | int vector, int level, int trig_mode) | 313 | int vector, int level, int trig_mode) |
314 | { | 314 | { |
315 | int result = 0; | 315 | int orig_irr, result = 0; |
316 | int orig_irr; | 316 | struct kvm_vcpu *vcpu = apic->vcpu; |
317 | 317 | ||
318 | switch (delivery_mode) { | 318 | switch (delivery_mode) { |
319 | case APIC_DM_FIXED: | 319 | case APIC_DM_FIXED: |
@@ -335,7 +335,13 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
335 | } else | 335 | } else |
336 | apic_clear_vector(vector, apic->regs + APIC_TMR); | 336 | apic_clear_vector(vector, apic->regs + APIC_TMR); |
337 | 337 | ||
338 | kvm_vcpu_kick(apic->vcpu); | 338 | if (vcpu->mp_state == VCPU_MP_STATE_RUNNABLE) |
339 | kvm_vcpu_kick(vcpu); | ||
340 | else if (vcpu->mp_state == VCPU_MP_STATE_HALTED) { | ||
341 | vcpu->mp_state = VCPU_MP_STATE_RUNNABLE; | ||
342 | if (waitqueue_active(&vcpu->wq)) | ||
343 | wake_up_interruptible(&vcpu->wq); | ||
344 | } | ||
339 | 345 | ||
340 | result = (orig_irr == 0); | 346 | result = (orig_irr == 0); |
341 | break; | 347 | break; |
@@ -352,11 +358,30 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode, | |||
352 | break; | 358 | break; |
353 | 359 | ||
354 | case APIC_DM_INIT: | 360 | case APIC_DM_INIT: |
355 | printk(KERN_DEBUG "Ignoring guest INIT\n"); | 361 | if (level) { |
362 | if (vcpu->mp_state == VCPU_MP_STATE_RUNNABLE) | ||
363 | printk(KERN_DEBUG | ||
364 | "INIT on a runnable vcpu %d\n", | ||
365 | vcpu->vcpu_id); | ||
366 | vcpu->mp_state = VCPU_MP_STATE_INIT_RECEIVED; | ||
367 | kvm_vcpu_kick(vcpu); | ||
368 | } else { | ||
369 | printk(KERN_DEBUG | ||
370 | "Ignoring de-assert INIT to vcpu %d\n", | ||
371 | vcpu->vcpu_id); | ||
372 | } | ||
373 | |||
356 | break; | 374 | break; |
357 | 375 | ||
358 | case APIC_DM_STARTUP: | 376 | case APIC_DM_STARTUP: |
359 | printk(KERN_DEBUG "Ignoring guest STARTUP\n"); | 377 | printk(KERN_DEBUG "SIPI to vcpu %d vector 0x%02x\n", |
378 | vcpu->vcpu_id, vector); | ||
379 | if (vcpu->mp_state == VCPU_MP_STATE_INIT_RECEIVED) { | ||
380 | vcpu->sipi_vector = vector; | ||
381 | vcpu->mp_state = VCPU_MP_STATE_SIPI_RECEIVED; | ||
382 | if (waitqueue_active(&vcpu->wq)) | ||
383 | wake_up_interruptible(&vcpu->wq); | ||
384 | } | ||
360 | break; | 385 | break; |
361 | 386 | ||
362 | default: | 387 | default: |
@@ -792,7 +817,7 @@ u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu) | |||
792 | } | 817 | } |
793 | EXPORT_SYMBOL_GPL(kvm_lapic_get_base); | 818 | EXPORT_SYMBOL_GPL(kvm_lapic_get_base); |
794 | 819 | ||
795 | static void lapic_reset(struct kvm_vcpu *vcpu) | 820 | void kvm_lapic_reset(struct kvm_vcpu *vcpu) |
796 | { | 821 | { |
797 | struct kvm_lapic *apic; | 822 | struct kvm_lapic *apic; |
798 | int i; | 823 | int i; |
@@ -839,6 +864,7 @@ static void lapic_reset(struct kvm_vcpu *vcpu) | |||
839 | vcpu, kvm_apic_id(apic), | 864 | vcpu, kvm_apic_id(apic), |
840 | vcpu->apic_base, apic->base_address); | 865 | vcpu->apic_base, apic->base_address); |
841 | } | 866 | } |
867 | EXPORT_SYMBOL_GPL(kvm_lapic_reset); | ||
842 | 868 | ||
843 | int kvm_lapic_enabled(struct kvm_vcpu *vcpu) | 869 | int kvm_lapic_enabled(struct kvm_vcpu *vcpu) |
844 | { | 870 | { |
@@ -867,7 +893,10 @@ static int __apic_timer_fn(struct kvm_lapic *apic) | |||
867 | 893 | ||
868 | atomic_inc(&apic->timer.pending); | 894 | atomic_inc(&apic->timer.pending); |
869 | if (waitqueue_active(q)) | 895 | if (waitqueue_active(q)) |
896 | { | ||
897 | apic->vcpu->mp_state = VCPU_MP_STATE_RUNNABLE; | ||
870 | wake_up_interruptible(q); | 898 | wake_up_interruptible(q); |
899 | } | ||
871 | if (apic_lvtt_period(apic)) { | 900 | if (apic_lvtt_period(apic)) { |
872 | result = 1; | 901 | result = 1; |
873 | apic->timer.dev.expires = ktime_add_ns( | 902 | apic->timer.dev.expires = ktime_add_ns( |
@@ -928,7 +957,7 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu) | |||
928 | apic->base_address = APIC_DEFAULT_PHYS_BASE; | 957 | apic->base_address = APIC_DEFAULT_PHYS_BASE; |
929 | vcpu->apic_base = APIC_DEFAULT_PHYS_BASE; | 958 | vcpu->apic_base = APIC_DEFAULT_PHYS_BASE; |
930 | 959 | ||
931 | lapic_reset(vcpu); | 960 | kvm_lapic_reset(vcpu); |
932 | apic->dev.read = apic_mmio_read; | 961 | apic->dev.read = apic_mmio_read; |
933 | apic->dev.write = apic_mmio_write; | 962 | apic->dev.write = apic_mmio_write; |
934 | apic->dev.in_range = apic_mmio_range; | 963 | apic->dev.in_range = apic_mmio_range; |
diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index f4618b9edf9c..440cacfda89c 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c | |||
@@ -1412,6 +1412,8 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx) | |||
1412 | goto out; | 1412 | goto out; |
1413 | } | 1413 | } |
1414 | 1414 | ||
1415 | vmx->vcpu.rmode.active = 0; | ||
1416 | |||
1415 | vmx->vcpu.regs[VCPU_REGS_RDX] = get_rdx_init_val(); | 1417 | vmx->vcpu.regs[VCPU_REGS_RDX] = get_rdx_init_val(); |
1416 | set_cr8(&vmx->vcpu, 0); | 1418 | set_cr8(&vmx->vcpu, 0); |
1417 | msr = 0xfee00000 | MSR_IA32_APICBASE_ENABLE; | 1419 | msr = 0xfee00000 | MSR_IA32_APICBASE_ENABLE; |
@@ -1425,8 +1427,13 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx) | |||
1425 | * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode | 1427 | * GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode |
1426 | * insists on having GUEST_CS_BASE == GUEST_CS_SELECTOR << 4. Sigh. | 1428 | * insists on having GUEST_CS_BASE == GUEST_CS_SELECTOR << 4. Sigh. |
1427 | */ | 1429 | */ |
1428 | vmcs_write16(GUEST_CS_SELECTOR, 0xf000); | 1430 | if (vmx->vcpu.vcpu_id == 0) { |
1429 | vmcs_writel(GUEST_CS_BASE, 0x000f0000); | 1431 | vmcs_write16(GUEST_CS_SELECTOR, 0xf000); |
1432 | vmcs_writel(GUEST_CS_BASE, 0x000f0000); | ||
1433 | } else { | ||
1434 | vmcs_write16(GUEST_CS_SELECTOR, vmx->vcpu.sipi_vector << 8); | ||
1435 | vmcs_writel(GUEST_CS_BASE, vmx->vcpu.sipi_vector << 12); | ||
1436 | } | ||
1430 | vmcs_write32(GUEST_CS_LIMIT, 0xffff); | 1437 | vmcs_write32(GUEST_CS_LIMIT, 0xffff); |
1431 | vmcs_write32(GUEST_CS_AR_BYTES, 0x9b); | 1438 | vmcs_write32(GUEST_CS_AR_BYTES, 0x9b); |
1432 | 1439 | ||
@@ -1451,7 +1458,10 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx) | |||
1451 | vmcs_writel(GUEST_SYSENTER_EIP, 0); | 1458 | vmcs_writel(GUEST_SYSENTER_EIP, 0); |
1452 | 1459 | ||
1453 | vmcs_writel(GUEST_RFLAGS, 0x02); | 1460 | vmcs_writel(GUEST_RFLAGS, 0x02); |
1454 | vmcs_writel(GUEST_RIP, 0xfff0); | 1461 | if (vmx->vcpu.vcpu_id == 0) |
1462 | vmcs_writel(GUEST_RIP, 0xfff0); | ||
1463 | else | ||
1464 | vmcs_writel(GUEST_RIP, 0); | ||
1455 | vmcs_writel(GUEST_RSP, 0); | 1465 | vmcs_writel(GUEST_RSP, 0); |
1456 | 1466 | ||
1457 | //todo: dr0 = dr1 = dr2 = dr3 = 0; dr6 = 0xffff0ff0 | 1467 | //todo: dr0 = dr1 = dr2 = dr3 = 0; dr6 = 0xffff0ff0 |
@@ -2201,6 +2211,14 @@ static int vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
2201 | u8 fail; | 2211 | u8 fail; |
2202 | int r; | 2212 | int r; |
2203 | 2213 | ||
2214 | if (unlikely(vcpu->mp_state == VCPU_MP_STATE_SIPI_RECEIVED)) { | ||
2215 | printk("vcpu %d received sipi with vector # %x\n", | ||
2216 | vcpu->vcpu_id, vcpu->sipi_vector); | ||
2217 | kvm_lapic_reset(vcpu); | ||
2218 | vmx_vcpu_setup(vmx); | ||
2219 | vcpu->mp_state = VCPU_MP_STATE_RUNNABLE; | ||
2220 | } | ||
2221 | |||
2204 | preempted: | 2222 | preempted: |
2205 | if (vcpu->guest_debug.enabled) | 2223 | if (vcpu->guest_debug.enabled) |
2206 | kvm_guest_debug_pre(vcpu); | 2224 | kvm_guest_debug_pre(vcpu); |