Index: sys/amd64/vmm/io/vatpic.c =================================================================== --- sys/amd64/vmm/io/vatpic.c (revision 265166) +++ sys/amd64/vmm/io/vatpic.c (working copy) @@ -85,6 +85,8 @@ struct mtx mtx; struct atpic atpic[2]; uint8_t elc[2]; + uint8_t imcr_addr; + uint8_t imcr_data; }; #define VATPIC_CTR0(vatpic, fmt) \ @@ -676,6 +678,57 @@ return (0); } +/* + * IMCR is documented in section "PIC Mode" of the MPTable Spec version 1.4. + * + * The _PIC method in bhyve's ACPI also updates the IMCR register so the + * PIC mode is always available in the IMCR even if the guest is using ACPI. + */ +int +vatpic_imcr_handler(void *vm, int vcpuid, bool in, int port, int bytes, + uint32_t *eax) +{ + struct vatpic *vatpic; + + KASSERT(port == IO_IMCR_ADDR || port == IO_IMCR_DATA, + ("vatpic_imcr_handler: invalid i/o port %#x", port)); + + if (bytes != 1) + return (-1); + + vatpic = vm_atpic(vm); + VATPIC_LOCK(vatpic); + if (in) { + if (port == IO_IMCR_ADDR) + *eax = vatpic->imcr_addr; + else + *eax = vatpic->imcr_data; + } else { + if (port == IO_IMCR_ADDR) { + vatpic->imcr_addr = *eax; + } else { + if (vatpic->imcr_addr == 0x70) { + vatpic->imcr_data = *eax; + VATPIC_CTR1(vatpic, "PIC mode set to %#x", + vatpic->imcr_data); + } else { + VATPIC_CTR2(vatpic, "Ignoring write to IMCR " + "addr %#x data %#x", + vatpic->imcr_addr, (uint8_t)*eax); + } + } + } + VATPIC_UNLOCK(vatpic); + return (0); +} + +int +vatpic_pic_mode(struct vatpic *vatpic) +{ + + return (vatpic->imcr_data); +} + struct vatpic * vatpic_init(struct vm *vm) { Index: sys/amd64/vmm/io/vatpic.h =================================================================== --- sys/amd64/vmm/io/vatpic.h (revision 265166) +++ sys/amd64/vmm/io/vatpic.h (working copy) @@ -33,6 +33,9 @@ #define ICU_IMR_OFFSET 1 +#define IO_IMCR_ADDR 0x22 +#define IO_IMCR_DATA 0x23 + #define IO_ELCR1 0x4d0 #define IO_ELCR2 0x4d1 @@ -45,6 +48,9 @@ uint32_t *eax); int vatpic_elc_handler(void *vm, int vcpuid, bool in, int port, int bytes, uint32_t *eax); +int vatpic_imcr_handler(void *vm, int vcpuid, bool in, int port, int bytes, + uint32_t *eax); +int vatpic_pic_mode(struct vatpic *vatpic); int vatpic_assert_irq(struct vm *vm, int irq); int vatpic_deassert_irq(struct vm *vm, int irq); Index: sys/amd64/vmm/vmm.c =================================================================== --- sys/amd64/vmm/vmm.c (revision 265166) +++ 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_pic_mode(vm->vatpic); + halted = 0; vcpu_lock(vcpu); while (1) { @@ -1032,10 +1057,31 @@ } } - 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))) { + VCPU_CTR1(vm, vcpuid, "Halted because of %s disabled", + intr_disabled ? "interrupts" : "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); + VCPU_CTR0(vm, vcpuid, "Writing to keyboard I/O " + "port to trigger virtual machine reset"); + break; + } + } else { wmesg = "vmidle"; - else - wmesg = "vmhalt"; + } t = ticks; vcpu_require_state_locked(vcpu, VCPU_SLEEPING); @@ -1043,6 +1089,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); Index: sys/amd64/vmm/vmm_ioport.c =================================================================== --- sys/amd64/vmm/vmm_ioport.c (revision 265166) +++ sys/amd64/vmm/vmm_ioport.c (working copy) @@ -53,6 +53,8 @@ [IO_ICU2 + ICU_IMR_OFFSET] = vatpic_slave_handler, [IO_ELCR1] = vatpic_elc_handler, [IO_ELCR2] = vatpic_elc_handler, + [IO_IMCR_ADDR] = vatpic_imcr_handler, + [IO_IMCR_DATA] = vatpic_imcr_handler, }; int Index: usr.sbin/bhyve/acpi.c =================================================================== --- usr.sbin/bhyve/acpi.c (revision 265166) +++ usr.sbin/bhyve/acpi.c (working copy) @@ -710,6 +710,22 @@ dsdt_line(" Zero,"); dsdt_line(" })"); + dsdt_line(""); + dsdt_line(" Method (_PIC, 1, NotSerialized) // _PIC: Interrupt Model"); + dsdt_line(" {"); + dsdt_line(" Store (0x70, IMCA)"); + dsdt_line(" Store (ARG0, IMCD)"); + dsdt_line(" }"); + + /* IMCR address and data ports */ + dsdt_line(""); + dsdt_line(" OperationRegion (IMCR, SystemIO, 0x22, 2)"); + dsdt_line(" Field(IMCR, ByteAcc, NoLock, Preserve)"); + dsdt_line(" {"); + dsdt_line(" IMCA, 8,"); + dsdt_line(" IMCD, 8"); + dsdt_line(" }"); + pci_write_dsdt(); dsdt_line(""); Index: usr.sbin/bhyve/mptbl.c =================================================================== --- usr.sbin/bhyve/mptbl.c (revision 265166) +++ usr.sbin/bhyve/mptbl.c (working copy) @@ -107,6 +107,7 @@ mpfp->pap = gpa + sizeof(*mpfp); mpfp->length = 1; mpfp->spec_rev = MP_SPECREV; + mpfp->mpfb2 = MPFB2_IMCR_PRESENT; mpfp->checksum = mpt_compute_checksum(mpfp, sizeof(*mpfp)); } Index: usr.sbin/bhyve/pci_lpc.c =================================================================== --- usr.sbin/bhyve/pci_lpc.c (revision 265166) +++ usr.sbin/bhyve/pci_lpc.c (working copy) @@ -60,6 +60,11 @@ #define NMISC_PORT 0x61 SYSRES_IO(NMISC_PORT, 1); +#define IMCR_ADDR 0x22 +#define IMCR_DATA 0x23 +SYSRES_IO(IMCR_ADDR, 1); +SYSRES_IO(IMCR_DATA, 1); + static struct pci_devinst *lpc_bridge; #define LPC_UART_NUM 2