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 / 2)); 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 @@ -84,6 +84,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 */ @@ -1163,6 +1164,8 @@ rtc_init(ctx, rtc_localtime); sci_init(ctx); + if (acpi) + vmgenc_init(ctx); /* * Exit if a device emulation finds an error in its initilization 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,167 @@ +/*- + * 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 "acpi.h" +#include "mem.h" +#include "vmgenc.h" + +static struct uuid vmgen_id; +static uint64_t vmgen_gpa; + +static int +vmgenc_mem_handler(struct vmctx *ctx __unused, int vcpu __unused, int dir, + uint64_t addr, int size, uint64_t *val, void *arg1 __unused, + long arg2 __unused) +{ + uint64_t offset; + + /* Ignore writes. */ + if (dir == MEM_F_WRITE) + return (0); + + assert(addr >= vmgen_gpa && size <= sizeof(vmgen_id) && + addr + size <= vmgen_gpa + sizeof(vmgen_id) && + ((size == 8 && (addr & 0x7) == 0) || + (size == 4 && (addr & 0x3) == 0))); + + offset = addr - vmgen_gpa; + + if (size == 8) + *val = *(uint64_t *)((char *)&vmgen_id + offset); + else + *val = *(uint32_t *)((char *)&vmgen_id + offset); + return (0); +} + +void +vmgenc_init(struct vmctx *ctx) +{ + struct mem_range mr; + uint32_t lowmem_gpa; + int error; + + /* + * Slice off some PCI hole memory, which is not allocatable to the + * guest operating system's general use. + */ + lowmem_gpa = vm_get_lowmem_limit(ctx); + /* + * GUID must be 8-byte aligned, per spec. + */ + lowmem_gpa = roundup2(lowmem_gpa, 8); + vmgen_gpa = lowmem_gpa; + /* + * Ostensibly it needs to be cachable, but we don't attempt to enforce + * that at all. + */ + vm_set_lowmem_limit(ctx, lowmem_gpa + sizeof(vmgen_id)); + + /* + * It is basically harmless to always generate a random ID when + * starting a VM. + */ + error = getentropy(&vmgen_id, sizeof(vmgen_id)); + if (error == -1) + err(4, "vmgenc: getentropy"); + + mr = (struct mem_range) { + .name = "vmgenc", + .base = vmgen_gpa, + .size = sizeof(vmgen_id), + .flags = MEM_F_READ | MEM_F_IMMUTABLE, + .handler = vmgenc_mem_handler, + }; + error = register_mem(&mr); + if (error != 0) + errc(4, error, "vmgenc: register_mem"); + + /* 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); +}