Index: sys/amd64/vmm/io/vatpic.c =================================================================== --- sys/amd64/vmm/io/vatpic.c (revision 265126) +++ sys/amd64/vmm/io/vatpic.c (working copy) @@ -694,3 +694,9 @@ { free(vatpic, M_VATPIC); } + +int +vatpic_imcr_mode(struct vatpic *vatpic) +{ + return (1); /* XXX */ +} Index: sys/amd64/vmm/io/vatpic.h =================================================================== --- sys/amd64/vmm/io/vatpic.h (revision 265126) +++ sys/amd64/vmm/io/vatpic.h (working copy) @@ -52,5 +52,6 @@ void vatpic_pending_intr(struct vm *vm, int *vecptr); void vatpic_intr_accepted(struct vm *vm, int vector); +int vatpic_imcr_mode(struct vatpic *vatpic); #endif /* _VATPIC_H_ */ Index: sys/amd64/vmm/vmm.c =================================================================== --- sys/amd64/vmm/vmm.c (revision 265126) +++ sys/amd64/vmm/vmm.c (working copy) @@ -142,6 +142,8 @@ int suspend; volatile cpuset_t suspended_cpus; + + volatile cpuset_t halted_cpus; }; static int vmm_initialized; @@ -999,6 +1001,25 @@ } /* + * VM exit collateral to fake an 'outb' to the AT keyboard I/O port. The value + * written will cause a virtual machine reset. + */ +static void +vm_exit_atkbd_reset(struct vm *vm, int vcpuid) +{ + struct vm_exit *vmexit; + + vmexit = vm_exitinfo(vm, vcpuid); + vmexit->exitcode = VM_EXITCODE_INOUT; + vmexit->u.inout.bytes = 1; + vmexit->u.inout.in = 0; + vmexit->u.inout.string = 0; + vmexit->u.inout.rep = 0; + vmexit->u.inout.port = 0x64; + vmexit->u.inout.eax = 0xFE; +} + +/* * Emulate a guest 'hlt' by sleeping until the vcpu is ready to run. */ static int @@ -1006,9 +1027,13 @@ { struct vcpu *vcpu; const char *wmesg; - int t; + int t, apic_mode, halted; + KASSERT(!CPU_ISSET(vcpuid, &vm->halted_cpus), ("vcpu already halted")); + vcpu = &vm->vcpu[vcpuid]; + apic_mode = vatpic_imcr_mode(vm->vatpic); + halted = 0; vcpu_lock(vcpu); while (1) { @@ -1032,10 +1057,27 @@ } } - if (vlapic_enabled(vcpu->vlapic)) + /* + * Some Linux guests implement "halt" by having all vcpus + * execute HLT with the APIC disabled. The 'halted_cpus' keep + * track of the vcpus that have entered this state. When all + * vcpus enter the halted state we trigger a VM reset. + */ + if (intr_disabled || + (apic_mode && !vlapic_enabled(vcpu->vlapic))) { + wmesg = "vmhalt"; + if (!halted) { + halted = 1; + CPU_SET_ATOMIC(vcpuid, &vm->halted_cpus); + } + if (CPU_CMP(&vm->halted_cpus, &vm->active_cpus) == 0) { + *retu = true; + vm_exit_atkbd_reset(vm, vcpuid); + break; + } + } else { wmesg = "vmidle"; - else - wmesg = "vmhalt"; + } t = ticks; vcpu_require_state_locked(vcpu, VCPU_SLEEPING); @@ -1043,6 +1085,10 @@ vcpu_require_state_locked(vcpu, VCPU_FROZEN); vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t); } + + if (halted) + CPU_CLR_ATOMIC(vcpuid, &vm->halted_cpus); + vcpu_unlock(vcpu); return (0);