Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F145242082
D34897.id104954.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
94 KB
Referenced Files
None
Subscribers
None
D34897.id104954.diff
View Options
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -15,9 +15,11 @@
BHYVE_SYSDIR?=${SRCTOP}
SRCS= \
+ acpi_device.c \
atkbdc.c \
acpi.c \
audio.c \
+ basl.c \
bhyvegc.c \
bhyverun.c \
block_if.c \
@@ -61,6 +63,8 @@
post.c \
ps2kbd.c \
ps2mouse.c \
+ qemu_fwcfg.c \
+ qemu_loader.c \
rfb.c \
rtc.c \
smbiostbl.c \
diff --git a/usr.sbin/bhyve/acpi.h b/usr.sbin/bhyve/acpi.h
--- a/usr.sbin/bhyve/acpi.h
+++ b/usr.sbin/bhyve/acpi.h
@@ -31,6 +31,8 @@
#ifndef _ACPI_H_
#define _ACPI_H_
+#include "acpi_device.h"
+
#define SCI_INT 9
#define SMI_CMD 0xb2
@@ -55,6 +57,7 @@
int acpi_build(struct vmctx *ctx, int ncpu);
void acpi_raise_gpe(struct vmctx *ctx, unsigned bit);
+int acpi_tables_add_device(const struct acpi_device *const dev);
void dsdt_line(const char *fmt, ...);
void dsdt_fixed_ioport(uint16_t iobase, uint16_t length);
void dsdt_fixed_irq(uint8_t irq);
diff --git a/usr.sbin/bhyve/acpi.c b/usr.sbin/bhyve/acpi.c
--- a/usr.sbin/bhyve/acpi.c
+++ b/usr.sbin/bhyve/acpi.c
@@ -38,20 +38,6 @@
*
* The tables are placed in the guest's ROM area just below 1MB physical,
* above the MPTable.
- *
- * Layout (No longer correct at FADT and beyond due to properly
- * calculating the size of the MADT to allow for changes to
- * VM_MAXCPU above 21 which overflows this layout.)
- * ------
- * RSDP -> 0xf2400 (36 bytes fixed)
- * RSDT -> 0xf2440 (36 bytes + 4*7 table addrs, 4 used)
- * XSDT -> 0xf2480 (36 bytes + 8*7 table addrs, 4 used)
- * MADT -> 0xf2500 (depends on #CPUs)
- * FADT -> 0xf2600 (268 bytes)
- * HPET -> 0xf2740 (56 bytes)
- * MCFG -> 0xf2780 (60 bytes)
- * FACS -> 0xf27C0 (64 bytes)
- * DSDT -> 0xf2800 (variable - can go up to 0x100000)
*/
#include <sys/cdefs.h>
@@ -61,6 +47,7 @@
#include <sys/errno.h>
#include <sys/stat.h>
+#include <err.h>
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
@@ -73,44 +60,21 @@
#include "bhyverun.h"
#include "acpi.h"
+#include "basl.h"
#include "pci_emul.h"
#include "vmgenc.h"
-/*
- * Define the base address of the ACPI tables, the sizes of some tables,
- * and the offsets to the individual tables,
- */
-#define BHYVE_ACPI_BASE 0xf2400
-#define RSDT_OFFSET 0x040
-#define XSDT_OFFSET 0x080
-#define MADT_OFFSET 0x100
-/*
- * The MADT consists of:
- * 44 Fixed Header
- * 8 * maxcpu Processor Local APIC entries
- * 12 I/O APIC entry
- * 2 * 10 Interrupt Source Override entries
- * 6 Local APIC NMI entry
- */
-#define MADT_SIZE roundup2((44 + basl_ncpu*8 + 12 + 2*10 + 6), 0x100)
-#define FADT_OFFSET (MADT_OFFSET + MADT_SIZE)
-#define FADT_SIZE 0x140
-#define HPET_OFFSET (FADT_OFFSET + FADT_SIZE)
-#define HPET_SIZE 0x40
-#define MCFG_OFFSET (HPET_OFFSET + HPET_SIZE)
-#define MCFG_SIZE 0x40
-#define FACS_OFFSET (MCFG_OFFSET + MCFG_SIZE)
-#define FACS_SIZE 0x40
-#define DSDT_OFFSET (FACS_OFFSET + FACS_SIZE)
-
#define BHYVE_ASL_TEMPLATE "bhyve.XXXXXXX"
#define BHYVE_ASL_SUFFIX ".aml"
#define BHYVE_ASL_COMPILER "/usr/sbin/iasl"
+#define BHYVE_ADDRESS_IOAPIC 0xFEC00000
+#define BHYVE_ADDRESS_HPET 0xFED00000
+#define BHYVE_ADDRESS_LAPIC 0xFEE00000
+
static int basl_keep_temps;
static int basl_verbose_iasl;
static int basl_ncpu;
-static uint32_t basl_acpi_base = BHYVE_ACPI_BASE;
static uint32_t hpet_capabilities;
/*
@@ -127,6 +91,9 @@
static int dsdt_indent_level;
static int dsdt_error;
+struct basl_table *rsdt;
+struct basl_table *xsdt;
+
struct basl_fio {
int fd;
FILE *fp;
@@ -139,512 +106,28 @@
#define EFFLUSH(x) \
if (fflush(x) != 0) goto err_exit;
-static int
-basl_fwrite_rsdp(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve RSDP template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0008]\t\tSignature : \"RSD PTR \"\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 43\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 02\n");
- EFPRINTF(fp, "[0004]\t\tRSDT Address : %08X\n",
- basl_acpi_base + RSDT_OFFSET);
- EFPRINTF(fp, "[0004]\t\tLength : 00000024\n");
- EFPRINTF(fp, "[0008]\t\tXSDT Address : 00000000%08X\n",
- basl_acpi_base + XSDT_OFFSET);
- EFPRINTF(fp, "[0001]\t\tExtended Checksum : 00\n");
- EFPRINTF(fp, "[0003]\t\tReserved : 000000\n");
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_rsdt(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve RSDT template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"RSDT\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVRSDT \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "\n");
-
- /* Add in pointers to the MADT, FADT and HPET */
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : %08X\n",
- basl_acpi_base + MADT_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : %08X\n",
- basl_acpi_base + FADT_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : %08X\n",
- basl_acpi_base + HPET_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : %08X\n",
- basl_acpi_base + MCFG_OFFSET);
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_xsdt(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve XSDT template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"XSDT\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVXSDT \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "\n");
-
- /* Add in pointers to the MADT, FADT and HPET */
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : 00000000%08X\n",
- basl_acpi_base + MADT_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : 00000000%08X\n",
- basl_acpi_base + FADT_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : 00000000%08X\n",
- basl_acpi_base + HPET_OFFSET);
- EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : 00000000%08X\n",
- basl_acpi_base + MCFG_OFFSET);
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
+/*
+ * A list for additional ACPI devices like a TPM.
+ */
+struct acpi_device_list_entry {
+ SLIST_ENTRY(acpi_device_list_entry) chain;
+ const struct acpi_device *dev;
+};
+SLIST_HEAD(acpi_device_list,
+ acpi_device_list_entry) acpi_devices = SLIST_HEAD_INITIALIZER(acpi_devices);
-static int
-basl_fwrite_madt(FILE *fp)
+int
+acpi_tables_add_device(const struct acpi_device *const dev)
{
- int i;
-
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve MADT template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"APIC\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVMADT \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
-
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0004]\t\tLocal Apic Address : FEE00000\n");
- EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
- EFPRINTF(fp, "\t\t\tPC-AT Compatibility : 1\n");
- EFPRINTF(fp, "\n");
-
- /* Add a Processor Local APIC entry for each CPU */
- for (i = 0; i < basl_ncpu; i++) {
- EFPRINTF(fp, "[0001]\t\tSubtable Type : 00\n");
- EFPRINTF(fp, "[0001]\t\tLength : 08\n");
- /* iasl expects hex values for the proc and apic id's */
- EFPRINTF(fp, "[0001]\t\tProcessor ID : %02x\n", i);
- EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02x\n", i);
- EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
- EFPRINTF(fp, "\t\t\tProcessor Enabled : 1\n");
- EFPRINTF(fp, "\t\t\tRuntime Online Capable : 0\n");
- EFPRINTF(fp, "\n");
+ struct acpi_device_list_entry *const entry = calloc(1, sizeof(*entry));
+ if (entry == NULL) {
+ return (ENOMEM);
}
- /* Always a single IOAPIC entry, with ID 0 */
- EFPRINTF(fp, "[0001]\t\tSubtable Type : 01\n");
- EFPRINTF(fp, "[0001]\t\tLength : 0C\n");
- /* iasl expects a hex value for the i/o apic id */
- EFPRINTF(fp, "[0001]\t\tI/O Apic ID : %02x\n", 0);
- EFPRINTF(fp, "[0001]\t\tReserved : 00\n");
- EFPRINTF(fp, "[0004]\t\tAddress : fec00000\n");
- EFPRINTF(fp, "[0004]\t\tInterrupt : 00000000\n");
- EFPRINTF(fp, "\n");
-
- /* Legacy IRQ0 is connected to pin 2 of the IOAPIC */
- EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
- EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
- EFPRINTF(fp, "[0001]\t\tBus : 00\n");
- EFPRINTF(fp, "[0001]\t\tSource : 00\n");
- EFPRINTF(fp, "[0004]\t\tInterrupt : 00000002\n");
- EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0005\n");
- EFPRINTF(fp, "\t\t\tPolarity : 1\n");
- EFPRINTF(fp, "\t\t\tTrigger Mode : 1\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0001]\t\tSubtable Type : 02\n");
- EFPRINTF(fp, "[0001]\t\tLength : 0A\n");
- EFPRINTF(fp, "[0001]\t\tBus : 00\n");
- EFPRINTF(fp, "[0001]\t\tSource : %02X\n", SCI_INT);
- EFPRINTF(fp, "[0004]\t\tInterrupt : %08X\n", SCI_INT);
- EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0000\n");
- EFPRINTF(fp, "\t\t\tPolarity : 3\n");
- EFPRINTF(fp, "\t\t\tTrigger Mode : 3\n");
- EFPRINTF(fp, "\n");
-
- /* Local APIC NMI is connected to LINT 1 on all CPUs */
- EFPRINTF(fp, "[0001]\t\tSubtable Type : 04\n");
- EFPRINTF(fp, "[0001]\t\tLength : 06\n");
- EFPRINTF(fp, "[0001]\t\tProcessor ID : FF\n");
- EFPRINTF(fp, "[0002]\t\tFlags (decoded below) : 0005\n");
- EFPRINTF(fp, "\t\t\tPolarity : 1\n");
- EFPRINTF(fp, "\t\t\tTrigger Mode : 1\n");
- EFPRINTF(fp, "[0001]\t\tInterrupt Input LINT : 01\n");
- EFPRINTF(fp, "\n");
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_fadt(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve FADT template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"FACP\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 0000010C\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 05\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVFACP \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0004]\t\tFACS Address : %08X\n",
- basl_acpi_base + FACS_OFFSET);
- EFPRINTF(fp, "[0004]\t\tDSDT Address : %08X\n",
- basl_acpi_base + DSDT_OFFSET);
- EFPRINTF(fp, "[0001]\t\tModel : 01\n");
- EFPRINTF(fp, "[0001]\t\tPM Profile : 00 [Unspecified]\n");
- EFPRINTF(fp, "[0002]\t\tSCI Interrupt : %04X\n",
- SCI_INT);
- EFPRINTF(fp, "[0004]\t\tSMI Command Port : %08X\n",
- SMI_CMD);
- EFPRINTF(fp, "[0001]\t\tACPI Enable Value : %02X\n",
- BHYVE_ACPI_ENABLE);
- EFPRINTF(fp, "[0001]\t\tACPI Disable Value : %02X\n",
- BHYVE_ACPI_DISABLE);
- EFPRINTF(fp, "[0001]\t\tS4BIOS Command : 00\n");
- EFPRINTF(fp, "[0001]\t\tP-State Control : 00\n");
- EFPRINTF(fp, "[0004]\t\tPM1A Event Block Address : %08X\n",
- PM1A_EVT_ADDR);
- EFPRINTF(fp, "[0004]\t\tPM1B Event Block Address : 00000000\n");
- EFPRINTF(fp, "[0004]\t\tPM1A Control Block Address : %08X\n",
- PM1A_CNT_ADDR);
- EFPRINTF(fp, "[0004]\t\tPM1B Control Block Address : 00000000\n");
- 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 : %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 : %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");
- EFPRINTF(fp, "[0002]\t\tC2 Latency : 0000\n");
- EFPRINTF(fp, "[0002]\t\tC3 Latency : 0000\n");
- EFPRINTF(fp, "[0002]\t\tCPU Cache Size : 0000\n");
- EFPRINTF(fp, "[0002]\t\tCache Flush Stride : 0000\n");
- EFPRINTF(fp, "[0001]\t\tDuty Cycle Offset : 00\n");
- EFPRINTF(fp, "[0001]\t\tDuty Cycle Width : 00\n");
- EFPRINTF(fp, "[0001]\t\tRTC Day Alarm Index : 00\n");
- EFPRINTF(fp, "[0001]\t\tRTC Month Alarm Index : 00\n");
- EFPRINTF(fp, "[0001]\t\tRTC Century Index : 32\n");
- EFPRINTF(fp, "[0002]\t\tBoot Flags (decoded below) : 0000\n");
- EFPRINTF(fp, "\t\t\tLegacy Devices Supported (V2) : 0\n");
- EFPRINTF(fp, "\t\t\t8042 Present on ports 60/64 (V2) : 0\n");
- EFPRINTF(fp, "\t\t\tVGA Not Present (V4) : 1\n");
- EFPRINTF(fp, "\t\t\tMSI Not Supported (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tPCIe ASPM Not Supported (V4) : 1\n");
- EFPRINTF(fp, "\t\t\tCMOS RTC Not Present (V5) : 0\n");
- EFPRINTF(fp, "[0001]\t\tReserved : 00\n");
- EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000000\n");
- EFPRINTF(fp, "\t\t\tWBINVD instruction is operational (V1) : 1\n");
- EFPRINTF(fp, "\t\t\tWBINVD flushes all caches (V1) : 0\n");
- EFPRINTF(fp, "\t\t\tAll CPUs support C1 (V1) : 1\n");
- EFPRINTF(fp, "\t\t\tC2 works on MP system (V1) : 0\n");
- EFPRINTF(fp, "\t\t\tControl Method Power Button (V1) : 0\n");
- EFPRINTF(fp, "\t\t\tControl Method Sleep Button (V1) : 1\n");
- EFPRINTF(fp, "\t\t\tRTC wake not in fixed reg space (V1) : 0\n");
- EFPRINTF(fp, "\t\t\tRTC can wake system from S4 (V1) : 0\n");
- EFPRINTF(fp, "\t\t\t32-bit PM Timer (V1) : 1\n");
- EFPRINTF(fp, "\t\t\tDocking Supported (V1) : 0\n");
- EFPRINTF(fp, "\t\t\tReset Register Supported (V2) : 1\n");
- EFPRINTF(fp, "\t\t\tSealed Case (V3) : 0\n");
- EFPRINTF(fp, "\t\t\tHeadless - No Video (V3) : 1\n");
- EFPRINTF(fp, "\t\t\tUse native instr after SLP_TYPx (V3) : 0\n");
- EFPRINTF(fp, "\t\t\tPCIEXP_WAK Bits Supported (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tUse Platform Timer (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tRTC_STS valid on S4 wake (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tRemote Power-on capable (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tUse APIC Cluster Model (V4) : 0\n");
- EFPRINTF(fp, "\t\t\tUse APIC Physical Destination Mode (V4) : 1\n");
- EFPRINTF(fp, "\t\t\tHardware Reduced (V5) : 0\n");
- EFPRINTF(fp, "\t\t\tLow Power S0 Idle (V5) : 0\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tReset Register : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
- 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 : 0000000000000CF9\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0001]\t\tValue to cause reset : 06\n");
- EFPRINTF(fp, "[0002]\t\tARM Flags (decoded below): 0000\n");
- EFPRINTF(fp, "\t\t\tPSCI Compliant : 0\n");
- EFPRINTF(fp, "\t\t\tMust use HVC for PSCI : 0\n");
- EFPRINTF(fp, "[0001]\t\tFADT Minor Revision : 01\n");
- EFPRINTF(fp, "[0008]\t\tFACS Address : 00000000%08X\n",
- basl_acpi_base + FACS_OFFSET);
- EFPRINTF(fp, "[0008]\t\tDSDT Address : 00000000%08X\n",
- basl_acpi_base + DSDT_OFFSET);
- EFPRINTF(fp,
- "[0012]\t\tPM1A Event Block : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 20\n");
- EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
- EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
- PM1A_EVT_ADDR);
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tPM1B Event 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 Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tPM1A Control Block : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 10\n");
- EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
- EFPRINTF(fp, "[0001]\t\tEncoded Access Width : 02 [Word Access:16]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
- PM1A_CNT_ADDR);
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tPM1B Control 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 Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tPM2 Control Block : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
- EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
- EFPRINTF(fp, "\n");
-
- /* Valid for bhyve */
- EFPRINTF(fp,
- "[0012]\t\tPM Timer Block : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 20\n");
- EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 03 [DWord Access:32]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 00000000%08X\n",
- IO_PMTMR);
- EFPRINTF(fp, "\n");
-
- 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 : %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 : %016X\n", IO_GPE0_BLK);
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0012]\t\tGPE1 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 Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 0000000000000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tSleep Control Register : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
- 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, "\n");
-
- EFPRINTF(fp,
- "[0012]\t\tSleep Status Register : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 01 [SystemIO]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 08\n");
- 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");
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_hpet(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve HPET template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"HPET\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVHPET \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
-
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0004]\t\tHardware Block ID : %08X\n", hpet_capabilities);
- EFPRINTF(fp,
- "[0012]\t\tTimer Block Register : [Generic Address Structure]\n");
- EFPRINTF(fp, "[0001]\t\tSpace ID : 00 [SystemMemory]\n");
- EFPRINTF(fp, "[0001]\t\tBit Width : 00\n");
- EFPRINTF(fp, "[0001]\t\tBit Offset : 00\n");
- EFPRINTF(fp,
- "[0001]\t\tEncoded Access Width : 00 [Undefined/Legacy]\n");
- EFPRINTF(fp, "[0008]\t\tAddress : 00000000FED00000\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0001]\t\tSequence Number : 00\n");
- EFPRINTF(fp, "[0002]\t\tMinimum Clock Ticks : 0000\n");
- EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n");
- EFPRINTF(fp, "\t\t\t4K Page Protect : 1\n");
- EFPRINTF(fp, "\t\t\t64K Page Protect : 0\n");
- EFPRINTF(fp, "\n");
-
- EFFLUSH(fp);
-
- return (0);
-
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_mcfg(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve MCFG template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"MCFG\"\n");
- EFPRINTF(fp, "[0004]\t\tTable Length : 00000000\n");
- EFPRINTF(fp, "[0001]\t\tRevision : 01\n");
- EFPRINTF(fp, "[0001]\t\tChecksum : 00\n");
- EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n");
- EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVMCFG \"\n");
- EFPRINTF(fp, "[0004]\t\tOem Revision : 00000001\n");
-
- /* iasl will fill in the compiler ID/revision fields */
- EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n");
- EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n");
- EFPRINTF(fp, "[0008]\t\tReserved : 0\n");
- EFPRINTF(fp, "\n");
-
- EFPRINTF(fp, "[0008]\t\tBase Address : %016lX\n", pci_ecfg_base());
- EFPRINTF(fp, "[0002]\t\tSegment Group Number : 0000\n");
- EFPRINTF(fp, "[0001]\t\tStart Bus Number : 00\n");
- EFPRINTF(fp, "[0001]\t\tEnd Bus Number : FF\n");
- EFPRINTF(fp, "[0004]\t\tReserved : 0\n");
- EFFLUSH(fp);
- return (0);
-err_exit:
- return (errno);
-}
-
-static int
-basl_fwrite_facs(FILE *fp)
-{
- EFPRINTF(fp, "/*\n");
- EFPRINTF(fp, " * bhyve FACS template\n");
- EFPRINTF(fp, " */\n");
- EFPRINTF(fp, "[0004]\t\tSignature : \"FACS\"\n");
- EFPRINTF(fp, "[0004]\t\tLength : 00000040\n");
- EFPRINTF(fp, "[0004]\t\tHardware Signature : 00000000\n");
- EFPRINTF(fp, "[0004]\t\t32 Firmware Waking Vector : 00000000\n");
- EFPRINTF(fp, "[0004]\t\tGlobal Lock : 00000000\n");
- EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000000\n");
- EFPRINTF(fp, "\t\t\tS4BIOS Support Present : 0\n");
- EFPRINTF(fp, "\t\t\t64-bit Wake Supported (V2) : 0\n");
- EFPRINTF(fp,
- "[0008]\t\t64 Firmware Waking Vector : 0000000000000000\n");
- EFPRINTF(fp, "[0001]\t\tVersion : 02\n");
- EFPRINTF(fp, "[0003]\t\tReserved : 000000\n");
- EFPRINTF(fp, "[0004]\t\tOspmFlags (decoded below) : 00000000\n");
- EFPRINTF(fp, "\t\t\t64-bit Wake Env Required (V2) : 0\n");
-
- EFFLUSH(fp);
+ entry->dev = dev;
+ SLIST_INSERT_HEAD(&acpi_devices, entry, chain);
return (0);
-
-err_exit:
- return (errno);
}
/*
@@ -731,8 +214,10 @@
dsdt_line("/*");
dsdt_line(" * bhyve DSDT template");
dsdt_line(" */");
- dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"DSDT\", 2,"
- "\"BHYVE \", \"BVDSDT \", 0x00000001)");
+ dsdt_line("DefinitionBlock (\"bhyve_dsdt.aml\", \"%s\", 0x%02x,"
+ "\"%s\", \"%s\", 0x%08x)",
+ ACPI_SIG_DSDT, BASL_REVISION_DSDT, BASL_OEM_ID,
+ BASL_OEM_TABLE_ID_DSDT, BASL_OEM_REVISION_DSDT);
dsdt_line("{");
dsdt_line(" Name (_S5, Package ()");
dsdt_line(" {");
@@ -760,6 +245,11 @@
vmgenc_write_dsdt();
+ const struct acpi_device_list_entry *entry;
+ SLIST_FOREACH(entry, &acpi_devices, chain) {
+ acpi_device_write_dsdt(entry->dev);
+ }
+
dsdt_line("}");
if (dsdt_error != 0)
@@ -835,26 +325,33 @@
}
static int
-basl_load(struct vmctx *ctx, int fd, uint64_t off)
+basl_load(struct vmctx *ctx, int fd)
{
struct stat sb;
- void *gaddr;
+ void *addr;
if (fstat(fd, &sb) < 0)
return (errno);
- gaddr = paddr_guest2host(ctx, basl_acpi_base + off, sb.st_size);
- if (gaddr == NULL)
+ addr = calloc(1, sb.st_size);
+ if (addr == NULL)
return (EFAULT);
- if (read(fd, gaddr, sb.st_size) < 0)
+ if (read(fd, addr, sb.st_size) < 0)
return (errno);
+ struct basl_table *table;
+
+ uint8_t name[ACPI_NAMESEG_SIZE + 1] = { 0 };
+ memcpy(name, addr, sizeof(name) - 1 /* last char is '\0' */);
+ BASL_EXEC(basl_table_create(&table, ctx, name, BASL_TABLE_ALIGNMENT));
+ BASL_EXEC(basl_table_append_bytes(table, addr, sb.st_size));
+
return (0);
}
static int
-basl_compile(struct vmctx *ctx, int (*fwrite_section)(FILE *), uint64_t offset)
+basl_compile(struct vmctx *ctx, int (*fwrite_section)(FILE *))
{
struct basl_fio io[2];
static char iaslbuf[3*MAXPATHLEN + 10];
@@ -888,7 +385,7 @@
* Copy the aml output file into guest
* memory at the specified location
*/
- err = basl_load(ctx, io[1].fd, offset);
+ err = basl_load(ctx, io[1].fd);
}
}
basl_end(&io[0], &io[1]);
@@ -943,6 +440,429 @@
return (err);
}
+static int
+build_dsdt(struct vmctx *const ctx)
+{
+ BASL_EXEC(basl_compile(ctx, basl_fwrite_dsdt));
+
+ return (0);
+}
+
+static int
+build_facs(struct vmctx *const ctx)
+{
+ struct basl_table *facs;
+
+ BASL_EXEC(basl_table_create(&facs, ctx, ACPI_SIG_FACS,
+ BASL_TABLE_ALIGNMENT_FACS));
+
+ /* Signature */
+ BASL_EXEC(
+ basl_table_append_bytes(facs, ACPI_SIG_FACS, ACPI_NAMESEG_SIZE));
+ /* Length */
+ BASL_EXEC(basl_table_append_length(facs, 4));
+ /* Hardware Signature */
+ BASL_EXEC(basl_table_append_int(facs, 0, 4));
+ /* Firmware Waking Vector */
+ BASL_EXEC(basl_table_append_int(facs, 0, 4));
+ /* Global Lock */
+ BASL_EXEC(basl_table_append_int(facs, 0, 4));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(facs, 0, 4));
+ /* Extended Firmware Waking Vector */
+ BASL_EXEC(basl_table_append_int(facs, 0, 8));
+ /* Version */
+ BASL_EXEC(basl_table_append_int(facs, 2, 1));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(facs, 0, 3));
+ /* OSPM Flags */
+ BASL_EXEC(basl_table_append_int(facs, 0, 4));
+ /* Reserved */
+ const uint8_t reserved[24] = { 0 };
+ BASL_EXEC(basl_table_append_bytes(facs, reserved, 24));
+
+ return (0);
+}
+
+static int
+build_fadt(struct vmctx *const ctx)
+{
+ struct basl_table *fadt;
+
+ BASL_EXEC(
+ basl_table_create(&fadt, ctx, ACPI_SIG_FADT, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(fadt, ACPI_SIG_FADT, BASL_REVISION_FADT,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_FADT, BASL_OEM_REVISION_FADT));
+ /* FACS Address */
+ BASL_EXEC(basl_table_append_pointer(fadt, ACPI_SIG_FACS,
+ ACPI_RSDT_ENTRY_SIZE));
+ /* DSDT Address */
+ BASL_EXEC(basl_table_append_pointer(fadt, ACPI_SIG_DSDT,
+ ACPI_RSDT_ENTRY_SIZE));
+ /* Eeserved */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* Preferred_PM_Profile [Unspecified] */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* SCI Interrupt */
+ BASL_EXEC(basl_table_append_int(fadt, SCI_INT, 2));
+ /* SMI Command Port */
+ BASL_EXEC(basl_table_append_int(fadt, SMI_CMD, 4));
+ /* ACPI Enable Value */
+ BASL_EXEC(basl_table_append_int(fadt, BHYVE_ACPI_ENABLE, 1));
+ /* ACPI Disable Value */
+ BASL_EXEC(basl_table_append_int(fadt, BHYVE_ACPI_DISABLE, 1));
+ /* S4BIOS Command */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* P-State Control */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* PM1A Event Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, PM1A_EVT_ADDR, 4));
+ /* PM1B Event Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 4));
+ /* PM1A Control Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, PM1A_CNT_ADDR, 4));
+ /* PM1B Control Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 4));
+ /* PM2 Control Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 4));
+ /* PM Timer Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, IO_PMTMR, 4));
+ /* GPE0 Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, IO_GPE0_BLK, 4));
+ /* GPE1 Block Address */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 4));
+ /* PM1 Event Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, 4, 1));
+ /* PM1 Control Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, 2, 1));
+ /* PM2 Control Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* PM Timer Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, 4, 1));
+ /* GPE0 Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, IO_GPE0_LEN, 1));
+ /* GPE1 Block Length */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* GPE1 Base Offset */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* _CST Support */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* C2 Latency */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 2));
+ /* C3 Latency */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 2));
+ /* CPU Cache Size */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 2));
+ /* Cache Flush Stride */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 2));
+ /* Duty Cycle Offset */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* Duty Cycle Width */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* RTC Day Alarm Index */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* RTC Month Alarm Index */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* RTC Centyr Index */
+ BASL_EXEC(basl_table_append_int(fadt, 32, 1));
+ /* Boot Flags */
+ BASL_EXEC(basl_table_append_int(fadt,
+ ACPI_FADT_NO_VGA | ACPI_FADT_NO_ASPM, 2));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 1));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(fadt,
+ ACPI_FADT_WBINVD | ACPI_FADT_C1_SUPPORTED | ACPI_FADT_SLEEP_BUTTON |
+ ACPI_FADT_32BIT_TIMER | ACPI_FADT_RESET_REGISTER |
+ ACPI_FADT_HEADLESS | ACPI_FADT_APIC_PHYSICAL,
+ 4));
+ /* Reset Register */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 8, 0,
+ ACPI_GAS_ACCESS_WIDTH_BYTE, 0xCF9));
+ /* Reset Value */
+ BASL_EXEC(basl_table_append_int(fadt, 6, 1));
+ /* ARM Boot Architecture Flags */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 2));
+ /* FADT Minor Version */
+ BASL_EXEC(basl_table_append_int(fadt, 1, 1));
+ /* Extended FACS Address */
+ BASL_EXEC(basl_table_append_pointer(fadt, ACPI_SIG_FACS,
+ ACPI_XSDT_ENTRY_SIZE));
+ /* Extended DSDT Address */
+ BASL_EXEC(basl_table_append_pointer(fadt, ACPI_SIG_DSDT,
+ ACPI_XSDT_ENTRY_SIZE));
+ /* Extended PM1A Event Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0x20, 0,
+ ACPI_GAS_ACCESS_WIDTH_WORD, PM1A_EVT_ADDR));
+ /* Extended PM1B Event Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0, 0,
+ ACPI_GAS_ACCESS_WIDTH_UNDEFINED, 0));
+ /* Extended PM1A Control Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0x10, 0,
+ ACPI_GAS_ACCESS_WIDTH_WORD, PM1A_CNT_ADDR));
+ /* Extended PM1B Control Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0, 0,
+ ACPI_GAS_ACCESS_WIDTH_UNDEFINED, 0));
+ /* Extended PM2 Control Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 8, 0,
+ ACPI_GAS_ACCESS_WIDTH_UNDEFINED, 0));
+ /* Extended PM Timer Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0x20, 0,
+ ACPI_GAS_ACCESS_WIDTH_DWORD, IO_PMTMR));
+ /* Extended GPE0 Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO,
+ IO_GPE0_LEN * 8, 0, ACPI_GAS_ACCESS_WIDTH_BYTE, IO_GPE0_BLK));
+ /* Extended GPE1 Block Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 0, 0,
+ ACPI_GAS_ACCESS_WIDTH_UNDEFINED, 0));
+ /* Sleep Control Register Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 8, 0,
+ ACPI_GAS_ACCESS_WIDTH_BYTE, 0));
+ /* Sleep Status Register Address */
+ BASL_EXEC(basl_table_append_gas(fadt, ACPI_ADR_SPACE_SYSTEM_IO, 8, 0,
+ ACPI_GAS_ACCESS_WIDTH_BYTE, 0));
+ /* Hypervisor Vendor Identity */
+ BASL_EXEC(basl_table_append_int(fadt, 0, 8));
+
+ BASL_EXEC(basl_table_append_pointer(rsdt, ACPI_SIG_FADT,
+ ACPI_RSDT_ENTRY_SIZE));
+ BASL_EXEC(basl_table_append_pointer(xsdt, ACPI_SIG_FADT,
+ ACPI_XSDT_ENTRY_SIZE));
+
+ return (0);
+}
+
+static int
+build_hpet(struct vmctx *const ctx)
+{
+ struct basl_table *hpet;
+
+ BASL_EXEC(
+ basl_table_create(&hpet, ctx, ACPI_SIG_HPET, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(hpet, ACPI_SIG_HPET, BASL_REVISION_HPET,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_HPET, BASL_OEM_REVISION_HPET));
+ /* Hardware Block ID */
+ BASL_EXEC(basl_table_append_int(hpet, hpet_capabilities, 4));
+ /* Timer Block Register */
+ BASL_EXEC(basl_table_append_gas(hpet, ACPI_ADR_SPACE_SYSTEM_MEMORY, 0,
+ 0, 0, BHYVE_ADDRESS_HPET));
+ /* Sequence Number */
+ BASL_EXEC(basl_table_append_int(hpet, 0, 1));
+ /* Minimum Clock Ticks */
+ BASL_EXEC(basl_table_append_int(hpet, 0, 2));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(hpet, ACPI_HPET_PAGE_PROTECT4, 4));
+
+ BASL_EXEC(basl_table_append_pointer(rsdt, ACPI_SIG_HPET,
+ ACPI_RSDT_ENTRY_SIZE));
+ BASL_EXEC(basl_table_append_pointer(xsdt, ACPI_SIG_HPET,
+ ACPI_XSDT_ENTRY_SIZE));
+
+ return (0);
+}
+
+static int
+build_madt(struct vmctx *const ctx)
+{
+ struct basl_table *madt;
+
+ BASL_EXEC(
+ basl_table_create(&madt, ctx, ACPI_SIG_MADT, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(madt, ACPI_SIG_MADT, BASL_REVISION_MADT,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_MADT, BASL_OEM_REVISION_MADT));
+ /* Local Apic Address */
+ BASL_EXEC(basl_table_append_int(madt, BHYVE_ADDRESS_LAPIC, 4));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(madt, ACPI_MADT_PCAT_COMPAT, 4));
+
+ /* Local APIC for each CPU */
+ for (int i = 0; i < basl_ncpu; ++i) {
+ /* Type */
+ BASL_EXEC(
+ basl_table_append_int(madt, ACPI_MADT_TYPE_LOCAL_APIC, 1));
+ /* Length */
+ BASL_EXEC(basl_table_append_int(madt, 8, 1));
+ /* ACPI Processor UID */
+ BASL_EXEC(basl_table_append_int(madt, i, 1));
+ /* APIC ID */
+ BASL_EXEC(basl_table_append_int(madt, i, 1));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(madt, ACPI_MADT_ENABLED, 4));
+ }
+
+ /* I/O APIC */
+ /* Type */
+ BASL_EXEC(basl_table_append_int(madt, ACPI_MADT_TYPE_IO_APIC, 1));
+ /* Length */
+ BASL_EXEC(basl_table_append_int(madt, 12, 1));
+ /* I/O APIC ID */
+ BASL_EXEC(basl_table_append_int(madt, 0, 1));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(madt, 0, 1));
+ /* I/O APIC Address */
+ BASL_EXEC(basl_table_append_int(madt, BHYVE_ADDRESS_IOAPIC, 4));
+ /* Interrupt Base */
+ BASL_EXEC(basl_table_append_int(madt, 0, 4));
+
+ /* Legacy IRQ0 is connected to pin 2 of the I/O APIC */
+ /* Type */
+ BASL_EXEC(
+ basl_table_append_int(madt, ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, 1));
+ /* Length */
+ BASL_EXEC(basl_table_append_int(madt, 10, 1));
+ /* Bus */
+ BASL_EXEC(basl_table_append_int(madt, 0, 1));
+ /* Source */
+ BASL_EXEC(basl_table_append_int(madt, 0, 1));
+ /* Interrupt */
+ BASL_EXEC(basl_table_append_int(madt, 2, 4));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(madt,
+ ACPI_MADT_POLARITY_ACTIVE_LOW | ACPI_MADT_TRIGGER_LEVEL, 2));
+
+ /* Type */
+ BASL_EXEC(
+ basl_table_append_int(madt, ACPI_MADT_TYPE_INTERRUPT_OVERRIDE, 1));
+ /* Length */
+ BASL_EXEC(basl_table_append_int(madt, 10, 1));
+ /* Bus */
+ BASL_EXEC(basl_table_append_int(madt, 0, 1));
+ /* Source */
+ BASL_EXEC(basl_table_append_int(madt, SCI_INT, 1));
+ /* Interrupt */
+ BASL_EXEC(basl_table_append_int(madt, SCI_INT, 4));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(madt,
+ ACPI_MADT_POLARITY_ACTIVE_LOW | ACPI_MADT_TRIGGER_LEVEL, 2));
+
+ /* Local APIC NMI is conntected to LINT 1 on all CPUs */
+ /* Type */
+ BASL_EXEC(
+ basl_table_append_int(madt, ACPI_MADT_TYPE_LOCAL_APIC_NMI, 1));
+ /* Length */
+ BASL_EXEC(basl_table_append_int(madt, 6, 1));
+ /* Processor UID */
+ BASL_EXEC(basl_table_append_int(madt, 0xFF, 1));
+ /* Flags */
+ BASL_EXEC(basl_table_append_int(madt,
+ ACPI_MADT_POLARITY_ACTIVE_HIGH | ACPI_MADT_TRIGGER_EDGE, 2));
+ /* Local APIC LINT */
+ BASL_EXEC(basl_table_append_int(madt, 1, 1));
+
+ BASL_EXEC(basl_table_append_pointer(rsdt, ACPI_SIG_MADT,
+ ACPI_RSDT_ENTRY_SIZE));
+ BASL_EXEC(basl_table_append_pointer(xsdt, ACPI_SIG_MADT,
+ ACPI_XSDT_ENTRY_SIZE));
+
+ return (0);
+}
+
+static int
+build_mcfg(struct vmctx *const ctx)
+{
+ struct basl_table *mcfg;
+
+ BASL_EXEC(
+ basl_table_create(&mcfg, ctx, ACPI_SIG_MCFG, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(mcfg, ACPI_SIG_MCFG, BASL_REVISION_MCFG,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_MCFG, BASL_OEM_REVISION_MCFG));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(mcfg, 0, 8));
+ /* Base Address */
+ BASL_EXEC(basl_table_append_int(mcfg, pci_ecfg_base(), 8));
+ /* Segment Group Number */
+ BASL_EXEC(basl_table_append_int(mcfg, 0, 2));
+ /* Start Bus Number */
+ BASL_EXEC(basl_table_append_int(mcfg, 0, 1));
+ /* End Bus Number */
+ BASL_EXEC(basl_table_append_int(mcfg, 0xFF, 1));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(mcfg, 0, 4));
+
+ BASL_EXEC(basl_table_append_pointer(rsdt, ACPI_SIG_MCFG,
+ ACPI_RSDT_ENTRY_SIZE));
+ BASL_EXEC(basl_table_append_pointer(xsdt, ACPI_SIG_MCFG,
+ ACPI_XSDT_ENTRY_SIZE));
+
+ return (0);
+}
+
+static int
+build_rsdp(struct vmctx *const ctx)
+{
+ struct basl_table *rsdp;
+
+ BASL_EXEC(basl_table_create(&rsdp, ctx, ACPI_RSDP_NAME,
+ BASL_TABLE_ALIGNMENT));
+
+ /* Signature */
+ BASL_EXEC(basl_table_append_bytes(rsdp, ACPI_SIG_RSDP,
+ sizeof(ACPI_SIG_RSDP) - 1));
+ /* Checksum (patched by guest) */
+ BASL_EXEC(basl_table_append_checksum(rsdp, 0, 20));
+ /* OEM Id */
+ BASL_EXEC(basl_table_append_bytes(rsdp, BASL_OEM_ID, ACPI_OEM_ID_SIZE));
+ /* Revision */
+ BASL_EXEC(basl_table_append_int(rsdp, BASL_REVISION_RSDP, 1));
+ /* RSDT Address (patched by guest) */
+ BASL_EXEC(basl_table_append_pointer(rsdp, ACPI_SIG_RSDT,
+ ACPI_RSDT_ENTRY_SIZE));
+ /* Length (patched by basl_finish) */
+ BASL_EXEC(basl_table_append_length(rsdp, sizeof(UINT32)));
+ /* XSDT Address (patched by guest) */
+ BASL_EXEC(basl_table_append_pointer(rsdp, ACPI_SIG_XSDT,
+ ACPI_XSDT_ENTRY_SIZE));
+ /* Extended Checksum (patched by guest) */
+ BASL_EXEC(basl_table_append_checksum(rsdp, 0,
+ BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
+ /* Reserved */
+ BASL_EXEC(basl_table_append_int(rsdp, 0, 3));
+
+ return (0);
+}
+
+static int
+build_rsdt(struct vmctx *const ctx)
+{
+ BASL_EXEC(
+ basl_table_create(&rsdt, ctx, ACPI_SIG_RSDT, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(rsdt, ACPI_SIG_RSDT, BASL_REVISION_RSDT,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_RSDT, BASL_OEM_REVISION_RSDT));
+ /* Pointers (added by other build_XXX funcs) */
+
+ return (0);
+}
+
+static int
+build_xsdt(struct vmctx *const ctx)
+{
+ BASL_EXEC(
+ basl_table_create(&xsdt, ctx, ACPI_SIG_XSDT, BASL_TABLE_ALIGNMENT));
+
+ /* Header */
+ BASL_EXEC(
+ basl_table_append_header(xsdt, ACPI_SIG_XSDT, BASL_REVISION_XSDT,
+ BASL_OEM_ID, BASL_OEM_TABLE_ID_XSDT, BASL_OEM_REVISION_XSDT));
+ /* Pointers (added by other build_XXX funcs) */
+
+ return (0);
+}
+
int
acpi_build(struct vmctx *ctx, int ncpu)
{
@@ -968,30 +888,29 @@
if (getenv("BHYVE_ACPI_KEEPTMPS"))
basl_keep_temps = 1;
- err = basl_make_templates();
+ BASL_EXEC(basl_init());
+
+ BASL_EXEC(basl_make_templates());
/*
* Run through all the ASL files, compiling them and
* copying them into guest memory
+ *
+ * According to UEFI Specification v6.3 chapter 5.1 the FADT should be
+ * the first table pointed to by XSDT. For that reason, build it as
+ * first table after XSDT.
*/
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_rsdp, 0);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_rsdt, RSDT_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_xsdt, XSDT_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_madt, MADT_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_fadt, FADT_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_hpet, HPET_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_mcfg, MCFG_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_facs, FACS_OFFSET);
- if (err == 0)
- err = basl_compile(ctx, basl_fwrite_dsdt, DSDT_OFFSET);
+ BASL_EXEC(build_rsdp(ctx));
+ BASL_EXEC(build_rsdt(ctx));
+ BASL_EXEC(build_xsdt(ctx));
+ BASL_EXEC(build_fadt(ctx));
+ BASL_EXEC(build_madt(ctx));
+ BASL_EXEC(build_hpet(ctx));
+ BASL_EXEC(build_mcfg(ctx));
+ BASL_EXEC(build_facs(ctx));
+ BASL_EXEC(build_dsdt(ctx));
+
+ BASL_EXEC(basl_finish());
- return (err);
+ return (0);
}
diff --git a/usr.sbin/bhyve/acpi_device.h b/usr.sbin/bhyve/acpi_device.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/acpi_device.h
@@ -0,0 +1,42 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+struct vmctx;
+
+struct acpi_device;
+
+/**
+ * Creates an ACPI device.
+ *
+ * @param[out] new_dev Returns the newly create ACPI device.
+ * @param[in] vm_ctx VM context the ACPI device is created in.
+ * @param[in] name Name of the ACPI device. Should always be a NULL
+ * terminated string.
+ * @param[in] hid Hardware ID of the ACPI device. Should always be a NULL
+ * terminated string.
+ */
+int acpi_device_create(struct acpi_device **const new_dev,
+ struct vmctx *const vm_ctx, const char *const name, const char *const hid);
+void acpi_device_destroy(struct acpi_device *const dev);
+
+/**
+ * @note: acpi_device_add_res_acpi_buffer doesn't ensure that no resources are
+ * added on an error condition. On error the caller should assume that
+ * the ACPI_BUFFER is partially added to the ACPI device.
+ */
+int acpi_device_add_res_acpi_buffer(struct acpi_device *const dev,
+ const ACPI_BUFFER resources);
+int acpi_device_add_res_fixed_ioport(struct acpi_device *const dev,
+ const UINT16 port, UINT8 length);
+int acpi_device_add_res_fixed_memory32(struct acpi_device *const dev,
+ const UINT8 write_protected, const UINT32 address, const UINT32 length);
+
+void acpi_device_write_dsdt(const struct acpi_device *const dev);
diff --git a/usr.sbin/bhyve/acpi_device.c b/usr.sbin/bhyve/acpi_device.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/acpi_device.c
@@ -0,0 +1,240 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <vmmapi.h>
+
+#include "acpi.h"
+#include "acpi_device.h"
+
+/**
+ * List entry to enumerate all resources used by an ACPI device.
+ *
+ * @param chain Used to chain multiple elements together.
+ * @param type Type of the ACPI resource.
+ * @param data Data of the ACPI resource.
+ */
+struct acpi_resource_list_entry {
+ SLIST_ENTRY(acpi_resource_list_entry) chain;
+ UINT32 type;
+ ACPI_RESOURCE_DATA data;
+};
+
+/**
+ * Holds information about an ACPI device.
+ *
+ * @param vm_ctx VM context the ACPI device was created in.
+ * @param name Name of the ACPI device.
+ * @param hid Hardware ID of the ACPI device.
+ * @param crs Current resources used by the ACPI device.
+ */
+struct acpi_device {
+ struct vmctx *vm_ctx;
+ const char *name;
+ const char *hid;
+ SLIST_HEAD(acpi_resource_list, acpi_resource_list_entry) crs;
+};
+
+int
+acpi_device_create(struct acpi_device **const new_dev,
+ struct vmctx *const vm_ctx, const char *const name, const char *const hid)
+{
+ if (new_dev == NULL || vm_ctx == NULL || name == NULL || hid == NULL) {
+ return (EINVAL);
+ }
+
+ struct acpi_device *const dev = calloc(1, sizeof(*dev));
+ if (dev == NULL) {
+ return (ENOMEM);
+ }
+
+ dev->vm_ctx = vm_ctx;
+ dev->name = name;
+ dev->hid = hid;
+ SLIST_INIT(&dev->crs);
+
+ /* current resources always contain an end tag */
+ struct acpi_resource_list_entry *const crs_end_tag = calloc(1,
+ sizeof(*crs_end_tag));
+ if (crs_end_tag == NULL) {
+ acpi_device_destroy(dev);
+ return (ENOMEM);
+ }
+ crs_end_tag->type = ACPI_RESOURCE_TYPE_END_TAG;
+ SLIST_INSERT_HEAD(&dev->crs, crs_end_tag, chain);
+
+ const int error = acpi_tables_add_device(dev);
+ if (error) {
+ acpi_device_destroy(dev);
+ return (error);
+ }
+
+ *new_dev = dev;
+
+ return (0);
+}
+
+void
+acpi_device_destroy(struct acpi_device *const dev)
+{
+ if (dev == NULL) {
+ return;
+ }
+
+ struct acpi_resource_list_entry *res;
+ while (!SLIST_EMPTY(&dev->crs)) {
+ res = SLIST_FIRST(&dev->crs);
+ SLIST_REMOVE_HEAD(&dev->crs, chain);
+ free(res);
+ }
+}
+
+int
+acpi_device_add_res_acpi_buffer(struct acpi_device *const dev,
+ const ACPI_BUFFER resources)
+{
+ if (dev == NULL) {
+ return (EINVAL);
+ }
+
+ int error = 0;
+ size_t offset = 0;
+ while (offset < resources.Length) {
+ const ACPI_RESOURCE *const res =
+ (const ACPI_RESOURCE *)((UINT8 *)resources.Pointer +
+ offset);
+ switch (res->Type) {
+ case ACPI_RESOURCE_TYPE_FIXED_IO:
+ error = acpi_device_add_res_fixed_ioport(dev,
+ res->Data.FixedIo.Address,
+ res->Data.FixedIo.AddressLength);
+ break;
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ error = acpi_device_add_res_fixed_memory32(dev,
+ res->Data.FixedMemory32.WriteProtect,
+ res->Data.FixedMemory32.Address,
+ res->Data.FixedMemory32.AddressLength);
+ break;
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ break;
+ default:
+ warnx("%s: unknown resource type %d", __func__,
+ res->Type);
+ return (ENODEV);
+ }
+ if (error) {
+ break;
+ }
+ offset += res->Length;
+ }
+
+ return (error);
+}
+
+int
+acpi_device_add_res_fixed_ioport(struct acpi_device *const dev,
+ const UINT16 port, const UINT8 length)
+{
+ if (dev == NULL) {
+ return (EINVAL);
+ }
+
+ struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
+ if (res == NULL) {
+ return (ENOMEM);
+ }
+
+ res->type = ACPI_RESOURCE_TYPE_FIXED_IO;
+ res->data.FixedIo.Address = port;
+ res->data.FixedIo.AddressLength = length;
+
+ SLIST_INSERT_HEAD(&dev->crs, res, chain);
+
+ return (0);
+}
+
+int
+acpi_device_add_res_fixed_memory32(struct acpi_device *const dev,
+ const UINT8 write_protected, const UINT32 address, const UINT32 length)
+{
+ if (dev == NULL) {
+ return (EINVAL);
+ }
+
+ struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res));
+ if (res == NULL) {
+ return (ENOMEM);
+ }
+
+ res->type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32;
+ res->data.FixedMemory32.WriteProtect = write_protected;
+ res->data.FixedMemory32.Address = address;
+ res->data.FixedMemory32.AddressLength = length;
+
+ SLIST_INSERT_HEAD(&dev->crs, res, chain);
+
+ return (0);
+}
+
+static void
+acpi_device_write_dsdt_crs(const struct acpi_device *const dev)
+{
+ const struct acpi_resource_list_entry *res;
+ SLIST_FOREACH (res, &dev->crs, chain) {
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_FIXED_IO:
+ dsdt_fixed_ioport(res->data.FixedIo.Address,
+ res->data.FixedIo.AddressLength);
+ break;
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: {
+ dsdt_fixed_mem32(res->data.FixedMemory32.Address,
+ res->data.FixedMemory32.AddressLength);
+ break;
+ }
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ break;
+ default:
+ warnx("%s: unknown resource type %d", __func__,
+ res->type);
+ return;
+ }
+ }
+}
+
+void
+acpi_device_write_dsdt(const struct acpi_device *const dev)
+{
+ if (dev == NULL) {
+ return;
+ }
+
+ dsdt_line("");
+ dsdt_line(" Scope (\\_SB)");
+ dsdt_line(" {");
+ dsdt_line(" Device (%s)", dev->name);
+ dsdt_line(" {");
+ dsdt_line(" Name (_HID, \"%s\")", dev->hid);
+ dsdt_line(" Name (_STA, 0x0F)");
+ dsdt_line(" Name (_CRS, ResourceTemplate ()");
+ dsdt_line(" {");
+ dsdt_indent(4);
+ acpi_device_write_dsdt_crs(dev);
+ dsdt_unindent(4);
+ dsdt_line(" })");
+ dsdt_line(" }");
+ dsdt_line(" }");
+}
diff --git a/usr.sbin/bhyve/basl.h b/usr.sbin/bhyve/basl.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/basl.h
@@ -0,0 +1,90 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+#include "qemu_fwcfg.h"
+
+#define ACPI_GAS_ACCESS_WIDTH_LEGACY 0
+#define ACPI_GAS_ACCESS_WIDTH_UNDEFINED 0
+#define ACPI_GAS_ACCESS_WIDTH_BYTE 1
+#define ACPI_GAS_ACCESS_WIDTH_WORD 2
+#define ACPI_GAS_ACCESS_WIDTH_DWORD 3
+#define ACPI_GAS_ACCESS_WIDTH_QWORD 4
+
+#define BHYVE_ACPI_BASE 0xf2400
+
+#define BASL_REVISION_DSDT 2
+#define BASL_REVISION_FADT 5
+#define BASL_REVISION_HPET 1
+#define BASL_REVISION_MADT 1
+#define BASL_REVISION_MCFG 1
+#define BASL_REVISION_RSDP 2
+#define BASL_REVISION_RSDT 1
+#define BASL_REVISION_XSDT 1
+
+#define BASL_OEM_ID "BHYVE "
+
+#define BASL_OEM_TABLE_ID_DSDT "BVDSDT "
+#define BASL_OEM_TABLE_ID_FADT "BVFACP "
+#define BASL_OEM_TABLE_ID_HPET "BVHPET "
+#define BASL_OEM_TABLE_ID_MADT "BVMADT "
+#define BASL_OEM_TABLE_ID_MCFG "BVMCFG "
+#define BASL_OEM_TABLE_ID_RSDT "BVRSDT "
+#define BASL_OEM_TABLE_ID_XSDT "BVXSDT "
+
+#define BASL_OEM_REVISION_DSDT 1
+#define BASL_OEM_REVISION_FADT 1
+#define BASL_OEM_REVISION_HPET 1
+#define BASL_OEM_REVISION_MADT 1
+#define BASL_OEM_REVISION_MCFG 1
+#define BASL_OEM_REVISION_RSDT 1
+#define BASL_OEM_REVISION_XSDT 1
+
+#define BASL_COMPILER_ID "BASL"
+
+#define BASL_COMPILER_REVISION 20220504
+
+#define BASL_TABLE_ALIGNMENT 0x10
+#define BASL_TABLE_ALIGNMENT_FACS 0x40
+
+#define BASL_TABLE_CHECKSUM_LEN_FULL_TABLE (-1)
+
+#define BASL_EXEC(x) \
+ do { \
+ const int error = (x); \
+ if (error) { \
+ warnc(error, \
+ "BASL failed @ %s:%d\n Failed to execute %s", \
+ __func__, __LINE__, #x); \
+ return (error); \
+ } \
+ } while (0)
+
+struct basl_table;
+
+int basl_finish();
+int basl_init();
+int basl_table_append_bytes(struct basl_table *table, const void *bytes,
+ uint32_t len);
+int basl_table_append_checksum(struct basl_table *table, uint32_t start,
+ uint32_t len);
+int basl_table_append_gas(struct basl_table *table, uint8_t space_id,
+ uint8_t bit_width, uint8_t bit_offset, uint8_t access_width,
+ uint64_t address);
+int basl_table_append_header(struct basl_table *table,
+ const uint8_t sign[ACPI_NAMESEG_SIZE], uint8_t rev,
+ const uint8_t oem_id[ACPI_OEM_ID_SIZE],
+ const uint8_t oem_table_id[ACPI_OEM_TABLE_ID_SIZE], uint32_t oem_revision);
+int basl_table_append_int(struct basl_table *table, uint64_t val, uint8_t size);
+int basl_table_append_length(struct basl_table *table, uint8_t size);
+int basl_table_append_pointer(struct basl_table *table,
+ const uint8_t src_sign[ACPI_NAMESEG_SIZE], uint8_t size);
+int basl_table_create(struct basl_table **table, struct vmctx *ctx,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], uint32_t alignment);
diff --git a/usr.sbin/bhyve/basl.c b/usr.sbin/bhyve/basl.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/basl.c
@@ -0,0 +1,559 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <machine/vmm.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <vmmapi.h>
+
+#include "basl.h"
+#include "qemu_loader.h"
+
+struct basl_table {
+ STAILQ_ENTRY(basl_table) chain;
+ struct vmctx *ctx;
+ uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
+ void *data;
+ uint32_t len;
+ uint32_t off;
+ uint32_t alignment;
+};
+STAILQ_HEAD(basl_table_list, basl_table) basl_tables = STAILQ_HEAD_INITIALIZER(
+ basl_tables);
+
+struct basl_table_checksum {
+ STAILQ_ENTRY(basl_table_checksum) chain;
+ struct basl_table *table;
+ uint32_t off;
+ uint32_t start;
+ uint32_t len;
+};
+STAILQ_HEAD(basl_table_checksum_list, basl_table_checksum) basl_checksums =
+ STAILQ_HEAD_INITIALIZER(basl_checksums);
+
+struct basl_table_length {
+ STAILQ_ENTRY(basl_table_length) chain;
+ struct basl_table *table;
+ uint32_t off;
+ uint8_t size;
+};
+STAILQ_HEAD(basl_table_length_list,
+ basl_table_length) basl_lengths = STAILQ_HEAD_INITIALIZER(basl_lengths);
+
+struct basl_table_pointer {
+ STAILQ_ENTRY(basl_table_pointer) chain;
+ struct basl_table *table;
+ uint8_t src_sign[ACPI_NAMESEG_SIZE];
+ uint32_t off;
+ uint8_t size;
+};
+STAILQ_HEAD(basl_table_pointer_list,
+ basl_table_pointer) basl_pointers = STAILQ_HEAD_INITIALIZER(basl_pointers);
+
+struct qemu_loader *basl_loader;
+
+static int
+basl_dump_table(const struct basl_table *const table, const int mem)
+{
+ const ACPI_TABLE_HEADER *const header = table->data;
+ const uint8_t *data;
+
+ if (!mem) {
+ data = table->data;
+ } else {
+ data = (uint8_t *)vm_map_gpa(table->ctx,
+ BHYVE_ACPI_BASE + table->off, table->len);
+ if (data == NULL) {
+ return (ENOMEM);
+ }
+ }
+
+ printf("%c%c%c%c @ %8x (%s)\n\r", header->Signature[0],
+ header->Signature[1], header->Signature[2], header->Signature[3],
+ BHYVE_ACPI_BASE + table->off, mem ? "Memory" : "FwCfg");
+ for (uint32_t i = 0; i < table->len; i += 0x10) {
+ printf("%08x: ", i);
+ for (uint32_t n = 0; n < 0x10; ++n) {
+ if (table->len <= i + n) {
+ printf(" ");
+ continue;
+ }
+ printf("%02x ", data[i + n]);
+ }
+ printf("| ");
+ for (uint32_t n = 0; n < 0x10; ++n) {
+ if (table->len <= i + n) {
+ printf(" ");
+ continue;
+ }
+ const uint8_t c = data[i + n];
+ if (c < 0x20 || c >= 0x7F) {
+ printf(".");
+ } else {
+ printf("%c", c);
+ }
+ }
+ printf("\n\r");
+ }
+
+ return (0);
+}
+
+static int
+basl_dump(const int mem)
+{
+ struct basl_table *table;
+ STAILQ_FOREACH (table, &basl_tables, chain) {
+ BASL_EXEC(basl_dump_table(table, mem));
+ }
+
+ return (0);
+}
+
+static int
+basl_finish_alloc()
+{
+ struct basl_table *table;
+ uint32_t off = 0;
+ STAILQ_FOREACH (table, &basl_tables, chain) {
+ table->off = roundup2(off, table->alignment);
+ off = table->off + table->len;
+ if (off <= table->off) {
+ warnx("%s: invalid table length 0x%8x @ offset 0x%8x",
+ __func__, table->len, table->off);
+ return (EFAULT);
+ }
+
+ /*
+ * Old guest bios versions search for ACPI tables in the guest
+ * memory and install them as is. Therefore, copy the tables
+ * into the guest memory.
+ */
+ void *gva = vm_map_gpa(table->ctx, BHYVE_ACPI_BASE + table->off,
+ table->len);
+ if (gva == NULL) {
+ warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
+ __func__, (uint64_t)BHYVE_ACPI_BASE + table->off,
+ (uint64_t)BHYVE_ACPI_BASE + table->off +
+ table->len);
+ return (ENOMEM);
+ }
+ memcpy(gva, table->data, table->len);
+
+ /* Cause guest bios to copy the ACPI table into guest memory. */
+ BASL_EXEC(qemu_fwcfg_add_file(table->fwcfg_name, table->len,
+ table->data));
+ BASL_EXEC(qemu_loader_alloc(basl_loader, table->fwcfg_name,
+ table->alignment, QEMU_LOADER_ALLOC_HIGH));
+ }
+
+ return (0);
+}
+
+static int
+basl_finish_patch_checksums()
+{
+ struct basl_table_checksum *checksum;
+ STAILQ_FOREACH (checksum, &basl_checksums, chain) {
+ const struct basl_table *const table = checksum->table;
+
+ uint32_t len = checksum->len;
+ if (len == BASL_TABLE_CHECKSUM_LEN_FULL_TABLE) {
+ len = table->len;
+ }
+
+ /*
+ * Old guest bios versions search for ACPI tables in the guest
+ * memory and install them as is. Therefore, patch the checksum
+ * in the guest memory copies to a correct value.
+ */
+ const uint64_t gpa = BHYVE_ACPI_BASE + table->off +
+ checksum->start;
+ if ((gpa < BHYVE_ACPI_BASE) ||
+ (gpa < BHYVE_ACPI_BASE + table->off)) {
+ warnx("%s: invalid gpa (off 0x%8x start 0x%8x)",
+ __func__, table->off, checksum->start);
+ return (EFAULT);
+ }
+
+ uint8_t *const gva = (uint8_t *)vm_map_gpa(table->ctx, gpa,
+ len);
+ if (gva == NULL) {
+ warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
+ __func__, gpa, gpa + len);
+ return (ENOMEM);
+ }
+ uint8_t *const checksum_gva = gva + checksum->off;
+ if (checksum_gva < gva) {
+ warnx("%s: invalid checksum offset 0x%8x", __func__,
+ checksum->off);
+ return (EFAULT);
+ }
+
+ uint8_t sum = 0;
+ for (uint32_t i = 0; i < len; ++i) {
+ sum += *(gva + i);
+ }
+ *checksum_gva = -sum;
+
+ /* Cause guest bios to patch the checksum. */
+ BASL_EXEC(qemu_loader_add_checksum(basl_loader,
+ table->fwcfg_name, checksum->off, checksum->start, len));
+ }
+
+ return (0);
+}
+
+static struct basl_table *
+basl_get_table_by_sign(const uint8_t sign[ACPI_NAMESEG_SIZE])
+{
+ struct basl_table *table;
+ STAILQ_FOREACH (table, &basl_tables, chain) {
+ const ACPI_TABLE_HEADER *const header =
+ (const ACPI_TABLE_HEADER *)table->data;
+ if (strncmp(header->Signature, sign,
+ sizeof(header->Signature)) == 0) {
+ return (table);
+ }
+ }
+
+ warnx("%s: %c%c%c%c not found", __func__, sign[0], sign[1], sign[2],
+ sign[3]);
+ return (NULL);
+}
+
+static int
+basl_finish_patch_pointers()
+{
+ struct basl_table_pointer *pointer;
+ STAILQ_FOREACH (pointer, &basl_pointers, chain) {
+ const struct basl_table *const table = pointer->table;
+ const struct basl_table *const src_table =
+ basl_get_table_by_sign(pointer->src_sign);
+ if (src_table == NULL) {
+ warnx("%s: could not find ACPI table %c%c%c%c",
+ __func__, pointer->src_sign[0],
+ pointer->src_sign[1], pointer->src_sign[2],
+ pointer->src_sign[3]);
+ return (EFAULT);
+ }
+
+ /*
+ * Old guest bios versions search for ACPI tables in the guest
+ * memory and install them as is. Therefore, patch the pointers
+ * in the guest memory copies manually.
+ */
+ const uint64_t gpa = BHYVE_ACPI_BASE + table->off;
+ if (gpa < BHYVE_ACPI_BASE) {
+ warnx("%s: table offset of 0x%8x is too large",
+ __func__, table->off);
+ return (EFAULT);
+ }
+
+ uint8_t *const gva = (uint8_t *)vm_map_gpa(table->ctx, gpa,
+ table->len);
+ if (gva == NULL) {
+ warnx("%s: could not map gpa [ 0x%16lx, 0x%16lx ]",
+ __func__, gpa, gpa + table->len);
+ return (ENOMEM);
+ }
+
+ uint64_t val_le = 0;
+ memcpy(&val_le, gva + pointer->off, pointer->size);
+ uint64_t val = le64toh(val_le);
+
+ val += BHYVE_ACPI_BASE + src_table->off;
+
+ val_le = htole64(val);
+ memcpy(gva + pointer->off, &val_le, pointer->size);
+
+ /* Cause guest bios to patch the pointer. */
+ BASL_EXEC(
+ qemu_loader_add_pointer(basl_loader, table->fwcfg_name,
+ src_table->fwcfg_name, pointer->off, pointer->size));
+ }
+
+ return (0);
+}
+
+static int
+basl_finish_set_length()
+{
+ struct basl_table_length *length;
+ STAILQ_FOREACH (length, &basl_lengths, chain) {
+ const struct basl_table *const table = length->table;
+
+ uint32_t len_le = htole32(table->len);
+
+ memcpy(table->data + length->off, &len_le, length->size);
+ }
+
+ return (0);
+}
+
+int
+basl_finish()
+{
+ if (STAILQ_EMPTY(&basl_tables)) {
+ warnx("%s: no ACPI tables found", __func__);
+ return (EINVAL);
+ }
+
+ BASL_EXEC(basl_finish_set_length());
+ BASL_EXEC(basl_finish_alloc());
+ BASL_EXEC(basl_finish_patch_pointers());
+ BASL_EXEC(basl_finish_patch_checksums());
+ BASL_EXEC(qemu_loader_finish(basl_loader));
+
+ return (0);
+}
+
+int
+basl_init()
+{
+ return (qemu_loader_create(&basl_loader, QEMU_FWCFG_FILE_TABLE_LOADER));
+}
+
+static int
+basl_table_add_checksum(struct basl_table *const table, const uint32_t off,
+ const uint32_t start, const uint32_t len)
+{
+ struct basl_table_checksum *const checksum = calloc(1,
+ sizeof(struct basl_table_checksum));
+ if (checksum == NULL) {
+ warnx("%s: failed to allocate checksum", __func__);
+ return (ENOMEM);
+ }
+
+ checksum->table = table;
+ checksum->off = off;
+ checksum->start = start;
+ checksum->len = len;
+
+ STAILQ_INSERT_TAIL(&basl_checksums, checksum, chain);
+
+ return (0);
+}
+
+static int
+basl_table_add_length(struct basl_table *const table, const uint32_t off,
+ const uint8_t size)
+{
+ struct basl_table_length *const length = calloc(1,
+ sizeof(struct basl_table_length));
+ if (length == NULL) {
+ warnx("%s: failed to allocate length", __func__);
+ return (ENOMEM);
+ }
+
+ length->table = table;
+ length->off = off;
+ length->size = size;
+
+ STAILQ_INSERT_TAIL(&basl_lengths, length, chain);
+
+ return (0);
+}
+
+static int
+basl_table_add_pointer(struct basl_table *const table,
+ const uint8_t src_sign[ACPI_NAMESEG_SIZE], const uint32_t off,
+ const uint8_t size)
+{
+ struct basl_table_pointer *const pointer = calloc(1,
+ sizeof(struct basl_table_pointer));
+ if (pointer == NULL) {
+ warnx("%s: failed to allocate pointer", __func__);
+ return (ENOMEM);
+ }
+
+ pointer->table = table;
+ memcpy(pointer->src_sign, src_sign, sizeof(pointer->src_sign));
+ pointer->off = off;
+ pointer->size = size;
+
+ STAILQ_INSERT_TAIL(&basl_pointers, pointer, chain);
+
+ return (0);
+}
+
+int
+basl_table_append_bytes(struct basl_table *const table, const void *const bytes,
+ const uint32_t len)
+{
+ if (table == NULL || bytes == NULL) {
+ return (EINVAL);
+ }
+ if (table->len + len <= table->len) {
+ warnx("%s: table too large (table->len 0x%8x len 0x%8x)",
+ __func__, table->len, len);
+ return (EFAULT);
+ }
+
+ table->data = reallocf(table->data, table->len + len);
+ if (table->data == NULL) {
+ warnx("%s: failed to realloc table to length 0x%8x", __func__,
+ table->len + len);
+ table->len = 0;
+ return (ENOMEM);
+ }
+ void *const end = (uint8_t *)table->data + table->len;
+ table->len += len;
+
+ memcpy(end, bytes, len);
+
+ return (0);
+}
+
+int
+basl_table_append_checksum(struct basl_table *const table, const uint32_t start,
+ const uint32_t len)
+{
+ if (table == NULL) {
+ return (EINVAL);
+ }
+
+ BASL_EXEC(basl_table_add_checksum(table, table->len, start, len));
+ BASL_EXEC(basl_table_append_int(table, 0, 1));
+
+ return (0);
+}
+
+int
+basl_table_append_gas(struct basl_table *const table, const uint8_t space_id,
+ const uint8_t bit_width, const uint8_t bit_offset,
+ const uint8_t access_width, const uint64_t address)
+{
+ ACPI_GENERIC_ADDRESS gas_le = {
+ .SpaceId = space_id,
+ .BitWidth = bit_width,
+ .BitOffset = bit_offset,
+ .AccessWidth = access_width,
+ .Address = htole64(address),
+ };
+
+ return (basl_table_append_bytes(table, &gas_le, sizeof(gas_le)));
+}
+
+int
+basl_table_append_header(struct basl_table *const table,
+ const uint8_t sign[ACPI_NAMESEG_SIZE], const uint8_t rev,
+ const uint8_t oem_id[ACPI_OEM_ID_SIZE],
+ const uint8_t oem_table_id[ACPI_OEM_TABLE_ID_SIZE],
+ const uint32_t oem_revision)
+{
+ if (table == NULL || table->len != 0) {
+ return (EINVAL);
+ }
+
+ ACPI_TABLE_HEADER header_le;
+
+ memcpy(header_le.Signature, sign, sizeof(header_le.Signature));
+ header_le.Length = 0; /* patched by basl_finish */
+ header_le.Revision = rev;
+ header_le.Checksum = 0; /* patched by basl_finish */
+ memcpy(header_le.OemId, oem_id, sizeof(header_le.OemId));
+ memcpy(header_le.OemTableId, oem_table_id, sizeof(header_le.OemTableId));
+ header_le.OemRevision = htole32(oem_revision);
+ static_assert(sizeof(header_le.AslCompilerId) ==
+ sizeof(BASL_COMPILER_ID) - 1 /* Without '\0' */,
+ "Mismatching ASL compiler id size");
+ memcpy(header_le.AslCompilerId, BASL_COMPILER_ID,
+ sizeof(header_le.AslCompilerId));
+ header_le.AslCompilerRevision = htole32(BASL_COMPILER_REVISION);
+
+ BASL_EXEC(
+ basl_table_append_bytes(table, &header_le, sizeof(header_le)));
+
+ BASL_EXEC(basl_table_add_length(table,
+ offsetof(ACPI_TABLE_HEADER, Length), sizeof(header_le.Length)));
+ BASL_EXEC(basl_table_add_checksum(table,
+ offsetof(ACPI_TABLE_HEADER, Checksum), 0,
+ BASL_TABLE_CHECKSUM_LEN_FULL_TABLE));
+
+ return (0);
+}
+
+int
+basl_table_append_int(struct basl_table *const table, const uint64_t val,
+ const uint8_t size)
+{
+ if (size > sizeof(val)) {
+ return (EINVAL);
+ }
+
+ const uint64_t val_le = htole64(val);
+ return (basl_table_append_bytes(table, &val_le, size));
+}
+
+int
+basl_table_append_length(struct basl_table *const table, const uint8_t size)
+{
+ if (table == NULL || size > sizeof(table->len)) {
+ return (EINVAL);
+ }
+
+ BASL_EXEC(basl_table_add_length(table, table->len, size));
+ BASL_EXEC(basl_table_append_int(table, 0, size));
+
+ return (0);
+}
+
+int
+basl_table_append_pointer(struct basl_table *const table,
+ const uint8_t src_sign[ACPI_NAMESEG_SIZE], const uint8_t size)
+{
+ if (table == NULL || size > sizeof(UINT64)) {
+ return (EINVAL);
+ }
+
+ BASL_EXEC(basl_table_add_pointer(table, src_sign, table->len, size));
+ BASL_EXEC(basl_table_append_int(table, 0, size));
+
+ return (0);
+}
+
+int
+basl_table_create(struct basl_table **const table, struct vmctx *ctx,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], const uint32_t alignment)
+{
+ if (table == NULL) {
+ return (EINVAL);
+ }
+
+ struct basl_table *const new_table = (struct basl_table *)calloc(1,
+ sizeof(struct basl_table));
+ if (new_table == NULL) {
+ warnx("%s: failed to allocate table", __func__);
+ return (ENOMEM);
+ }
+
+ new_table->ctx = ctx;
+
+ snprintf(new_table->fwcfg_name, sizeof(new_table->fwcfg_name),
+ "etc/acpi/%s", name);
+
+ new_table->alignment = alignment;
+
+ STAILQ_INSERT_TAIL(&basl_tables, new_table, chain);
+
+ *table = new_table;
+
+ return (0);
+}
diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8
--- a/usr.sbin/bhyve/bhyve.8
+++ b/usr.sbin/bhyve/bhyve.8
@@ -45,6 +45,15 @@
.Op Cm ,threads= Ar n
.Oc
.Sm on
+.Oo Fl f
+.Sm off
+.Ar name Cm \&,
+.Oo
+.Cm string No | Cm file
+.Oc
+.Cm \&= Ar data
+.Sm on
+.Oc
.Oo
.Sm off
.Fl G\~
@@ -145,6 +154,16 @@
.Nm
to exit when a guest issues an access to an I/O port that is not emulated.
This is intended for debug purposes.
+.It Fl f Ar name Ns Cm \&, Ns Oo Cm string Ns No | Ns Cm file Ns Oc Ns Cm \&= Ns Ar data
+Add a fw_cfg file
+.Ar name
+to the fw_cfg interface.
+If a
+.Cm string
+is specified, the fw_cfg file contains the string as data.
+If a
+.Cm file
+is specified, bhyve reads the file and adds the file content as fw_cfg data.
.It Fl G Xo
.Sm off
.Oo Ar w Oc
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -100,6 +100,7 @@
#include "pci_emul.h"
#include "pci_irq.h"
#include "pci_lpc.h"
+#include "qemu_fwcfg.h"
#include "smbiostbl.h"
#ifdef BHYVE_SNAPSHOT
#include "snapshot.h"
@@ -1229,6 +1230,7 @@
set_config_bool("acpi_tables", false);
set_config_value("memory.size", "256M");
set_config_bool("x86.strictmsr", true);
+ set_config_value("lpc.fwcfg", "bhyve");
}
int
@@ -1254,9 +1256,9 @@
progname = basename(argv[0]);
#ifdef BHYVE_SNAPSHOT
- optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:K:U:r:";
+ optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:r:";
#else
- optstr = "aehuwxACDHIPSWYk:o:p:G:c:s:m:l:K:U:";
+ optstr = "aehuwxACDHIPSWYk:f:o:p:G:c:s:m:l:K:U:";
#endif
while ((c = getopt(argc, argv, optstr)) != -1) {
switch (c) {
@@ -1284,6 +1286,11 @@
case 'C':
set_config_bool("memory.guest_in_core", true);
break;
+ case 'f':
+ if (qemu_fwcfg_parse_cmdline_arg(optarg) != 0) {
+ exit(1);
+ }
+ break;
case 'G':
parse_gdb_options(optarg);
break;
@@ -1460,6 +1467,17 @@
rtc_init(ctx);
sci_init(ctx);
+ if (qemu_fwcfg_init(ctx) != 0) {
+ fprintf(stderr, "qemu fwcfg initialization error");
+ exit(4);
+ }
+
+ if (qemu_fwcfg_add_file("opt/bhyve/hw.ncpu", sizeof(guest_ncpus),
+ &guest_ncpus) != 0) {
+ fprintf(stderr, "Could not add qemu fwcfg opt/bhyve/hw.ncpu");
+ exit(4);
+ }
+
/*
* Exit if a device emulation finds an error in its initilization
*/
@@ -1543,8 +1561,9 @@
assert(error == 0);
}
- if (lpc_bootrom())
+ if (lpc_bootrom() && (strcmp(lpc_fwcfg(), "bhyve") == 0)) {
fwctl_init();
+ }
/*
* Change the proc title to include the VM name.
diff --git a/usr.sbin/bhyve/pci_lpc.h b/usr.sbin/bhyve/pci_lpc.h
--- a/usr.sbin/bhyve/pci_lpc.h
+++ b/usr.sbin/bhyve/pci_lpc.h
@@ -72,5 +72,6 @@
char *lpc_pirq_name(int pin);
void lpc_pirq_routed(void);
const char *lpc_bootrom(void);
+const char *lpc_fwcfg(void);
#endif
diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c
--- a/usr.sbin/bhyve/pci_lpc.c
+++ b/usr.sbin/bhyve/pci_lpc.c
@@ -110,10 +110,20 @@
set_config_value("lpc.bootrom", romfile);
varfile = strsep(&str, ",");
- if (varfile != NULL) {
+ if (varfile == NULL) {
+ error = 0;
+ goto done;
+ }
+ if (strchr(varfile, '=') == NULL) {
set_config_value("lpc.bootvars", varfile);
+ } else {
+ /* varfile doesn't exist, it's another config
+ * option */
+ pci_parse_legacy_config(find_config_node("lpc"),
+ varfile);
}
+ pci_parse_legacy_config(find_config_node("lpc"), str);
error = 0;
goto done;
}
@@ -160,6 +170,12 @@
return (get_config_value("lpc.bootrom"));
}
+const char *
+lpc_fwcfg(void)
+{
+ return (get_config_value("lpc.fwcfg"));
+}
+
static void
lpc_uart_intr_assert(void *arg)
{
diff --git a/usr.sbin/bhyve/qemu_fwcfg.h b/usr.sbin/bhyve/qemu_fwcfg.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_fwcfg.h
@@ -0,0 +1,26 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <vmmapi.h>
+
+#define QEMU_FWCFG_MAX_ARCHS 0x2
+#define QEMU_FWCFG_MAX_ENTRIES 0x3FFF
+#define QEMU_FWCFG_MAX_NAME 56
+
+#define QEMU_FWCFG_FILE_TABLE_LOADER "etc/table-loader"
+
+struct qemu_fwcfg_item {
+ uint32_t size;
+ uint8_t *data;
+};
+
+int qemu_fwcfg_add_file(const uint8_t name[QEMU_FWCFG_MAX_NAME],
+ const uint32_t size, void *const data);
+int qemu_fwcfg_init(struct vmctx *const ctx);
+int qemu_fwcfg_parse_cmdline_arg(const char *opt);
diff --git a/usr.sbin/bhyve/qemu_fwcfg.c b/usr.sbin/bhyve/qemu_fwcfg.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_fwcfg.c
@@ -0,0 +1,562 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpi_device.h"
+#include "inout.h"
+#include "pci_lpc.h"
+#include "qemu_fwcfg.h"
+
+#define QEMU_FWCFG_ACPI_DEVICE_NAME "FWCF"
+#define QEMU_FWCFG_ACPI_HARDWARE_ID "QEMU0002"
+
+#define QEMU_FWCFG_SELECTOR_PORT_NUMBER 0x510
+#define QEMU_FWCFG_SELECTOR_PORT_SIZE 1
+#define QEMU_FWCFG_SELECTOR_PORT_FLAGS IOPORT_F_INOUT
+#define QEMU_FWCFG_DATA_PORT_NUMBER 0x511
+#define QEMU_FWCFG_DATA_PORT_SIZE 1
+#define QEMU_FWCFG_DATA_PORT_FLAGS \
+ IOPORT_F_INOUT /* QEMU v2.4+ ignores writes */
+
+#define QEMU_FWCFG_ARCHITECTURE_MASK 0x0001
+#define QEMU_FWCFG_INDEX_MASK 0x3FFF
+
+#define QEMU_FWCFG_SELECT_READ 0
+#define QEMU_FWCFG_SELECT_WRITE 1
+
+#define QEMU_FWCFG_ARCHITECTURE_GENERIC 0
+#define QEMU_FWCFG_ARCHITECTURE_SPECIFIC 1
+
+#define QEMU_FWCFG_INDEX_SIGNATURE 0x00
+#define QEMU_FWCFG_INDEX_ID 0x01
+#define QEMU_FWCFG_INDEX_FILE_DIR 0x19
+
+#define QEMU_FWCFG_FIRST_FILE_INDEX 0x20
+
+#define QEMU_FWCFG_MIN_FILES 10
+
+#pragma pack(1)
+
+union qemu_fwcfg_selector {
+ struct {
+ uint16_t index : 14;
+ uint16_t writeable : 1;
+ /*
+ * 0 = generic | for all architectures
+ * 1 = specific | only for current architecture
+ */
+ uint16_t architecture : 1;
+ };
+ uint16_t bits;
+};
+
+struct qemu_fwcfg_signature {
+ uint8_t signature[4];
+};
+
+struct qemu_fwcfg_id {
+ uint32_t interface : 1; /* always set */
+ uint32_t DMA : 1;
+ uint32_t reserved : 30;
+};
+
+struct qemu_fwcfg_file {
+ uint32_t be_size;
+ uint16_t be_selector;
+ uint16_t reserved;
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+};
+
+struct qemu_fwcfg_directory {
+ uint32_t be_count;
+ struct qemu_fwcfg_file files[0];
+};
+
+struct qemu_fwcfg_softc {
+ struct acpi_device *acpi_dev;
+
+ uint32_t data_offset;
+ union qemu_fwcfg_selector selector;
+ struct qemu_fwcfg_item items[QEMU_FWCFG_MAX_ARCHS]
+ [QEMU_FWCFG_MAX_ENTRIES];
+ struct qemu_fwcfg_directory *directory;
+};
+
+#pragma pack()
+
+static struct qemu_fwcfg_softc sc;
+
+struct qemu_fwcfg_user_file {
+ STAILQ_ENTRY(qemu_fwcfg_user_file) chain;
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+ uint32_t size;
+ void *data;
+};
+STAILQ_HEAD(qemu_fwcfg_user_file_list,
+ qemu_fwcfg_user_file) user_files = STAILQ_HEAD_INITIALIZER(user_files);
+
+static int
+qemu_fwcfg_selector_port_handler(struct vmctx *const ctx, const int vcpu,
+ const int in, const int port, const int bytes, uint32_t *const eax,
+ void *const arg)
+{
+ if (bytes != sizeof(uint16_t)) {
+ warnx("%s: invalid size (%d) of IO port access", __func__,
+ bytes);
+ return (-1);
+ }
+
+ if (in) {
+ *eax = htole16(sc.selector.bits);
+ return (0);
+ }
+
+ sc.data_offset = 0;
+ sc.selector.bits = le16toh(*eax);
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_data_port_handler(struct vmctx *const ctx, const int vcpu,
+ const int in, const int port, const int bytes, uint32_t *const eax,
+ void *const arg)
+{
+ if (bytes != sizeof(uint8_t)) {
+ warnx("%s: invalid size (%d) of IO port access", __func__,
+ bytes);
+ return (-1);
+ }
+
+ if (!in) {
+ warnx("%s: Writes to qemu fwcfg data port aren't allowed",
+ __func__);
+ return (-1);
+ }
+
+ /* get fwcfg item */
+ struct qemu_fwcfg_item *const item =
+ &sc.items[sc.selector.architecture][sc.selector.index];
+ if (item->data == NULL) {
+ warnx(
+ "%s: qemu fwcfg item doesn't exist (architecture %s index 0x%x)",
+ __func__, sc.selector.architecture ? "specific" : "generic",
+ sc.selector.index);
+ *eax = 0x00;
+ return (0);
+ } else if (sc.data_offset >= item->size) {
+ warnx(
+ "%s: qemu fwcfg item read exceeds size (architecture %s index 0x%x size 0x%x offset 0x%x)",
+ __func__, sc.selector.architecture ? "specific" : "generic",
+ sc.selector.index, item->size, sc.data_offset);
+ *eax = 0x00;
+ return (0);
+ }
+
+ /* return item data */
+ *eax = item->data[sc.data_offset];
+ sc.data_offset++;
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_add_item(const uint16_t architecture, const uint16_t index,
+ const uint32_t size, void *const data)
+{
+ /* truncate architecture and index to their desired size */
+ const uint16_t arch = architecture & QEMU_FWCFG_ARCHITECTURE_MASK;
+ const uint16_t idx = index & QEMU_FWCFG_INDEX_MASK;
+
+ /* get pointer to item specified by selector */
+ struct qemu_fwcfg_item *const fwcfg_item = &sc.items[arch][idx];
+
+ /* check if item is already used */
+ if (fwcfg_item->data != NULL) {
+ warnx("%s: qemu fwcfg item exists (architecture %s index 0x%x)",
+ __func__, arch ? "specific" : "generic", idx);
+ return (-1);
+ }
+
+ /* save data of the item */
+ fwcfg_item->size = size;
+ fwcfg_item->data = data;
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_add_item_file_dir()
+{
+ /* alloc directory */
+ const size_t size = sizeof(struct qemu_fwcfg_directory) +
+ QEMU_FWCFG_MIN_FILES * sizeof(struct qemu_fwcfg_file);
+ struct qemu_fwcfg_directory *const fwcfg_directory = calloc(1, size);
+ if (fwcfg_directory == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init directory */
+ sc.directory = fwcfg_directory;
+
+ /* add directory */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_FILE_DIR, sizeof(struct qemu_fwcfg_directory), (uint8_t *)sc.directory);
+}
+
+static int
+qemu_fwcfg_add_item_id()
+{
+ /* alloc id */
+ struct qemu_fwcfg_id *const fwcfg_id = calloc(1,
+ sizeof(struct qemu_fwcfg_id));
+ if (fwcfg_id == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init id */
+ fwcfg_id->interface = 1;
+ fwcfg_id->DMA = 0;
+
+ /*
+ * QEMU specifies ID as little endian.
+ * Convert fwcfg_id to little endian.
+ */
+ uint32_t *const le_fwcfg_id_ptr = (uint32_t *)fwcfg_id;
+ *le_fwcfg_id_ptr = htole32(*le_fwcfg_id_ptr);
+
+ /* add id */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_ID, sizeof(struct qemu_fwcfg_id),
+ (uint8_t *)fwcfg_id);
+}
+
+static int
+qemu_fwcfg_add_item_signature()
+{
+ /* alloc signature */
+ struct qemu_fwcfg_signature *const fwcfg_signature = calloc(1,
+ sizeof(struct qemu_fwcfg_signature));
+ if (fwcfg_signature == NULL) {
+ return (-ENOMEM);
+ }
+
+ /* init signature */
+ fwcfg_signature->signature[0] = 'Q';
+ fwcfg_signature->signature[1] = 'E';
+ fwcfg_signature->signature[2] = 'M';
+ fwcfg_signature->signature[3] = 'U';
+
+ /* add signature */
+ return qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ QEMU_FWCFG_INDEX_SIGNATURE, sizeof(struct qemu_fwcfg_signature),
+ (uint8_t *)fwcfg_signature);
+}
+
+static int
+qemu_fwcfg_register_port(const char *const name, const int port, const int size,
+ const int flags, const inout_func_t handler)
+{
+ struct inout_port iop;
+
+ bzero(&iop, sizeof(iop));
+ iop.name = name;
+ iop.port = port;
+ iop.size = size;
+ iop.flags = flags;
+ iop.handler = handler;
+
+ return register_inout(&iop);
+}
+
+int
+qemu_fwcfg_add_file(const uint8_t name[QEMU_FWCFG_MAX_NAME], const uint32_t size,
+ void *const data)
+{
+ /*
+ * QEMU specifies count as big endian.
+ * Convert it to host endian to work with it.
+ */
+ const uint32_t count = be32toh(sc.directory->be_count) + 1;
+
+ /* add file to items list */
+ const uint32_t index = QEMU_FWCFG_FIRST_FILE_INDEX + count - 1;
+ const int error = qemu_fwcfg_add_item(QEMU_FWCFG_ARCHITECTURE_GENERIC,
+ index, size, data);
+ if (error != 0) {
+ return (error);
+ }
+
+ /*
+ * files should be sorted alphabetical, get index for new file
+ */
+ uint32_t file_index;
+ for (file_index = 0; file_index < count - 1; ++file_index) {
+ if (strcmp(name, sc.directory->files[file_index].name) < 0)
+ break;
+ }
+
+ if (count > QEMU_FWCFG_MIN_FILES) {
+ /* alloc new file directory */
+ const uint64_t new_size = sizeof(struct qemu_fwcfg_directory) +
+ count * sizeof(struct qemu_fwcfg_file);
+ struct qemu_fwcfg_directory *const new_directory = calloc(1,
+ new_size);
+ if (new_directory == NULL) {
+ warnx(
+ "%s: Unable to allocate a new qemu fwcfg files directory (count %d)",
+ __func__, count);
+ return (-ENOMEM);
+ }
+
+ /* copy files below file_index to new directory */
+ memcpy(new_directory->files, sc.directory->files,
+ file_index * sizeof(struct qemu_fwcfg_file));
+
+ /* copy files behind file_index to directory */
+ memcpy(&new_directory->files[file_index + 1],
+ &sc.directory->files[file_index],
+ (count - file_index) * sizeof(struct qemu_fwcfg_file));
+
+ /* free old directory */
+ free(sc.directory);
+
+ /* set directory pointer to new directory */
+ sc.directory = new_directory;
+
+ /* adjust directory pointer */
+ sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].data = (uint8_t *)
+ sc.directory;
+ } else {
+ /* shift files behind file_index */
+ for (uint32_t i = QEMU_FWCFG_MIN_FILES - 1; i > file_index; --i) {
+ memcpy(&sc.directory->files[i],
+ &sc.directory->files[i - 1],
+ sizeof(struct qemu_fwcfg_file));
+ }
+ }
+
+ /*
+ * QEMU specifies count, size and index as big endian.
+ * Save these values in big endian to simplify guest reads of these
+ * values.
+ */
+ sc.directory->be_count = htobe32(count);
+ sc.directory->files[file_index].be_size = htobe32(size);
+ sc.directory->files[file_index].be_selector = htobe16(index);
+ strcpy(sc.directory->files[file_index].name, name);
+
+ /* set new size for the fwcfg_file_directory */
+ sc.items[0][QEMU_FWCFG_INDEX_FILE_DIR].size =
+ sizeof(struct qemu_fwcfg_directory) +
+ count * sizeof(struct qemu_fwcfg_file);
+
+ return (0);
+}
+
+static int
+qemu_fwcfg_add_user_files()
+{
+ const struct qemu_fwcfg_user_file *fwcfg_file;
+ STAILQ_FOREACH (fwcfg_file, &user_files, chain) {
+ const int error = qemu_fwcfg_add_file(fwcfg_file->name,
+ fwcfg_file->size, fwcfg_file->data);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+qemu_fwcfg_init(struct vmctx *const ctx)
+{
+ int error;
+
+ error = acpi_device_create(&sc.acpi_dev, ctx,
+ QEMU_FWCFG_ACPI_DEVICE_NAME, QEMU_FWCFG_ACPI_HARDWARE_ID);
+ if (error) {
+ warnx("%s: failed to create ACPI device for QEMU FwCfg",
+ __func__);
+ goto done;
+ }
+
+ error = acpi_device_add_res_fixed_ioport(sc.acpi_dev,
+ QEMU_FWCFG_SELECTOR_PORT_NUMBER, 2);
+ if (error) {
+ warnx("%s: failed to add fixed IO port for QEMU FwCfg",
+ __func__);
+ goto done;
+ }
+
+ /* add common fwcfg items */
+ if ((error = qemu_fwcfg_add_item_signature()) != 0) {
+ warnx("%s: Unable to add signature item", __func__);
+ goto done;
+ }
+ if ((error = qemu_fwcfg_add_item_id()) != 0) {
+ warnx("%s: Unable to add id item", __func__);
+ goto done;
+ }
+ if ((error = qemu_fwcfg_add_item_file_dir()) != 0) {
+ warnx("%s: Unable to add file_dir item", __func__);
+ goto done;
+ }
+
+ /*
+ * Handlers for fwcfg ports can only be added if they are unused. That's
+ * only the case when fwcfg is set to qemu.
+ */
+ if (strcmp(lpc_fwcfg(), "qemu") == 0) {
+ if ((error = qemu_fwcfg_register_port("qemu_fwcfg_selector",
+ QEMU_FWCFG_SELECTOR_PORT_NUMBER,
+ QEMU_FWCFG_SELECTOR_PORT_SIZE,
+ QEMU_FWCFG_SELECTOR_PORT_FLAGS,
+ qemu_fwcfg_selector_port_handler)) != 0) {
+ warnx(
+ "%s: Unable to register qemu fwcfg selector port 0x%x",
+ __func__, QEMU_FWCFG_SELECTOR_PORT_NUMBER);
+ goto done;
+ }
+ if ((error = qemu_fwcfg_register_port("qemu_fwcfg_data",
+ QEMU_FWCFG_DATA_PORT_NUMBER, QEMU_FWCFG_DATA_PORT_SIZE,
+ QEMU_FWCFG_DATA_PORT_FLAGS,
+ qemu_fwcfg_data_port_handler)) != 0) {
+ warnx(
+ "%s: Unable to register qemu fwcfg data port 0x%x",
+ __func__, QEMU_FWCFG_DATA_PORT_NUMBER);
+ goto done;
+ }
+ }
+
+ if ((error = qemu_fwcfg_add_user_files()) != 0) {
+ warnx("%s: Unable to add user files", __func__);
+ goto done;
+ }
+
+done:
+ if (error) {
+ acpi_device_destroy(sc.acpi_dev);
+ }
+
+ return (error);
+}
+
+static void
+qemu_fwcfg_usage(const char *opt)
+{
+ warnx("Invalid fw_cfg option \"%s\"", opt);
+ warnx("-f [name=]<name>,(string|file)=<value>");
+}
+
+/*
+ * Parses the cmdline argument for user defined fw_cfg items. The cmdline
+ * argument has the format:
+ * "-f [name=]<name>,(string|file)=<value>"
+ *
+ * E.g.: "-f opt/com.page/example,string=Hello"
+ */
+int
+qemu_fwcfg_parse_cmdline_arg(const char *opt)
+{
+ struct qemu_fwcfg_user_file *const fwcfg_file = malloc(sizeof(*fwcfg_file));
+ if (fwcfg_file == NULL) {
+ warnx("Unable to allocate fw_cfg_user_file");
+ return (-ENOMEM);
+ }
+
+ /* get pointer to <name> */
+ const char *opt_ptr = opt;
+ /* If [name=] is specified, skip it */
+ if (strncmp(opt_ptr, "name=", sizeof("name=") - 1) == 0) {
+ opt_ptr += sizeof("name=") - 1;
+ }
+
+ /* get the end of <name> */
+ const char *opt_end = strchr(opt_ptr, ',');
+ if (opt_end == NULL) {
+ qemu_fwcfg_usage(opt);
+ return (-1);
+ }
+
+ /* check if <name> is too long */
+ if (opt_end - opt_ptr > QEMU_FWCFG_MAX_NAME) {
+ warnx("fw_cfg name too long: \"%s\"", opt);
+ return (-1);
+ }
+
+ /* save <name> */
+ strncpy(fwcfg_file->name, opt_ptr, opt_end - opt_ptr);
+
+ /* set opt_ptr and opt_end to <value> */
+ opt_ptr = opt_end + 1;
+ opt_end = opt_ptr + strlen(opt_ptr);
+
+ if (strncmp(opt_ptr, "string=", sizeof("string=") - 1) == 0) {
+ opt_ptr += sizeof("string=") - 1;
+ fwcfg_file->data = strdup(opt_ptr);
+ if (fwcfg_file->data == NULL) {
+ warnx(" Can't duplicate fw_cfg_user_file string \"%s\"",
+ opt_ptr);
+ return (-ENOMEM);
+ }
+ fwcfg_file->size = strlen(opt_ptr) + 1;
+
+ } else if (strncmp(opt_ptr, "file=", sizeof("file=") - 1) == 0) {
+ opt_ptr += sizeof("file=") - 1;
+
+ /* open file */
+ const int fd = open(opt_ptr, O_RDONLY);
+ if (fd < 0) {
+ warnx("Can't open fw_cfg_user_file file \"%s\"",
+ opt_ptr);
+ return (-1);
+ }
+
+ /* get file size */
+ const uint64_t size = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+
+ /* read file */
+ fwcfg_file->data = malloc(size);
+ if (fwcfg_file->data == NULL) {
+ warnx(
+ "Can't allocate fw_cfg_user_file file \"%s\" (size: 0x%16lx)",
+ opt_ptr, size);
+ close(fd);
+ return (-ENOMEM);
+ }
+ fwcfg_file->size = read(fd, fwcfg_file->data, size);
+
+ close(fd);
+
+ } else {
+ qemu_fwcfg_usage(opt);
+ return (-1);
+ }
+
+ STAILQ_INSERT_TAIL(&user_files, fwcfg_file, chain);
+
+ return (0);
+}
diff --git a/usr.sbin/bhyve/qemu_loader.h b/usr.sbin/bhyve/qemu_loader.h
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_loader.h
@@ -0,0 +1,79 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include "qemu_fwcfg.h"
+
+struct qemu_loader;
+
+/*
+ * Some guest bios like seabios assume the RSDP to be located in the FSEG. Bhyve
+ * only supports OVMF which has no such requirement.
+ */
+enum qemu_loader_zone {
+ QEMU_LOADER_ALLOC_HIGH = 1,
+ QEMU_LOADER_ALLOC_FSEG, /* 0x0F000000 - 0x100000 */
+};
+
+/**
+ * Loads a fwcfg item into guest memory. This command has to be issued before
+ * any subsequent command can be used.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param name Name of the fwcfg item which should be allocated.
+ * @param alignment Alignment required by the data.
+ * @param zone Memory zone in which it should be loaded.
+ */
+int qemu_loader_alloc(struct qemu_loader *loader,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], uint32_t alignment,
+ enum qemu_loader_zone zone);
+/**
+ * Calculates a checksum for @p name and writes it to @p name + @p off . The
+ * checksum calculation ranges from @p start to @p start + @p len. The checksum
+ * field is always one byte large and all bytes in the specified range,
+ * including the checksum, have to sum up to 0.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param name Name of the fwcfg item which should be patched.
+ * @param off Offset into @p name .
+ * @param start Start offset of checksum calculation.
+ * @param len Length of the checksum calculation.
+ */
+int qemu_loader_add_checksum(struct qemu_loader *loader,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], uint32_t off, uint32_t start,
+ uint32_t len);
+/**
+ * Adds the address of @p src_name to the value at @p dest_name + @p off . The
+ * size of the pointer is determined by @p dest_size and should be 1, 2, 4 or 8.
+ *
+ * @param loader Qemu loader instance the command should be added to.
+ * @param dest_name Name of the fwcfg item which should be patched.
+ * @param src_name Name of the fwcfg item which address should be written to
+ * @p dest_name + @p off.
+ * @param off Offset into @p dest_name .
+ * @param size Size of the pointer (1, 2, 4 or 8).
+ */
+int qemu_loader_add_pointer(struct qemu_loader *loader,
+ const uint8_t dest_name[QEMU_FWCFG_MAX_NAME],
+ const uint8_t src_name[QEMU_FWCFG_MAX_NAME], uint32_t off, uint8_t size);
+
+/**
+ * Creates a qemu loader instance.
+ *
+ * @param new_loader Returns the newly created qemu loader instance.
+ * @param fwcfg_name Name of the FwCfg item which represents the qemu loader
+ */
+int qemu_loader_create(struct qemu_loader **new_loader,
+ const uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME]);
+/**
+ * Signals that all commands are written to the qemu loader. This function
+ * creates a proper FwCfg item and registers it.
+ *
+ * @param loader Qemu loader instance which should be finished.
+ */
+int qemu_loader_finish(struct qemu_loader *loader);
diff --git a/usr.sbin/bhyve/qemu_loader.c b/usr.sbin/bhyve/qemu_loader.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/bhyve/qemu_loader.c
@@ -0,0 +1,256 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG
+ * Author: Corvin Köhne <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/queue.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <vmmapi.h>
+
+#include "qemu_loader.h"
+
+struct qemu_loader_entry {
+ uint32_t cmd_le;
+ union {
+ struct {
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+ uint32_t alignment_le;
+ uint8_t zone;
+ } alloc;
+ struct {
+ uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
+ uint8_t src_name[QEMU_FWCFG_MAX_NAME];
+ uint32_t off_le;
+ uint8_t size;
+ } add_pointer;
+ struct {
+ uint8_t name[QEMU_FWCFG_MAX_NAME];
+ uint32_t off_le;
+ uint32_t start_le;
+ uint32_t len_le;
+ } add_checksum;
+ struct {
+ uint8_t dest_name[QEMU_FWCFG_MAX_NAME];
+ uint8_t src_name[QEMU_FWCFG_MAX_NAME];
+ uint32_t dest_off_le;
+ uint32_t src_off_le;
+ uint8_t size;
+ } write_pointer;
+
+ /* padding */
+ uint8_t pad[124];
+ };
+} __packed;
+
+enum qemu_loader_command {
+ QEMU_LOADER_CMD_ALLOC = 0x1,
+ QEMU_LOADER_CMD_ADD_POINTER = 0x2,
+ QEMU_LOADER_CMD_ADD_CHECKSUM = 0x3,
+ QEMU_LOADER_CMD_WRITE_POINTER = 0x4,
+};
+
+struct qemu_loader_element {
+ STAILQ_ENTRY(qemu_loader_element) chain;
+ struct qemu_loader_entry entry;
+};
+
+struct qemu_loader {
+ uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME];
+ STAILQ_HEAD(qemu_loader_list, qemu_loader_element) list;
+};
+
+int
+qemu_loader_alloc(struct qemu_loader *const loader,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], const uint32_t alignment,
+ const enum qemu_loader_zone zone)
+{
+ struct qemu_loader_element *const element = calloc(1,
+ sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ALLOC);
+ strncpy(element->entry.alloc.name, name, QEMU_FWCFG_MAX_NAME);
+ element->entry.alloc.alignment_le = htole32(alignment);
+ element->entry.alloc.zone = zone;
+
+ /*
+ * The guest always works on copies of the fwcfg item, which where
+ * loaded into guest memory. Loading a fwcfg item is caused by ALLOC.
+ * For that reason, ALLOC should be scheduled in front of any other
+ * commands.
+ */
+ STAILQ_INSERT_TAIL(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_add_checksum(struct qemu_loader *const loader,
+ const uint8_t name[QEMU_FWCFG_MAX_NAME], const uint32_t off,
+ const uint32_t start, const uint32_t len)
+{
+ struct qemu_loader_element *const element = calloc(1,
+ sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_CHECKSUM);
+ strncpy(element->entry.add_checksum.name, name, QEMU_FWCFG_MAX_NAME);
+ element->entry.add_checksum.off_le = htole32(off);
+ element->entry.add_checksum.start_le = htole32(start);
+ element->entry.add_checksum.len_le = htole32(len);
+
+ STAILQ_INSERT_TAIL(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_add_pointer(struct qemu_loader *const loader,
+ const uint8_t dest_name[QEMU_FWCFG_MAX_NAME],
+ const uint8_t src_name[QEMU_FWCFG_MAX_NAME], const uint32_t off,
+ const uint8_t size)
+{
+ struct qemu_loader_element *const element = calloc(1,
+ sizeof(struct qemu_loader_element));
+ if (element == NULL) {
+ warnx("%s: failed to allocate command", __func__);
+ return (ENOMEM);
+ }
+
+ element->entry.cmd_le = htole32(QEMU_LOADER_CMD_ADD_POINTER);
+ strncpy(element->entry.add_pointer.dest_name, dest_name,
+ QEMU_FWCFG_MAX_NAME);
+ strncpy(element->entry.add_pointer.src_name, src_name,
+ QEMU_FWCFG_MAX_NAME);
+ element->entry.add_pointer.off_le = htole32(off);
+ element->entry.add_pointer.size = size;
+
+ STAILQ_INSERT_TAIL(&loader->list, element, chain);
+
+ return (0);
+}
+
+int
+qemu_loader_create(struct qemu_loader **const new_loader,
+ const uint8_t fwcfg_name[QEMU_FWCFG_MAX_NAME])
+{
+ if (new_loader == NULL) {
+ return (EINVAL);
+ }
+
+ struct qemu_loader *const loader = (struct qemu_loader *)calloc(1,
+ sizeof(struct qemu_loader));
+ if (loader == NULL) {
+ warnx("%s: failed to allocate loader", __func__);
+ return (ENOMEM);
+ }
+
+ memcpy(loader, fwcfg_name, QEMU_FWCFG_MAX_NAME);
+ STAILQ_INIT(&loader->list);
+
+ *new_loader = loader;
+
+ return (0);
+}
+
+static uint8_t *
+qemu_loader_get_zone_name(const enum qemu_loader_zone zone)
+{
+ switch (zone) {
+ case QEMU_LOADER_ALLOC_HIGH:
+ return "HIGH";
+ case QEMU_LOADER_ALLOC_FSEG:
+ return "FSEG";
+ default:
+ return "Unknown";
+ }
+}
+
+static void
+qemu_loader_dump_entry(const struct qemu_loader_entry *const entry)
+{
+ switch (le32toh(entry->cmd_le)) {
+ case QEMU_LOADER_CMD_ALLOC:
+ printf("CMD_ALLOC\n\r");
+ printf(" name : %s\n\r", entry->alloc.name);
+ printf(" alignment: %8x\n\r", le32toh(entry->alloc.alignment_le));
+ printf(" zone : %s\n\r",
+ qemu_loader_get_zone_name(entry->alloc.zone));
+ break;
+ case QEMU_LOADER_CMD_ADD_POINTER:
+ printf("CMD_ADD_POINTER\n\r");
+ printf(" dest_name: %s\n\r", entry->add_pointer.dest_name);
+ printf(" src_name : %s\n\r", entry->add_pointer.src_name);
+ printf(" off : %8x\n\r", le32toh(entry->add_pointer.off_le));
+ printf(" size : %8x\n\r", entry->add_pointer.size);
+ break;
+ case QEMU_LOADER_CMD_ADD_CHECKSUM:
+ printf("CMD_ADD_CHECKSUM\n\r");
+ printf(" name : %s\n\r", entry->add_checksum.name);
+ printf(" off : %8x\n\r", le32toh(entry->add_checksum.off_le));
+ printf(" start : %8x\n\r", le32toh(entry->add_checksum.start_le));
+ printf(" length : %8x\n\r", le32toh(entry->add_checksum.len_le));
+ break;
+ case QEMU_LOADER_CMD_WRITE_POINTER:
+ printf("CMD_WRITE_POINTER\n\r");
+ printf(" dest_name: %s\n\r", entry->write_pointer.dest_name);
+ printf(" src_name : %s\n\r", entry->write_pointer.src_name);
+ printf(" dest_off : %8x\n\r", le32toh(entry->write_pointer.dest_off_le));
+ printf(" src_off : %8x\n\r", le32toh(entry->write_pointer.src_off_le));
+ printf(" size : %8x\n\r", entry->write_pointer.size);
+ break;
+ default:
+ printf("UNKNOWN\n\r");
+ break;
+ }
+}
+
+int
+qemu_loader_finish(struct qemu_loader *const loader)
+{
+ uint64_t len = 0;
+ struct qemu_loader_element *element;
+ STAILQ_FOREACH (element, &loader->list, chain) {
+ len += sizeof(struct qemu_loader_entry);
+ }
+ if (len == 0) {
+ warnx("%s: bios loader empty", __func__);
+ return (EFAULT);
+ }
+
+ struct qemu_loader_entry *const data = calloc(1, len);
+ if (data == NULL) {
+ warnx("%s: failed to allocate fwcfg data", __func__);
+ return (ENOMEM);
+ }
+
+ int i = 0;
+ STAILQ_FOREACH (element, &loader->list, chain) {
+ memcpy(&data[i], &element->entry,
+ sizeof(struct qemu_loader_entry));
+ ++i;
+ }
+
+ return (qemu_fwcfg_add_file(loader->fwcfg_name, len, data));
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Feb 18, 12:17 PM (14 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28846512
Default Alt Text
D34897.id104954.diff (94 KB)
Attached To
Mode
D34897: bhyve: add support for qemu's ACPI table loader
Attached
Detach File
Event Timeline
Log In to Comment