Page MenuHomeFreeBSD

D34897.id104954.diff
No OneTemporary

D34897.id104954.diff

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

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)

Event Timeline