Index: usr.sbin/bhyve/Makefile =================================================================== --- usr.sbin/bhyve/Makefile +++ usr.sbin/bhyve/Makefile @@ -67,6 +67,7 @@ usb_mouse.c \ virtio.c \ vga.c \ + vmgenc.c \ xmsr.c \ spinup_ap.c \ iov.c Index: usr.sbin/bhyve/acpi.h =================================================================== --- usr.sbin/bhyve/acpi.h +++ usr.sbin/bhyve/acpi.h @@ -42,9 +42,19 @@ #define IO_PMTMR 0x408 /* 4-byte i/o port for the timer */ +#define IO_GPE0_BLK 0x40c /* 2x 1-byte IO port for GPE0_STS/EN */ +#define IO_GPE0_LEN 0x2 + +#define IO_GPE0_STS IO_GPE0_BLK +#define IO_GPE0_EN (IO_GPE0_BLK + (IO_GPE0_LEN / 2)) + +/* Allocated GPE bits. */ +#define GPE_VMGENC 0 + struct vmctx; int acpi_build(struct vmctx *ctx, int ncpu); +void acpi_raise_gpe(struct vmctx *ctx, unsigned bit); void dsdt_line(const char *fmt, ...); void dsdt_fixed_ioport(uint16_t iobase, uint16_t length); void dsdt_fixed_irq(uint8_t irq); Index: usr.sbin/bhyve/acpi.c =================================================================== --- usr.sbin/bhyve/acpi.c +++ usr.sbin/bhyve/acpi.c @@ -74,6 +74,7 @@ #include "bhyverun.h" #include "acpi.h" #include "pci_emul.h" +#include "vmgenc.h" /* * Define the base address of the ACPI tables, the sizes of some tables, @@ -367,13 +368,13 @@ EFPRINTF(fp, "[0004]\t\tPM2 Control Block Address : 00000000\n"); EFPRINTF(fp, "[0004]\t\tPM Timer Block Address : %08X\n", IO_PMTMR); - EFPRINTF(fp, "[0004]\t\tGPE0 Block Address : 00000000\n"); + EFPRINTF(fp, "[0004]\t\tGPE0 Block Address : %08X\n", IO_GPE0_BLK); EFPRINTF(fp, "[0004]\t\tGPE1 Block Address : 00000000\n"); EFPRINTF(fp, "[0001]\t\tPM1 Event Block Length : 04\n"); EFPRINTF(fp, "[0001]\t\tPM1 Control Block Length : 02\n"); EFPRINTF(fp, "[0001]\t\tPM2 Control Block Length : 00\n"); EFPRINTF(fp, "[0001]\t\tPM Timer Block Length : 04\n"); - EFPRINTF(fp, "[0001]\t\tGPE0 Block Length : 00\n"); + EFPRINTF(fp, "[0001]\t\tGPE0 Block Length : %02x\n", IO_GPE0_LEN); EFPRINTF(fp, "[0001]\t\tGPE1 Block Length : 00\n"); EFPRINTF(fp, "[0001]\t\tGPE1 Base Offset : 00\n"); EFPRINTF(fp, "[0001]\t\t_CST Support : 00\n"); @@ -501,10 +502,10 @@ EFPRINTF(fp, "[0012]\t\tGPE0 Block : [Generic Address Structure]\n"); EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n"); - EFPRINTF(fp, "[0001]\t\tBit Width : 00\n"); + EFPRINTF(fp, "[0001]\t\tBit Width : %02x\n", IO_GPE0_LEN * 8); EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n"); EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 01 [Byte Access:8]\n"); - EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n"); + EFPRINTF(fp, "[0008]\t\tAddress : %016X\n", IO_GPE0_BLK); EFPRINTF(fp, "\n"); EFPRINTF(fp, "[0012]\t\tGPE1 Block : [Generic Address Structure]\n"); @@ -756,6 +757,9 @@ dsdt_line(" })"); dsdt_line(" }"); dsdt_line(" }"); + + vmgenc_write_dsdt(); + dsdt_line("}"); if (dsdt_error != 0) Index: usr.sbin/bhyve/bhyverun.c =================================================================== --- usr.sbin/bhyve/bhyverun.c +++ usr.sbin/bhyve/bhyverun.c @@ -85,6 +85,7 @@ #include "xmsr.h" #include "spinup_ap.h" #include "rtc.h" +#include "vmgenc.h" #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -1174,6 +1175,13 @@ exit(4); } + /* + * Initialize after PCI, to allow a bootrom file to reserve the high + * region. + */ + if (acpi) + vmgenc_init(ctx); + if (dbg_port != 0) init_dbgport(dbg_port); Index: usr.sbin/bhyve/pm.c =================================================================== --- usr.sbin/bhyve/pm.c +++ usr.sbin/bhyve/pm.c @@ -49,6 +49,10 @@ static struct mevent *power_button; static sig_t old_power_handler; +static unsigned gpe0_active; +static unsigned gpe0_enabled; +static const unsigned gpe0_valid = (1u << GPE_VMGENC); + /* * Reset Control register at I/O port 0xcf9. Bit 2 forces a system * reset when it transitions from 0 to 1. Bit 1 selects the type of @@ -144,6 +148,9 @@ need_sci = 1; if ((pm1_enable & PM1_RTC_EN) && (pm1_status & PM1_RTC_STS)) need_sci = 1; + if ((gpe0_enabled & gpe0_active) != 0) + need_sci = 1; + if (need_sci) sci_assert(ctx); else @@ -262,6 +269,64 @@ INOUT_PORT(pm1_control, PM1A_CNT_ADDR, IOPORT_F_INOUT, pm1_control_handler); SYSRES_IO(PM1A_EVT_ADDR, 8); +void +acpi_raise_gpe(struct vmctx *ctx, unsigned bit) +{ + unsigned mask; + + assert(bit < (IO_GPE0_LEN * (8 / 2))); + mask = (1u << bit); + assert((mask & ~gpe0_valid) == 0); + + pthread_mutex_lock(&pm_lock); + gpe0_active |= mask; + sci_update(ctx); + pthread_mutex_unlock(&pm_lock); +} + +static int +gpe0_sts(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + /* + * ACPI 6.2 specifies the GPE register blocks are accessed + * byte-at-a-time. + */ + if (bytes != 1) + return (-1); + + pthread_mutex_lock(&pm_lock); + if (in) + *eax = gpe0_active; + else { + /* W1C */ + gpe0_active &= ~(*eax & gpe0_valid); + sci_update(ctx); + } + pthread_mutex_unlock(&pm_lock); + return (0); +} +INOUT_PORT(gpe0_sts, IO_GPE0_STS, IOPORT_F_INOUT, gpe0_sts); + +static int +gpe0_en(struct vmctx *ctx, int vcpu, int in, int port, int bytes, + uint32_t *eax, void *arg) +{ + if (bytes != 1) + return (-1); + + pthread_mutex_lock(&pm_lock); + if (in) + *eax = gpe0_enabled; + else { + gpe0_enabled = (*eax & gpe0_valid); + sci_update(ctx); + } + pthread_mutex_unlock(&pm_lock); + return (0); +} +INOUT_PORT(gpe0_en, IO_GPE0_EN, IOPORT_F_INOUT, gpe0_en); + /* * ACPI SMI Command Register * Index: usr.sbin/bhyve/vmgenc.h =================================================================== --- usr.sbin/bhyve/vmgenc.h +++ usr.sbin/bhyve/vmgenc.h @@ -1,8 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * - * Copyright (c) 2012 NetApp, Inc. - * All rights reserved. + * Copyright 2020 Conrad Meyer . All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,30 +26,6 @@ * * $FreeBSD$ */ - -#ifndef _ACPI_H_ -#define _ACPI_H_ - -#define SCI_INT 9 - -#define SMI_CMD 0xb2 -#define BHYVE_ACPI_ENABLE 0xa0 -#define BHYVE_ACPI_DISABLE 0xa1 - -#define PM1A_EVT_ADDR 0x400 -#define PM1A_CNT_ADDR 0x404 - -#define IO_PMTMR 0x408 /* 4-byte i/o port for the timer */ - -struct vmctx; - -int acpi_build(struct vmctx *ctx, int ncpu); -void dsdt_line(const char *fmt, ...); -void dsdt_fixed_ioport(uint16_t iobase, uint16_t length); -void dsdt_fixed_irq(uint8_t irq); -void dsdt_fixed_mem32(uint32_t base, uint32_t length); -void dsdt_indent(int levels); -void dsdt_unindent(int levels); -void sci_init(struct vmctx *ctx); - -#endif /* _ACPI_H_ */ +#pragma once +void vmgenc_init(struct vmctx *); +void vmgenc_write_dsdt(void); Index: usr.sbin/bhyve/vmgenc.c =================================================================== --- /dev/null +++ usr.sbin/bhyve/vmgenc.c @@ -0,0 +1,119 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Conrad Meyer . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "acpi.h" +#include "bootrom.h" +#include "vmgenc.h" + +static uint64_t vmgen_gpa; + +void +vmgenc_init(struct vmctx *ctx) +{ + char *region; + int error; + + error = bootrom_alloc(ctx, PAGE_SIZE, PROT_READ, 0, ®ion, + &vmgen_gpa); + if (error != 0) + errx(4, "%s: bootrom_alloc", __func__); + + /* + * It is basically harmless to always generate a random ID when + * starting a VM. + */ + error = getentropy(region, sizeof(struct uuid)); + if (error == -1) + err(4, "%s: getentropy", __func__); + + /* XXX When we have suspend/resume/rollback. */ +#if 0 + acpi_raise_gpe(ctx, GPE_VMGENC); +#endif +} + +void +vmgenc_write_dsdt(void) +{ + dsdt_line(""); + dsdt_indent(1); + dsdt_line("Scope (_SB)"); + dsdt_line("{"); + + dsdt_line(" Device (GENC)"); + dsdt_line(" {"); + + dsdt_indent(2); + dsdt_line("Name (_CID, \"VM_Gen_Counter\")"); + dsdt_line("Method (_HID, 0, NotSerialized)"); + dsdt_line("{"); + dsdt_line(" Return (\"Bhyve_V_Gen_Counter_V1\")"); + dsdt_line("}"); + dsdt_line("Name (_UID, 0)"); + dsdt_line("Name (_DDN, \"VM_Gen_Counter\")"); + dsdt_line("Name (ADDR, Package (0x02)"); + dsdt_line("{"); + dsdt_line(" 0x%08x,", (uint32_t)vmgen_gpa); + dsdt_line(" 0x%08x", (uint32_t)(vmgen_gpa >> 32)); + dsdt_line("})"); + + dsdt_unindent(2); + dsdt_line(" }"); /* Device (GENC) */ + + dsdt_line("}"); /* Scope (_SB) */ + dsdt_line(""); + + dsdt_line("Scope (_GPE)"); + dsdt_line("{"); + dsdt_line(" Method (_E%02x, 0, NotSerialized)", GPE_VMGENC); + dsdt_line(" {"); + dsdt_line(" Notify (\\_SB.GENC, 0x80)"); + dsdt_line(" }"); + dsdt_line("}"); + dsdt_unindent(1); +}