diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -18,6 +18,7 @@ atkbdc.c \ acpi.c \ audio.c \ + basl.c \ bhyvegc.c \ bhyverun.c \ block_if.c \ 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 @@ -61,6 +47,7 @@ #include #include +#include #include #include #include @@ -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,514 +106,6 @@ #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); -} - -static int -basl_fwrite_madt(FILE *fp) -{ - 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"); - } - - /* 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); - - return (0); - -err_exit: - return (errno); -} - /* * Helper routines for writing to the DSDT from other modules. */ @@ -731,8 +190,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(" {"); @@ -835,26 +296,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 +356,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 +411,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 +859,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/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 + */ + +#pragma once + +#include + +#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) + +#define QEMU_FWCFG_MAX_NAME 56 + +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,544 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "basl.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); + } + + /* + * Guest bios versions without qemu fwcfg support 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); + } + + 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; + } + + /* + * Guest bios versions without qemu fwcfg support 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; + } + + 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); + } + + /* + * Guest bios versions without qemu fwcfg support 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); + } + + 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()); + + return (0); +} + +int +basl_init() +{ + return (0); +} + +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); +}