diff --git a/Makefile b/Makefile index 587b409..232b590 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ VMM_SRC := \ src/vmm/io/vrtc.c XHYVE_SRC := \ - src/acpi.c \ + src/acpitbl.c \ src/atkbdc.c \ src/block_if.c \ src/consport.c \ @@ -61,7 +61,8 @@ XHYVE_SRC := \ src/xmsr.c FIRMWARE_SRC := \ - src/firmware/kexec.c + src/firmware/kexec.c \ + src/firmware/fbsd.c SRC := \ $(VMM_SRC) \ diff --git a/README.md b/README.md index 557df5d..c16b899 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ About ----- -The *xhyve hypervisor* is a port of [bhyve](http://www.bhyve.org) to OS X. It is built on top of Hypervisor.framework in OS X 10.10 Yosemite and higher, runs entirely in userspace, and has no other dependencies. It can run vanilla Linux distributions and may gain support for other guest operating systems in the future. +The *xhyve hypervisor* is a port of [bhyve](http://www.bhyve.org) to OS X. It is built on top of Hypervisor.framework in OS X 10.10 Yosemite and higher, runs entirely in userspace, and has no other dependencies. It can run FreeBSD and vanilla Linux distributions and may gain support for other guest operating systems in the future. License: BSD @@ -184,12 +184,6 @@ TODO - make it not require root - unify TAP and vmnet backends - performance: send/receive more than a single packet at a time -- ACPI tables don't work - - bhyve creates ASL on the fly and then calls out to an ASL compiler (iasl) on - every VM boot to create the DSDT: - - remove dependency on iasl by creating AML bytecode directly - - shouldn't be to hard since we we are only interested in a very small - subset of ASL - virtio_rnd: - is untested - remove explicit state transitions: diff --git a/include/xhyve/acpi.h b/include/xhyve/acpi.h index ebfb611..11969af 100644 --- a/include/xhyve/acpi.h +++ b/include/xhyve/acpi.h @@ -30,6 +30,9 @@ #include +/* if set, create AML instead of ASL and calling out to iasl */ +#define ACPITBL_AML 1 + #define SCI_INT 9 #define SMI_CMD 0xb2 @@ -49,4 +52,6 @@ void dsdt_fixed_irq(uint8_t irq); void dsdt_fixed_mem32(uint32_t base, uint32_t length); void dsdt_indent(int levels); void dsdt_unindent(int levels); +void dsdt_fixup(int bus, uint16_t iobase, uint16_t iolimit, uint32_t membase32, + uint32_t memlimit32, uint64_t membase64, uint64_t memlimit64); void sci_init(void); diff --git a/include/xhyve/firmware/fbsd.h b/include/xhyve/firmware/fbsd.h new file mode 100644 index 0000000..30d4388 --- /dev/null +++ b/include/xhyve/firmware/fbsd.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +/* + * USERBOOT interface versions + */ +#define USERBOOT_VERSION_1 1 +#define USERBOOT_VERSION_2 2 +#define USERBOOT_VERSION_3 3 + +/* + * Exit codes from the loader + */ +#define USERBOOT_EXIT_QUIT 1 +#define USERBOOT_EXIT_REBOOT 2 + +struct loader_callbacks { + /* Console i/o */ + + /* Wait until a key is pressed on the console and then return it */ + int (*getc)(void *arg); + /* Write the character ch to the console */ + void (*putc)(void *arg, int ch); + /* Return non-zero if a key can be read from the console */ + int (*poll)(void *arg); + + /* Host filesystem i/o */ + + /* Open a file in the host filesystem */ + int (*open)(void *arg, const char *filename, void **h_return); + /* Close a file */ + int (*close)(void *arg, void *h); + /* Return non-zero if the file is a directory */ + int (*isdir)(void *arg, void *h); + /* Read size bytes from a file. The number of bytes remaining in dst after + * reading is returned in *resid_return + */ + int (*read)(void *arg, void *h, void *dst, size_t size, + size_t *resid_return); + /* Read an entry from a directory. The entry's inode number is returned in + * fileno_return, its type in *type_return and the name length in + * *namelen_return. The name itself is copied to the buffer name which must + * be at least PATH_MAX in size. + */ + int (*readdir)(void *arg, void *h, uint32_t *fileno_return, + uint8_t *type_return, size_t *namelen_return, char *name); + /* Seek to a location within an open file */ + int (*seek)(void *arg, void *h, uint64_t offset, int whence); + /* Return some stat(2) related information about the file */ + int (*stat)(void *arg, void *h, int *mode_return, int *uid_return, + int *gid_return, uint64_t *size_return); + + /* Disk image i/o */ + + /* Read from a disk image at the given offset */ + int (*diskread)(void *arg, int unit, uint64_t offset, void *dst, + size_t size, size_t *resid_return); + + /* Guest virtual machine i/o */ + + /* Copy to the guest address space */ + int (*copyin)(void *arg, const void *from, uint64_t to, size_t size); + /* Copy from the guest address space */ + int (*copyout)(void *arg, uint64_t from, void *to, size_t size); + /* Set a guest register value */ + void (*setreg)(void *arg, int, uint64_t); + /* Set a guest MSR value */ + void (*setmsr)(void *arg, int, uint64_t); + /* Set a guest CR value */ + void (*setcr)(void *arg, int, uint64_t); + /* Set the guest GDT address */ + void (*setgdt)(void *arg, uint64_t, size_t); + /* Transfer control to the guest at the given address */ + void (*exec)(void *arg, uint64_t pc); + + /* Misc */ + + /* Sleep for usec microseconds */ + void (*delay)(void *arg, int usec); + /* Exit with the given exit code */ + void (*exit)(void); + /* Return guest physical memory map details */ + void (*getmem)(void *arg, uint64_t *lowmem, uint64_t *highmem); + /* ioctl interface to the disk device */ + int (*diskioctl)(void *arg, int unit, u_long cmd, void *data); + /* + * Returns an environment variable in the form "name=value". + * + * If there are no more variables that need to be set in the + * loader environment then return NULL. + * + * 'num' is used as a handle for the callback to identify which + * environment variable to return next. It will begin at 0 and + * each invocation will add 1 to the previous value of 'num'. + */ + const char * (*getenv)(void *arg, int num); +}; + +void fbsd_init(char *userboot_path, char *bootvolume_path, char *kernelenv, + char *cons); +uint64_t fbsd_load(void); diff --git a/include/xhyve/support/segments.h b/include/xhyve/support/segments.h index 4a75c9b..f91c4a3 100644 --- a/include/xhyve/support/segments.h +++ b/include/xhyve/support/segments.h @@ -54,23 +54,26 @@ #define LSEL(s,r) (((s)<<3) | SEL_LDT | r) /* a local selector */ #define GSEL(s,r) (((s)<<3) | r) /* a global selector */ -// /* -// * User segment descriptors (%cs, %ds etc for i386 apps. 64 bit wide) -// * For long-mode apps, %cs only has the conforming bit in sd_type, the sd_dpl, -// * sd_p, sd_l and sd_def32 which must be zero). %ds only has sd_p. -// */ -// struct segment_descriptor { -// unsigned sd_lolimit:16; /* segment extent (lsb) */ -// unsigned sd_lobase:24; /* segment base address (lsb) */ -// unsigned sd_type:5; /* segment type */ -// unsigned sd_dpl:2; /* segment descriptor priority level */ -// unsigned sd_p:1; /* segment descriptor present */ -// unsigned sd_hilimit:4; /* segment extent (msb) */ -// unsigned sd_xx:2; /* unused */ -// unsigned sd_def32:1; /* default 32 vs 16 bit size */ -// unsigned sd_gran:1; /* limit granularity (byte/page units)*/ -// unsigned sd_hibase:8; /* segment base address (msb) */ -// } __packed; +/* + * User segment descriptors (%cs, %ds etc for i386 apps. 64 bit wide) + * For long-mode apps, %cs only has the conforming bit in sd_type, the sd_dpl, + * sd_p, sd_l and sd_def32 which must be zero). %ds only has sd_p. + */ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpacked" +struct segment_descriptor { + unsigned sd_lolimit:16; /* segment extent (lsb) */ + unsigned sd_lobase:24; /* segment base address (lsb) */ + unsigned sd_type:5; /* segment type */ + unsigned sd_dpl:2; /* segment descriptor priority level */ + unsigned sd_p:1; /* segment descriptor present */ + unsigned sd_hilimit:4; /* segment extent (msb) */ + unsigned sd_xx:2; /* unused */ + unsigned sd_def32:1; /* default 32 vs 16 bit size */ + unsigned sd_gran:1; /* limit granularity (byte/page units)*/ + unsigned sd_hibase:8; /* segment base address (msb) */ +} __packed; +#pragma clang diagnostic pop struct user_segment_descriptor { uint64_t sd_lolimit:16; /* segment extent (lsb) */ @@ -167,16 +170,16 @@ struct user_segment_descriptor { // /* memory segment types */ // #define SDT_MEMRO 16 memory read only // #define SDT_MEMROA 17 /* memory read only accessed */ -// #define SDT_MEMRW 18 /* memory read write */ -// #define SDT_MEMRWA 19 /* memory read write accessed */ +#define SDT_MEMRW 18 /* memory read write */ +#define SDT_MEMRWA 19 /* memory read write accessed */ // #define SDT_MEMROD 20 /* memory read only expand dwn limit */ // #define SDT_MEMRODA 21 /* memory read only expand dwn limit accessed */ // #define SDT_MEMRWD 22 /* memory read write expand dwn limit */ // #define SDT_MEMRWDA 23 /* memory read write expand dwn limit accessed*/ // #define SDT_MEME 24 /* memory execute only */ // #define SDT_MEMEA 25 /* memory execute only accessed */ -// #define SDT_MEMER 26 /* memory execute read */ -// #define SDT_MEMERA 27 /* memory execute read accessed */ +#define SDT_MEMER 26 /* memory execute read */ +#define SDT_MEMERA 27 /* memory execute read accessed */ // #define SDT_MEMEC 28 /* memory execute only conforming */ // #define SDT_MEMEAC 29 /* memory execute only accessed conforming */ // #define SDT_MEMERC 30 /* memory execute read conforming */ diff --git a/src/acpitbl.c b/src/acpitbl.c new file mode 100644 index 0000000..48bfd74 --- /dev/null +++ b/src/acpitbl.c @@ -0,0 +1,1076 @@ +/*- + * Copyright (c) 2012 NetApp, Inc. + * Copyright (c) 2015 xhyve developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * xhyve ACPI table generator. + * + * Does not require iasl but DSDT is limited to 1 PCI bus (0) and 8 devices. + * + * slot 0 hostbridge + * slot 31 lpc + */ + +/* + * The tables are placed in the guest's ROM area just below 1MB physical, + * above the MPTable. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define XHYVE_ACPI_BASE 0xf2400 +#define XHYVE_ACPI_SIZE 0xdc00 +#define RSDT_OFFSET 0x040 +#define XSDT_OFFSET 0x080 +#define MADT_OFFSET 0x100 +#define FADT_OFFSET 0x200 +#define HPET_OFFSET 0x340 +#define MCFG_OFFSET 0x380 +#define FACS_OFFSET 0x3C0 +#define DSDT_OFFSET 0x400 + +/* ACPI table base in guest memory */ +static void *tb; +static int acpi_ncpu; +static uint32_t hpet_capabilities; +static void *dsdt; + +void +dsdt_line(UNUSED const char *fmt, ...) +{ +} + +void +dsdt_fixed_ioport(UNUSED uint16_t iobase, UNUSED uint16_t length) +{ +} + +void +dsdt_fixed_irq(UNUSED uint8_t irq) +{ +} + +void +dsdt_fixed_mem32(UNUSED uint32_t base, UNUSED uint32_t length) +{ +} + +void +dsdt_indent(UNUSED int levels) +{ +} + +void dsdt_unindent(UNUSED int levels) +{ +} + +static uint8_t +acpitbl_checksum(void *table, size_t length) { + unsigned int i; + uint8_t sum; + + for (sum = 0, i = 0; i < length; i++) { + sum += ((uint8_t *) table)[i]; + } + + return (((uint8_t) 0) - sum); +} + +static void +acpitbl_write8(void *base, uint64_t offset, uint8_t val) { + memcpy(((void *) (((uintptr_t) base) + offset)), &val, 1); +} + +static void +acpitbl_write16(void *base, uint64_t offset, uint16_t val) { + memcpy(((void *) (((uintptr_t) base) + offset)), &val, 2); +} + +static void +acpitbl_write32(void *base, uint64_t offset, uint32_t val) { + memcpy(((void *) (((uintptr_t) base) + offset)), &val, 4); +} + +static void +acpitbl_write64(void *base, uint64_t offset, uint64_t val) { + memcpy(((void *) (((uintptr_t) base) + offset)), &val, 8); +} + +static void +acpitbl_build_rdsp(void) { + void *rdsp; + /* + * [000h 0000 8] Signature : "RSD PTR " + * [008h 0008 1] Checksum : 00 + * [009h 0009 6] Oem ID : "BHYVE " + * [00Fh 0015 1] Revision : 02 + * [010h 0016 4] RSDT Address : 00000000 + * [014h 0020 4] Length : 00000024 + * [018h 0024 8] XSDT Address : 0000000000000000 + * [020h 0032 1] Extended Checksum : 00 + * [021h 0033 3] Reserved : 000000 + */ + static const uint8_t rdsp_tmpl[36] = { + 0x52, 0x53, 0x44, 0x20, 0x50, 0x54, 0x52, 0x20, + 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + rdsp = (void *) (((uintptr_t) tb) + 0); + /* copy RDSP template to guest memory */ + memcpy(rdsp, rdsp_tmpl, 36); + /* fixup table */ + acpitbl_write32(rdsp, 0x10, ((uint32_t) (XHYVE_ACPI_BASE + RSDT_OFFSET))); + acpitbl_write64(rdsp, 0x18, ((uint64_t) (XHYVE_ACPI_BASE + XSDT_OFFSET))); + /* write checksum */ + acpitbl_write8(rdsp, 0x8, acpitbl_checksum(rdsp, 20)); + /* write extended checksum */ + acpitbl_write8(rdsp, 0x20, acpitbl_checksum(rdsp, 36)); +} + +static void +acpitbl_build_rsdt(void) { + void *rsdt; + /* + * [000h 0000 4] Signature : "RSDT" + * [004h 0004 4] Table Length : 00000034 + * [008h 0008 1] Revision : 01 + * [009h 0009 1] Checksum : 00 + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVRSDT " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 4] ACPI Table Address 0 : 00000000 + * [028h 0040 4] ACPI Table Address 1 : 00000000 + * [02Ch 0044 4] ACPI Table Address 2 : 00000000 + * [030h 0048 4] ACPI Table Address 3 : 00000000 + */ + static const uint8_t rsdt_tmpl[52] = { + 0x52, 0x53, 0x44, 0x54, 0x34, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x52, 0x53, 0x44, 0x54, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + rsdt = (void *) (((uintptr_t) tb) + RSDT_OFFSET); + /* copy RSDT template to guest memory */ + memcpy(rsdt, rsdt_tmpl, 52); + /* fixup table */ + acpitbl_write32(rsdt, 0x24, ((uint32_t) (XHYVE_ACPI_BASE + MADT_OFFSET))); + acpitbl_write32(rsdt, 0x28, ((uint32_t) (XHYVE_ACPI_BASE + FADT_OFFSET))); + acpitbl_write32(rsdt, 0x2c, ((uint32_t) (XHYVE_ACPI_BASE + HPET_OFFSET))); + acpitbl_write32(rsdt, 0x30, ((uint32_t) (XHYVE_ACPI_BASE + MCFG_OFFSET))); + /* write checksum */ + acpitbl_write8(rsdt, 0x9, acpitbl_checksum(rsdt, 52)); +} + +static void +acpitbl_build_xsdt(void) { + void *xsdt; + /* + * [000h 0000 4] Signature : "XSDT" + * [004h 0004 4] Table Length : 00000044 + * [008h 0008 1] Revision : 01 + * [009h 0009 1] Checksum : 00 + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVXSDT " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 8] ACPI Table Address 0 : 0000000000000000 + * [02Ch 0044 8] ACPI Table Address 1 : 0000000000000000 + * [034h 0052 8] ACPI Table Address 2 : 0000000000000000 + * [03Ch 0060 8] ACPI Table Address 3 : 0000000000000000 + */ + static const uint8_t xsdt_tmpl[68] = { + 0x58, 0x53, 0x44, 0x54, 0x44, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x58, 0x53, 0x44, 0x54, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + xsdt = (void *) (((uintptr_t) tb) + XSDT_OFFSET); + /* copy XSDT template to guest memory */ + memcpy(xsdt, xsdt_tmpl, 68); + /* fixup table */ + acpitbl_write64(xsdt, 0x24, ((uint64_t) (XHYVE_ACPI_BASE + MADT_OFFSET))); + acpitbl_write64(xsdt, 0x2c, ((uint64_t) (XHYVE_ACPI_BASE + FADT_OFFSET))); + acpitbl_write64(xsdt, 0x34, ((uint64_t) (XHYVE_ACPI_BASE + HPET_OFFSET))); + acpitbl_write64(xsdt, 0x3c, ((uint64_t) (XHYVE_ACPI_BASE + MCFG_OFFSET))); + /* write checksum */ + acpitbl_write8(xsdt, 0x9, acpitbl_checksum(xsdt, 68)); +} + +static void +acpitbl_build_madt(void) { + void *madt_head, *madt_apic, *madt_tail; + int i; + /* + * [000h 0000 4] Signature : "APIC" + * [004h 0004 4] Table Length : 00000000 + * [008h 0008 1] Revision : 01 + * [009h 0009 1] Checksum : 4E + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVMADT " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 4] Local Apic Address : FEE00000 + * [028h 0040 4] Flags (decoded below) : 00000001 + * PC-AT Compatibility : 1 + */ + static const uint8_t madt_head_tmpl[44] = { + 0x41, 0x50, 0x49, 0x43, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x4D, 0x41, 0x44, 0x54, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0xE0, 0xFE, + 0x01, 0x00, 0x00, 0x00, + }; + /* + * [+000h +0000 1] Subtable Type : 00 + * [+001h +0001 1] Length : 08 + * [+002h +0002 1] Processor ID : 00 + * [+003h +0003 1] Local Apic ID : 00 + * [+004h +0004 4] Flags (decoded below) : 00000001 + * Processor Enabled : 1 + */ + static const uint8_t madt_apic_tmpl[8] = { + 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + }; + /* + * [+000h +0000 1] Subtable Type : 01 + * [+001h +0001 1] Length : 0C + * [+002h +0002 1] I/O Apic ID : 00 + * [+003h +0003 1] Reserved : 00 + * [+004h +0004 4] Address : FEC00000 + * [+008h +0008 4] Interrupt : 00000000 + * [+00Ch +0012 1] Subtable Type : 02 + * [+00Dh +0013 1] Length : 0A + * [+00Eh +0014 1] Bus : 00 + * [+00Fh +0015 1] Source : 00 + * [+010h +0016 4] Interrupt : 00000002 + * [+014h +0020 2] Flags (decoded below) : 0005 + * Polarity : 1 + * Trigger Mode : 1 + * [+016h +0022 1] Subtable Type : 02 + * [+017h +0023 1] Length : 0A + * [+018h +0024 1] Bus : 00 + * [+019h +0025 1] Source : 00 + * [+01Ah +0026 4] Interrupt : 00000000 + * [+01Eh +0030 2] Flags (decoded below) : 000F + * Polarity : 3 + * Trigger Mode : 3 + * [+020h +0032 1] Subtable Type : 04 + * [+021h +0033 1] Length : 06 + * [+022h +0034 1] Processor ID : FF + * [+023h +0035 2] Flags (decoded below) : 0005 + * Polarity : 1 + * Trigger Mode : 1 + * [+025h +0037 1] Interrupt Input LINT : 01 + */ + static const uint8_t madt_tail_tmpl[38] = { + 0x01, 0x0C, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x0A, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, + 0x04, 0x06, 0xFF, 0x05, 0x00, 0x01 + }; + + madt_head = (void *) (((uintptr_t) tb) + MADT_OFFSET); + /* copy MADT head template to guest memory */ + memcpy(madt_head, madt_head_tmpl, 44); + + for (i = 0; i < acpi_ncpu; i++) { + madt_apic = (void *) (((uintptr_t) tb) + + ((size_t) ((MADT_OFFSET + 44) + (8 * i)))); + /* copy MADT APIC template to guest memory */ + memcpy(madt_apic, madt_apic_tmpl, 8); + /* fixup table */ + acpitbl_write8(madt_apic, 0x2, ((uint8_t) i)); + acpitbl_write8(madt_apic, 0x3, ((uint8_t) i)); + } + + madt_tail = (void *) (((uintptr_t) tb) + + ((size_t) ((MADT_OFFSET + 44) + (8 * acpi_ncpu)))); + /* copy MADT tail template to guest memory */ + memcpy(madt_tail, madt_tail_tmpl, 38); + /* fixup table */ + acpitbl_write8(madt_tail, 0x2, 0); + acpitbl_write8(madt_tail, 0x19, SCI_INT); + acpitbl_write32(madt_tail, 0x1a, SCI_INT); + /* write checksum */ + acpitbl_write32(madt_head, 0x4, ((uint32_t) (44 + (8 * acpi_ncpu) + 38))); + acpitbl_write8(madt_head, 0x9, + acpitbl_checksum(madt_head, ((size_t) (44 + (8 * acpi_ncpu) + 38)))); +} + +static void +acpitbl_build_fadt(void) { + void *fadt; + /* + * [000h 0000 4] Signature : "FACP" + * [004h 0004 4] Table Length : 0000010C + * [008h 0008 1] Revision : 05 + * [009h 0009 1] Checksum : 00 + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVFACP " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 4] FACS Address : 00000000 + * [028h 0040 4] DSDT Address : 00000000 + * [02Ch 0044 1] Model : 01 + * [02Dh 0045 1] PM Profile : 00 (Unspecified) + * [02Eh 0046 2] SCI Interrupt : 0000 + * [030h 0048 4] SMI Command Port : 00000000 + * [034h 0052 1] ACPI Enable Value : 00 + * [035h 0053 1] ACPI Disable Value : 00 + * [036h 0054 1] S4BIOS Command : 00 + * [037h 0055 1] P-State Control : 00 + * [038h 0056 4] PM1A Event Block Address : 00000000 + * [03Ch 0060 4] PM1B Event Block Address : 00000000 + * [040h 0064 4] PM1A Control Block Address : 00000000 + * [044h 0068 4] PM1B Control Block Address : 00000000 + * [048h 0072 4] PM2 Control Block Address : 00000000 + * [04Ch 0076 4] PM Timer Block Address : 00000000 + * [050h 0080 4] GPE0 Block Address : 00000000 + * [054h 0084 4] GPE1 Block Address : 00000000 + * [058h 0088 1] PM1 Event Block Length : 04 + * [059h 0089 1] PM1 Control Block Length : 02 + * [05Ah 0090 1] PM2 Control Block Length : 00 + * [05Bh 0091 1] PM Timer Block Length : 04 + * [05Ch 0092 1] GPE0 Block Length : 00 + * [05Dh 0093 1] GPE1 Block Length : 00 + * [05Eh 0094 1] GPE1 Base Offset : 00 + * [05Fh 0095 1] _CST Support : 00 + * [060h 0096 2] C2 Latency : 0000 + * [062h 0098 2] C3 Latency : 0000 + * [064h 0100 2] CPU Cache Size : 0000 + * [066h 0102 2] Cache Flush Stride : 0000 + * [068h 0104 1] Duty Cycle Offset : 00 + * [069h 0105 1] Duty Cycle Width : 00 + * [06Ah 0106 1] RTC Day Alarm Index : 00 + * [06Bh 0107 1] RTC Month Alarm Index : 00 + * [06Ch 0108 1] RTC Century Index : 32 + * [06Dh 0109 2] Boot Flags (decoded below) : 0014 + * Legacy Devices Supported (V2) : 0 + * 8042 Present on ports 60/64 (V2) : 0 + * VGA Not Present (V4) : 1 + * MSI Not Supported (V4) : 0 + * PCIe ASPM Not Supported (V4) : 1 + * [06Fh 0111 1] Reserved : 00 + * [070h 0112 4] Flags (decoded below) : 00081525 + * WBINVD instruction is operational (V1) : 1 + * WBINVD flushes all caches (V1) : 0 + * All CPUs support C1 (V1) : 1 + * C2 works on MP system (V1) : 0 + * Control Method Power Button (V1) : 0 + * Control Method Sleep Button (V1) : 1 + * RTC wake not in fixed reg space (V1) : 0 + * RTC can wake system from S4 (V1) : 0 + * 32-bit PM Timer (V1) : 1 + * Docking Supported (V1) : 0 + * Reset Register Supported (V2) : 1 + * Sealed Case (V3) : 0 + * Headless - No Video (V3) : 1 + * Use native instr after SLP_TYPx (V3) : 0 + * PCIEXP_WAK Bits Supported (V4) : 0 + * Use Platform Timer (V4) : 0 + * RTC_STS valid on S4 wake (V4) : 0 + * Remote Power-on capable (V4) : 0 + * Use APIC Cluster Model (V4) : 0 + * Use APIC Physical Destination Mode (V4) : 1 + * [074h 0116 12] Reset Register : + * [074h 0116 1] Space ID : 01 (SystemIO) + * [075h 0117 1] Bit Width : 08 + * [076h 0118 1] Bit Offset : 00 + * [077h 0119 1] Access Width : 01 + * [078h 0120 8] Address : 0000000000000CF9 + * [080h 0128 1] Value to cause reset : 06 + * [081h 0129 3] Reserved : 000001 + * [084h 0132 8] FACS Address : 0000000000000000 + * [08Ch 0140 8] DSDT Address : 0000000000000000 + * [094h 0148 12] PM1A Event Block : + * [094h 0148 1] Space ID : 01 (SystemIO) + * [095h 0149 1] Bit Width : 20 + * [096h 0150 1] Bit Offset : 00 + * [097h 0151 1] Access Width : 02 + * [098h 0152 8] Address : 0000000000000000 + * [0A0h 0160 12] PM1B Event Block : + * [0A0h 0160 1] Space ID : 01 (SystemIO) + * [0A1h 0161 1] Bit Width : 00 + * [0A2h 0162 1] Bit Offset : 00 + * [0A3h 0163 1] Access Width : 00 + * [0A4h 0164 8] Address : 0000000000000000 + * [0ACh 0172 12] PM1A Control Block : + * [0ACh 0172 1] Space ID : 01 (SystemIO) + * [0ADh 0173 1] Bit Width : 10 + * [0AEh 0174 1] Bit Offset : 00 + * [0AFh 0175 1] Access Width : 02 + * [0B0h 0176 8] Address : 0000000000000000 + * [0B8h 0184 12] PM1B Control Block : + * [0B8h 0184 1] Space ID : 01 (SystemIO) + * [0B9h 0185 1] Bit Width : 00 + * [0BAh 0186 1] Bit Offset : 00 + * [0BBh 0187 1] Access Width : 00 + * [0BCh 0188 8] Address : 0000000000000000 + * [0C4h 0196 12] PM2 Control Block : + * [0C4h 0196 1] Space ID : 01 (SystemIO) + * [0C5h 0197 1] Bit Width : 08 + * [0C6h 0198 1] Bit Offset : 00 + * [0C7h 0199 1] Access Width : 00 + * [0C8h 0200 8] Address : 0000000000000000 + * [0D0h 0208 12] PM Timer Block : + * [0D0h 0208 1] Space ID : 01 (SystemIO) + * [0D1h 0209 1] Bit Width : 20 + * [0D2h 0210 1] Bit Offset : 00 + * [0D3h 0211 1] Access Width : 03 + * [0D4h 0212 8] Address : 0000000000000000 + * [0DCh 0220 12] GPE0 Block : + * [0DCh 0220 1] Space ID : 01 (SystemIO) + * [0DDh 0221 1] Bit Width : 00 + * [0DEh 0222 1] Bit Offset : 00 + * [0DFh 0223 1] Access Width : 01 + * [0E0h 0224 8] Address : 0000000000000000 + * [0E8h 0232 12] GPE1 Block : + * [0E8h 0232 1] Space ID : 01 (SystemIO) + * [0E9h 0233 1] Bit Width : 00 + * [0EAh 0234 1] Bit Offset : 00 + * [0EBh 0235 1] Access Width : 00 + * [0ECh 0236 8] Address : 0000000000000000 + * [0F4h 0244 12] Sleep Control Register : + * [0F4h 0244 1] Space ID : 01 (SystemIO) + * [0F5h 0245 1] Bit Width : 08 + * [0F6h 0246 1] Bit Offset : 00 + * [0F7h 0247 1] Access Width : 01 + * [0F8h 0248 8] Address : 0000000000000000 + * [100h 0256 12] Sleep Status Register : + * [100h 0256 01] Space ID : 01 (SystemIO) + * [101h 0257 01] Bit Width : 08 + * [102h 0258 01] Bit Offset : 00 + * [103h 0259 01] Access Width : 01 + * [104h 0260 08] Address : 0000000000000000 + */ + static const uint8_t fadt_tmpl[268] = { + 0x46, 0x41, 0x43, 0x50, 0x0C, 0x01, 0x00, 0x00, + 0x05, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x46, 0x41, 0x43, 0x50, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x14, 0x00, 0x00, + 0x25, 0x15, 0x08, 0x00, 0x01, 0x08, 0x00, 0x01, + 0xF9, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x20, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + fadt = (void *) (((uintptr_t) tb) + FADT_OFFSET); + /* copy FADT template to guest memory */ + memcpy(fadt, fadt_tmpl, 268); + /* fixup table */ + acpitbl_write32(fadt, 0x24, ((uint32_t) (XHYVE_ACPI_BASE + FACS_OFFSET))); + acpitbl_write32(fadt, 0x28, ((uint32_t) (XHYVE_ACPI_BASE + DSDT_OFFSET))); + acpitbl_write16(fadt, 0x2e, SCI_INT); + acpitbl_write32(fadt, 0x30, SMI_CMD); + acpitbl_write8(fadt, 0x34, BHYVE_ACPI_ENABLE); + acpitbl_write8(fadt, 0x35, BHYVE_ACPI_DISABLE); + acpitbl_write32(fadt, 0x38, PM1A_EVT_ADDR); + acpitbl_write32(fadt, 0x40, PM1A_CNT_ADDR); + acpitbl_write32(fadt, 0x4c, IO_PMTMR); + acpitbl_write64(fadt, 0x84, ((uint64_t) (XHYVE_ACPI_BASE + FACS_OFFSET))); + acpitbl_write64(fadt, 0x8c, ((uint64_t) (XHYVE_ACPI_BASE + DSDT_OFFSET))); + acpitbl_write64(fadt, 0x98, ((uint64_t) PM1A_EVT_ADDR)); + acpitbl_write64(fadt, 0xb0, ((uint64_t) PM1A_CNT_ADDR)); + acpitbl_write64(fadt, 0xd4, ((uint64_t) IO_PMTMR)); + /* write checksum */ + acpitbl_write8(fadt, 0x9, acpitbl_checksum(fadt, 268)); +} + +static void +acpitbl_build_hpet(void) { + void *hpet; + /* + * [000h 0000 4] Signature : "HPET" + * [004h 0004 4] Table Length : 00000038 + * [008h 0008 1] Revision : 01 + * [009h 0009 1] Checksum : 00 + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVHPET " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 4] Hardware Block ID : 00000000 + * [028h 0040 12] Timer Block Register : + * [028h 0040 1] Space ID : 00 (SystemMemory) + * [029h 0041 1] Bit Width : 00 + * [02Ah 0042 1] Bit Offset : 00 + * [02Bh 0043 1] Access Width : 00 + * [02Ch 0044 8] Address : 00000000FED00000 + * [034h 0052 1] Sequence Number : 00 + * [035h 0053 2] Minimum Clock Ticks : 0000 + * [037h 0055 1] Flags (decoded below) : 01 + * 4K Page Protect : 1 + * 64K Page Protect : 0 + */ + static const uint8_t hpet_tmpl[56] = { + 0x48, 0x50, 0x45, 0x54, 0x38, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x48, 0x50, 0x45, 0x54, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + + hpet = (void *) (((uintptr_t) tb) + HPET_OFFSET); + /* copy HPET template to guest memory */ + memcpy(hpet, hpet_tmpl, 56); + /* fixup table */ + acpitbl_write32(hpet, 0x24, hpet_capabilities); + /* write checksum */ + acpitbl_write8(hpet, 0x9, acpitbl_checksum(hpet, 56)); +} + +static void +acpitbl_build_mcfg(void) { + void *mcfg; + /* + * [000h 0000 4] Signature : "MCFG" + * [004h 0004 4] Table Length : 0000003C + * [008h 0008 1] Revision : 01 + * [009h 0009 1] Checksum : 00 + * [00Ah 0010 6] Oem ID : "BHYVE " + * [010h 0016 8] Oem Table ID : "BVMCFG " + * [018h 0024 4] Oem Revision : 00000001 + * [01Ch 0028 4] Asl Compiler ID : "INTL" + * [020h 0032 4] Asl Compiler Revision : 20140828 + * [024h 0036 8] Reserved : 0000000000000000 + * [02Ch 0044 8] Base Address : 0000000000000000 + * [034h 0052 2] Segment Group Number : 0000 + * [036h 0054 1] Start Bus Number : 00 + * [037h 0055 1] End Bus Number : FF + * [038h 0056 4] Reserved : 00000000 + */ + static const uint8_t mcfg_tmpl[60] = { + 0x4D, 0x43, 0x46, 0x47, 0x3C, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x4D, 0x43, 0x46, 0x47, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4E, 0x54, 0x4C, + 0x28, 0x08, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00 + }; + + mcfg = (void *) (((uintptr_t) tb) + MCFG_OFFSET); + /* copy MCFG template to guest memory */ + memcpy(mcfg, mcfg_tmpl, 60); + /* fixup table */ + acpitbl_write64(mcfg, 0x2c, pci_ecfg_base()); + /* write checksum */ + acpitbl_write8(mcfg, 0x9, acpitbl_checksum(mcfg, 60)); +} + +static void +acpitbl_build_facs(void) { + void *facs; + /* + * [000h 0000 4] Signature : "FACS" + * [004h 0004 4] Length : 00000040 + * [008h 0008 4] Hardware Signature : 00000000 + * [00Ch 0012 4] 32 Firmware Waking Vector : 00000000 + * [010h 0016 4] Global Lock : 00000000 + * [014h 0020 4] Flags (decoded below) : 00000000 + * S4BIOS Support Present : 0 + * 64-bit Wake Supported (V2) : 0 + * [018h 0024 8] 64 Firmware Waking Vector : 0000000000000000 + * [020h 0032 1] Version : 02 + * [021h 0033 3] Reserved : 000000 + * [024h 0036 4] OspmFlags (decoded below) : 00000000 + * 64-bit Wake Env Required (V2) : 0 + */ + static const uint8_t facs_tmpl[64] = { + 0x46, 0x41, 0x43, 0x53, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + facs = (void *) (((uintptr_t) tb) + FACS_OFFSET); + /* copy MCFG template to guest memory */ + memcpy(facs, facs_tmpl, 64); +} + +void dsdt_fixup(int bus, uint16_t iobase, uint16_t iolimit, uint32_t membase32, + uint32_t memlimit32, uint64_t membase64, uint64_t memlimit64) +{ + if (bus != 0) { + fprintf(stderr, "DSDT, unsupported PCI bus (%d)\n", bus); + exit(-1); + } + + acpitbl_write16(dsdt, 0xb6, iobase); + acpitbl_write16(dsdt, 0xb8, (iolimit - 1)); + acpitbl_write16(dsdt, 0xbc, (iolimit - iobase)); + acpitbl_write32(dsdt, 0xc8, membase32); + acpitbl_write32(dsdt, 0xcc, (memlimit32 - 1)); + acpitbl_write32(dsdt, 0xd4, (memlimit32 - membase32)); + acpitbl_write64(dsdt, 0xe6, membase64); + acpitbl_write64(dsdt, 0xee, (memlimit64 - 1)); + acpitbl_write64(dsdt, 0xfe, (memlimit64 - membase64)); +} + +static void +acpitbl_build_dsdt(void) { + static const uint8_t dsdt_tmpl[2604] = { + 0x44, 0x53, 0x44, 0x54, 0x2d, 0x0a, 0x00, 0x00, + 0x02, 0x5d, 0x42, 0x48, 0x59, 0x56, 0x45, 0x20, + 0x42, 0x56, 0x44, 0x53, 0x44, 0x54, 0x20, 0x20, + 0x01, 0x00, 0x00, 0x00, 0x49, 0x4e, 0x54, 0x4c, + 0x28, 0x08, 0x14, 0x20, 0x08, 0x5f, 0x53, 0x35, + 0x5f, 0x12, 0x05, 0x02, 0x0a, 0x05, 0x00, 0x08, + 0x50, 0x49, 0x43, 0x4d, 0x0a, 0x00, 0x14, 0x0c, + 0x5f, 0x50, 0x49, 0x43, 0x01, 0x70, 0x68, 0x50, + 0x49, 0x43, 0x4d, 0x10, 0x4f, 0x9a, 0x5f, 0x53, + 0x42, 0x5f, 0x5b, 0x82, 0x47, 0x9a, 0x50, 0x43, + 0x30, 0x30, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, + 0x41, 0xd0, 0x0a, 0x03, 0x08, 0x5f, 0x41, 0x44, + 0x52, 0x00, 0x14, 0x09, 0x5f, 0x42, 0x42, 0x4e, + 0x00, 0xa4, 0x0a, 0x00, 0x08, 0x5f, 0x43, 0x52, + 0x53, 0x11, 0x46, 0x09, 0x0a, 0x92, 0x88, 0x0d, + 0x00, 0x02, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x47, 0x01, + 0xf8, 0x0c, 0xf8, 0x0c, 0x01, 0x08, 0x88, 0x0d, + 0x00, 0x01, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0x0c, 0x00, 0x00, 0xf8, 0x0c, 0x88, 0x0d, + 0x00, 0x01, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x0d, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0x13, 0x88, 0x0d, + 0x00, 0x01, 0x0c, 0x03, 0x00, 0x00, 0x00, 0x20, + 0x1f, 0x20, 0x00, 0x00, 0x20, 0x00, 0x87, 0x17, + 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x1f, 0xc0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x8a, 0x2b, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x0f, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x79, 0x00, + 0x08, 0x50, 0x50, 0x52, 0x54, 0x12, 0x4b, 0x0f, + 0x08, 0x12, 0x1e, 0x04, 0x0c, 0xff, 0xff, 0x01, + 0x00, 0x0a, 0x00, 0x5c, 0x2f, 0x04, 0x5f, 0x53, + 0x42, 0x5f, 0x50, 0x43, 0x30, 0x30, 0x49, 0x53, + 0x41, 0x5f, 0x4c, 0x4e, 0x4b, 0x41, 0x0a, 0x00, + 0x12, 0x1e, 0x04, 0x0c, 0xff, 0xff, 0x02, 0x00, + 0x0a, 0x00, 0x5c, 0x2f, 0x04, 0x5f, 0x53, 0x42, + 0x5f, 0x50, 0x43, 0x30, 0x30, 0x49, 0x53, 0x41, + 0x5f, 0x4c, 0x4e, 0x4b, 0x42, 0x0a, 0x00, 0x12, + 0x1e, 0x04, 0x0c, 0xff, 0xff, 0x03, 0x00, 0x0a, + 0x00, 0x5c, 0x2f, 0x04, 0x5f, 0x53, 0x42, 0x5f, + 0x50, 0x43, 0x30, 0x30, 0x49, 0x53, 0x41, 0x5f, + 0x4c, 0x4e, 0x4b, 0x43, 0x0a, 0x00, 0x12, 0x1e, + 0x04, 0x0c, 0xff, 0xff, 0x04, 0x00, 0x0a, 0x00, + 0x5c, 0x2f, 0x04, 0x5f, 0x53, 0x42, 0x5f, 0x50, + 0x43, 0x30, 0x30, 0x49, 0x53, 0x41, 0x5f, 0x4c, + 0x4e, 0x4b, 0x44, 0x0a, 0x00, 0x12, 0x1e, 0x04, + 0x0c, 0xff, 0xff, 0x05, 0x00, 0x0a, 0x00, 0x5c, + 0x2f, 0x04, 0x5f, 0x53, 0x42, 0x5f, 0x50, 0x43, + 0x30, 0x30, 0x49, 0x53, 0x41, 0x5f, 0x4c, 0x4e, + 0x4b, 0x45, 0x0a, 0x00, 0x12, 0x1e, 0x04, 0x0c, + 0xff, 0xff, 0x06, 0x00, 0x0a, 0x00, 0x5c, 0x2f, + 0x04, 0x5f, 0x53, 0x42, 0x5f, 0x50, 0x43, 0x30, + 0x30, 0x49, 0x53, 0x41, 0x5f, 0x4c, 0x4e, 0x4b, + 0x46, 0x0a, 0x00, 0x12, 0x1e, 0x04, 0x0c, 0xff, + 0xff, 0x07, 0x00, 0x0a, 0x00, 0x5c, 0x2f, 0x04, + 0x5f, 0x53, 0x42, 0x5f, 0x50, 0x43, 0x30, 0x30, + 0x49, 0x53, 0x41, 0x5f, 0x4c, 0x4e, 0x4b, 0x47, + 0x0a, 0x00, 0x12, 0x1e, 0x04, 0x0c, 0xff, 0xff, + 0x08, 0x00, 0x0a, 0x00, 0x5c, 0x2f, 0x04, 0x5f, + 0x53, 0x42, 0x5f, 0x50, 0x43, 0x30, 0x30, 0x49, + 0x53, 0x41, 0x5f, 0x4c, 0x4e, 0x4b, 0x48, 0x0a, + 0x00, 0x08, 0x41, 0x50, 0x52, 0x54, 0x12, 0x4b, + 0x06, 0x08, 0x12, 0x0c, 0x04, 0x0c, 0xff, 0xff, + 0x01, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x10, 0x12, + 0x0c, 0x04, 0x0c, 0xff, 0xff, 0x02, 0x00, 0x0a, + 0x00, 0x00, 0x0a, 0x11, 0x12, 0x0c, 0x04, 0x0c, + 0xff, 0xff, 0x03, 0x00, 0x0a, 0x00, 0x00, 0x0a, + 0x12, 0x12, 0x0c, 0x04, 0x0c, 0xff, 0xff, 0x04, + 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x13, 0x12, 0x0c, + 0x04, 0x0c, 0xff, 0xff, 0x05, 0x00, 0x0a, 0x00, + 0x00, 0x0a, 0x14, 0x12, 0x0c, 0x04, 0x0c, 0xff, + 0xff, 0x06, 0x00, 0x0a, 0x00, 0x00, 0x0a, 0x15, + 0x12, 0x0c, 0x04, 0x0c, 0xff, 0xff, 0x07, 0x00, + 0x0a, 0x00, 0x00, 0x0a, 0x16, 0x12, 0x0c, 0x04, + 0x0c, 0xff, 0xff, 0x08, 0x00, 0x0a, 0x00, 0x00, + 0x0a, 0x17, 0x14, 0x18, 0x5f, 0x50, 0x52, 0x54, + 0x00, 0xa0, 0x0a, 0x50, 0x49, 0x43, 0x4d, 0xa4, + 0x41, 0x50, 0x52, 0x54, 0xa1, 0x06, 0xa4, 0x50, + 0x50, 0x52, 0x54, 0x5b, 0x82, 0x4e, 0x75, 0x49, + 0x53, 0x41, 0x5f, 0x08, 0x5f, 0x41, 0x44, 0x52, + 0x0c, 0x00, 0x00, 0x1f, 0x00, 0x5b, 0x80, 0x4c, + 0x50, 0x43, 0x52, 0x02, 0x0a, 0x00, 0x0b, 0x00, + 0x01, 0x5b, 0x81, 0x33, 0x4c, 0x50, 0x43, 0x52, + 0x00, 0x00, 0x40, 0x30, 0x50, 0x49, 0x52, 0x41, + 0x08, 0x50, 0x49, 0x52, 0x42, 0x08, 0x50, 0x49, + 0x52, 0x43, 0x08, 0x50, 0x49, 0x52, 0x44, 0x08, + 0x00, 0x20, 0x50, 0x49, 0x52, 0x45, 0x08, 0x50, + 0x49, 0x52, 0x46, 0x08, 0x50, 0x49, 0x52, 0x47, + 0x08, 0x50, 0x49, 0x52, 0x48, 0x08, 0x14, 0x33, + 0x50, 0x49, 0x52, 0x56, 0x01, 0xa0, 0x09, 0x7b, + 0x68, 0x0a, 0x80, 0x00, 0xa4, 0x0a, 0x00, 0x7b, + 0x68, 0x0a, 0x0f, 0x60, 0xa0, 0x08, 0x95, 0x60, + 0x0a, 0x03, 0xa4, 0x0a, 0x00, 0xa0, 0x08, 0x93, + 0x60, 0x0a, 0x08, 0xa4, 0x0a, 0x00, 0xa0, 0x08, + 0x93, 0x60, 0x0a, 0x0d, 0xa4, 0x0a, 0x00, 0xa4, + 0x0a, 0x01, 0x5b, 0x82, 0x4f, 0x0a, 0x4c, 0x4e, + 0x4b, 0x41, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, + 0x41, 0xd0, 0x0c, 0x0f, 0x08, 0x5f, 0x55, 0x49, + 0x44, 0x0a, 0x01, 0x14, 0x18, 0x5f, 0x53, 0x54, + 0x41, 0x00, 0xa0, 0x0c, 0x50, 0x49, 0x52, 0x56, + 0x50, 0x49, 0x52, 0x41, 0xa4, 0x0a, 0x0b, 0xa1, + 0x04, 0xa4, 0x0a, 0x09, 0x08, 0x5f, 0x50, 0x52, + 0x53, 0x11, 0x09, 0x0a, 0x06, 0x23, 0xf8, 0xde, + 0x18, 0x79, 0x00, 0x08, 0x43, 0x42, 0x30, 0x31, + 0x11, 0x09, 0x0a, 0x06, 0x23, 0x00, 0x00, 0x18, + 0x79, 0x00, 0x8b, 0x43, 0x42, 0x30, 0x31, 0x0a, + 0x01, 0x43, 0x49, 0x52, 0x41, 0x14, 0x2b, 0x5f, + 0x43, 0x52, 0x53, 0x00, 0x7b, 0x50, 0x49, 0x52, + 0x41, 0x0a, 0x8f, 0x60, 0xa0, 0x0e, 0x50, 0x49, + 0x52, 0x56, 0x60, 0x79, 0x0a, 0x01, 0x60, 0x43, + 0x49, 0x52, 0x41, 0xa1, 0x08, 0x70, 0x0a, 0x00, + 0x43, 0x49, 0x52, 0x41, 0xa4, 0x43, 0x42, 0x30, + 0x31, 0x14, 0x0d, 0x5f, 0x44, 0x49, 0x53, 0x00, + 0x70, 0x0a, 0x80, 0x50, 0x49, 0x52, 0x41, 0x14, + 0x1b, 0x5f, 0x53, 0x52, 0x53, 0x01, 0x8b, 0x68, + 0x0a, 0x01, 0x53, 0x49, 0x52, 0x41, 0x82, 0x53, + 0x49, 0x52, 0x41, 0x60, 0x70, 0x76, 0x60, 0x50, + 0x49, 0x52, 0x41, 0x5b, 0x82, 0x4f, 0x0a, 0x4c, + 0x4e, 0x4b, 0x42, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0c, 0x41, 0xd0, 0x0c, 0x0f, 0x08, 0x5f, 0x55, + 0x49, 0x44, 0x0a, 0x02, 0x14, 0x18, 0x5f, 0x53, + 0x54, 0x41, 0x00, 0xa0, 0x0c, 0x50, 0x49, 0x52, + 0x56, 0x50, 0x49, 0x52, 0x42, 0xa4, 0x0a, 0x0b, + 0xa1, 0x04, 0xa4, 0x0a, 0x09, 0x08, 0x5f, 0x50, + 0x52, 0x53, 0x11, 0x09, 0x0a, 0x06, 0x23, 0xf8, + 0xde, 0x18, 0x79, 0x00, 0x08, 0x43, 0x42, 0x30, + 0x32, 0x11, 0x09, 0x0a, 0x06, 0x23, 0x00, 0x00, + 0x18, 0x79, 0x00, 0x8b, 0x43, 0x42, 0x30, 0x32, + 0x0a, 0x01, 0x43, 0x49, 0x52, 0x42, 0x14, 0x2b, + 0x5f, 0x43, 0x52, 0x53, 0x00, 0x7b, 0x50, 0x49, + 0x52, 0x42, 0x0a, 0x8f, 0x60, 0xa0, 0x0e, 0x50, + 0x49, 0x52, 0x56, 0x60, 0x79, 0x0a, 0x01, 0x60, + 0x43, 0x49, 0x52, 0x42, 0xa1, 0x08, 0x70, 0x0a, + 0x00, 0x43, 0x49, 0x52, 0x42, 0xa4, 0x43, 0x42, + 0x30, 0x32, 0x14, 0x0d, 0x5f, 0x44, 0x49, 0x53, + 0x00, 0x70, 0x0a, 0x80, 0x50, 0x49, 0x52, 0x42, + 0x14, 0x1b, 0x5f, 0x53, 0x52, 0x53, 0x01, 0x8b, + 0x68, 0x0a, 0x01, 0x53, 0x49, 0x52, 0x42, 0x82, + 0x53, 0x49, 0x52, 0x42, 0x60, 0x70, 0x76, 0x60, + 0x50, 0x49, 0x52, 0x42, 0x5b, 0x82, 0x4f, 0x0a, + 0x4c, 0x4e, 0x4b, 0x43, 0x08, 0x5f, 0x48, 0x49, + 0x44, 0x0c, 0x41, 0xd0, 0x0c, 0x0f, 0x08, 0x5f, + 0x55, 0x49, 0x44, 0x0a, 0x03, 0x14, 0x18, 0x5f, + 0x53, 0x54, 0x41, 0x00, 0xa0, 0x0c, 0x50, 0x49, + 0x52, 0x56, 0x50, 0x49, 0x52, 0x43, 0xa4, 0x0a, + 0x0b, 0xa1, 0x04, 0xa4, 0x0a, 0x09, 0x08, 0x5f, + 0x50, 0x52, 0x53, 0x11, 0x09, 0x0a, 0x06, 0x23, + 0xf8, 0xde, 0x18, 0x79, 0x00, 0x08, 0x43, 0x42, + 0x30, 0x33, 0x11, 0x09, 0x0a, 0x06, 0x23, 0x00, + 0x00, 0x18, 0x79, 0x00, 0x8b, 0x43, 0x42, 0x30, + 0x33, 0x0a, 0x01, 0x43, 0x49, 0x52, 0x43, 0x14, + 0x2b, 0x5f, 0x43, 0x52, 0x53, 0x00, 0x7b, 0x50, + 0x49, 0x52, 0x43, 0x0a, 0x8f, 0x60, 0xa0, 0x0e, + 0x50, 0x49, 0x52, 0x56, 0x60, 0x79, 0x0a, 0x01, + 0x60, 0x43, 0x49, 0x52, 0x43, 0xa1, 0x08, 0x70, + 0x0a, 0x00, 0x43, 0x49, 0x52, 0x43, 0xa4, 0x43, + 0x42, 0x30, 0x33, 0x14, 0x0d, 0x5f, 0x44, 0x49, + 0x53, 0x00, 0x70, 0x0a, 0x80, 0x50, 0x49, 0x52, + 0x43, 0x14, 0x1b, 0x5f, 0x53, 0x52, 0x53, 0x01, + 0x8b, 0x68, 0x0a, 0x01, 0x53, 0x49, 0x52, 0x43, + 0x82, 0x53, 0x49, 0x52, 0x43, 0x60, 0x70, 0x76, + 0x60, 0x50, 0x49, 0x52, 0x43, 0x5b, 0x82, 0x4f, + 0x0a, 0x4c, 0x4e, 0x4b, 0x44, 0x08, 0x5f, 0x48, + 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0c, 0x0f, 0x08, + 0x5f, 0x55, 0x49, 0x44, 0x0a, 0x04, 0x14, 0x18, + 0x5f, 0x53, 0x54, 0x41, 0x00, 0xa0, 0x0c, 0x50, + 0x49, 0x52, 0x56, 0x50, 0x49, 0x52, 0x44, 0xa4, + 0x0a, 0x0b, 0xa1, 0x04, 0xa4, 0x0a, 0x09, 0x08, + 0x5f, 0x50, 0x52, 0x53, 0x11, 0x09, 0x0a, 0x06, + 0x23, 0xf8, 0xde, 0x18, 0x79, 0x00, 0x08, 0x43, + 0x42, 0x30, 0x34, 0x11, 0x09, 0x0a, 0x06, 0x23, + 0x00, 0x00, 0x18, 0x79, 0x00, 0x8b, 0x43, 0x42, + 0x30, 0x34, 0x0a, 0x01, 0x43, 0x49, 0x52, 0x44, + 0x14, 0x2b, 0x5f, 0x43, 0x52, 0x53, 0x00, 0x7b, + 0x50, 0x49, 0x52, 0x44, 0x0a, 0x8f, 0x60, 0xa0, + 0x0e, 0x50, 0x49, 0x52, 0x56, 0x60, 0x79, 0x0a, + 0x01, 0x60, 0x43, 0x49, 0x52, 0x44, 0xa1, 0x08, + 0x70, 0x0a, 0x00, 0x43, 0x49, 0x52, 0x44, 0xa4, + 0x43, 0x42, 0x30, 0x34, 0x14, 0x0d, 0x5f, 0x44, + 0x49, 0x53, 0x00, 0x70, 0x0a, 0x80, 0x50, 0x49, + 0x52, 0x44, 0x14, 0x1b, 0x5f, 0x53, 0x52, 0x53, + 0x01, 0x8b, 0x68, 0x0a, 0x01, 0x53, 0x49, 0x52, + 0x44, 0x82, 0x53, 0x49, 0x52, 0x44, 0x60, 0x70, + 0x76, 0x60, 0x50, 0x49, 0x52, 0x44, 0x5b, 0x82, + 0x4f, 0x0a, 0x4c, 0x4e, 0x4b, 0x45, 0x08, 0x5f, + 0x48, 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0c, 0x0f, + 0x08, 0x5f, 0x55, 0x49, 0x44, 0x0a, 0x05, 0x14, + 0x18, 0x5f, 0x53, 0x54, 0x41, 0x00, 0xa0, 0x0c, + 0x50, 0x49, 0x52, 0x56, 0x50, 0x49, 0x52, 0x45, + 0xa4, 0x0a, 0x0b, 0xa1, 0x04, 0xa4, 0x0a, 0x09, + 0x08, 0x5f, 0x50, 0x52, 0x53, 0x11, 0x09, 0x0a, + 0x06, 0x23, 0xf8, 0xde, 0x18, 0x79, 0x00, 0x08, + 0x43, 0x42, 0x30, 0x35, 0x11, 0x09, 0x0a, 0x06, + 0x23, 0x00, 0x00, 0x18, 0x79, 0x00, 0x8b, 0x43, + 0x42, 0x30, 0x35, 0x0a, 0x01, 0x43, 0x49, 0x52, + 0x45, 0x14, 0x2b, 0x5f, 0x43, 0x52, 0x53, 0x00, + 0x7b, 0x50, 0x49, 0x52, 0x45, 0x0a, 0x8f, 0x60, + 0xa0, 0x0e, 0x50, 0x49, 0x52, 0x56, 0x60, 0x79, + 0x0a, 0x01, 0x60, 0x43, 0x49, 0x52, 0x45, 0xa1, + 0x08, 0x70, 0x0a, 0x00, 0x43, 0x49, 0x52, 0x45, + 0xa4, 0x43, 0x42, 0x30, 0x35, 0x14, 0x0d, 0x5f, + 0x44, 0x49, 0x53, 0x00, 0x70, 0x0a, 0x80, 0x50, + 0x49, 0x52, 0x45, 0x14, 0x1b, 0x5f, 0x53, 0x52, + 0x53, 0x01, 0x8b, 0x68, 0x0a, 0x01, 0x53, 0x49, + 0x52, 0x45, 0x82, 0x53, 0x49, 0x52, 0x45, 0x60, + 0x70, 0x76, 0x60, 0x50, 0x49, 0x52, 0x45, 0x5b, + 0x82, 0x4f, 0x0a, 0x4c, 0x4e, 0x4b, 0x46, 0x08, + 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0c, + 0x0f, 0x08, 0x5f, 0x55, 0x49, 0x44, 0x0a, 0x06, + 0x14, 0x18, 0x5f, 0x53, 0x54, 0x41, 0x00, 0xa0, + 0x0c, 0x50, 0x49, 0x52, 0x56, 0x50, 0x49, 0x52, + 0x46, 0xa4, 0x0a, 0x0b, 0xa1, 0x04, 0xa4, 0x0a, + 0x09, 0x08, 0x5f, 0x50, 0x52, 0x53, 0x11, 0x09, + 0x0a, 0x06, 0x23, 0xf8, 0xde, 0x18, 0x79, 0x00, + 0x08, 0x43, 0x42, 0x30, 0x36, 0x11, 0x09, 0x0a, + 0x06, 0x23, 0x00, 0x00, 0x18, 0x79, 0x00, 0x8b, + 0x43, 0x42, 0x30, 0x36, 0x0a, 0x01, 0x43, 0x49, + 0x52, 0x46, 0x14, 0x2b, 0x5f, 0x43, 0x52, 0x53, + 0x00, 0x7b, 0x50, 0x49, 0x52, 0x46, 0x0a, 0x8f, + 0x60, 0xa0, 0x0e, 0x50, 0x49, 0x52, 0x56, 0x60, + 0x79, 0x0a, 0x01, 0x60, 0x43, 0x49, 0x52, 0x46, + 0xa1, 0x08, 0x70, 0x0a, 0x00, 0x43, 0x49, 0x52, + 0x46, 0xa4, 0x43, 0x42, 0x30, 0x36, 0x14, 0x0d, + 0x5f, 0x44, 0x49, 0x53, 0x00, 0x70, 0x0a, 0x80, + 0x50, 0x49, 0x52, 0x46, 0x14, 0x1b, 0x5f, 0x53, + 0x52, 0x53, 0x01, 0x8b, 0x68, 0x0a, 0x01, 0x53, + 0x49, 0x52, 0x46, 0x82, 0x53, 0x49, 0x52, 0x46, + 0x60, 0x70, 0x76, 0x60, 0x50, 0x49, 0x52, 0x46, + 0x5b, 0x82, 0x4f, 0x0a, 0x4c, 0x4e, 0x4b, 0x47, + 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, 0xd0, + 0x0c, 0x0f, 0x08, 0x5f, 0x55, 0x49, 0x44, 0x0a, + 0x07, 0x14, 0x18, 0x5f, 0x53, 0x54, 0x41, 0x00, + 0xa0, 0x0c, 0x50, 0x49, 0x52, 0x56, 0x50, 0x49, + 0x52, 0x47, 0xa4, 0x0a, 0x0b, 0xa1, 0x04, 0xa4, + 0x0a, 0x09, 0x08, 0x5f, 0x50, 0x52, 0x53, 0x11, + 0x09, 0x0a, 0x06, 0x23, 0xf8, 0xde, 0x18, 0x79, + 0x00, 0x08, 0x43, 0x42, 0x30, 0x37, 0x11, 0x09, + 0x0a, 0x06, 0x23, 0x00, 0x00, 0x18, 0x79, 0x00, + 0x8b, 0x43, 0x42, 0x30, 0x37, 0x0a, 0x01, 0x43, + 0x49, 0x52, 0x47, 0x14, 0x2b, 0x5f, 0x43, 0x52, + 0x53, 0x00, 0x7b, 0x50, 0x49, 0x52, 0x47, 0x0a, + 0x8f, 0x60, 0xa0, 0x0e, 0x50, 0x49, 0x52, 0x56, + 0x60, 0x79, 0x0a, 0x01, 0x60, 0x43, 0x49, 0x52, + 0x47, 0xa1, 0x08, 0x70, 0x0a, 0x00, 0x43, 0x49, + 0x52, 0x47, 0xa4, 0x43, 0x42, 0x30, 0x37, 0x14, + 0x0d, 0x5f, 0x44, 0x49, 0x53, 0x00, 0x70, 0x0a, + 0x80, 0x50, 0x49, 0x52, 0x47, 0x14, 0x1b, 0x5f, + 0x53, 0x52, 0x53, 0x01, 0x8b, 0x68, 0x0a, 0x01, + 0x53, 0x49, 0x52, 0x47, 0x82, 0x53, 0x49, 0x52, + 0x47, 0x60, 0x70, 0x76, 0x60, 0x50, 0x49, 0x52, + 0x47, 0x5b, 0x82, 0x4f, 0x0a, 0x4c, 0x4e, 0x4b, + 0x48, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, + 0xd0, 0x0c, 0x0f, 0x08, 0x5f, 0x55, 0x49, 0x44, + 0x0a, 0x08, 0x14, 0x18, 0x5f, 0x53, 0x54, 0x41, + 0x00, 0xa0, 0x0c, 0x50, 0x49, 0x52, 0x56, 0x50, + 0x49, 0x52, 0x48, 0xa4, 0x0a, 0x0b, 0xa1, 0x04, + 0xa4, 0x0a, 0x09, 0x08, 0x5f, 0x50, 0x52, 0x53, + 0x11, 0x09, 0x0a, 0x06, 0x23, 0xf8, 0xde, 0x18, + 0x79, 0x00, 0x08, 0x43, 0x42, 0x30, 0x38, 0x11, + 0x09, 0x0a, 0x06, 0x23, 0x00, 0x00, 0x18, 0x79, + 0x00, 0x8b, 0x43, 0x42, 0x30, 0x38, 0x0a, 0x01, + 0x43, 0x49, 0x52, 0x48, 0x14, 0x2b, 0x5f, 0x43, + 0x52, 0x53, 0x00, 0x7b, 0x50, 0x49, 0x52, 0x48, + 0x0a, 0x8f, 0x60, 0xa0, 0x0e, 0x50, 0x49, 0x52, + 0x56, 0x60, 0x79, 0x0a, 0x01, 0x60, 0x43, 0x49, + 0x52, 0x48, 0xa1, 0x08, 0x70, 0x0a, 0x00, 0x43, + 0x49, 0x52, 0x48, 0xa4, 0x43, 0x42, 0x30, 0x38, + 0x14, 0x0d, 0x5f, 0x44, 0x49, 0x53, 0x00, 0x70, + 0x0a, 0x80, 0x50, 0x49, 0x52, 0x48, 0x14, 0x1b, + 0x5f, 0x53, 0x52, 0x53, 0x01, 0x8b, 0x68, 0x0a, + 0x01, 0x53, 0x49, 0x52, 0x48, 0x82, 0x53, 0x49, + 0x52, 0x48, 0x60, 0x70, 0x76, 0x60, 0x50, 0x49, + 0x52, 0x48, 0x5b, 0x82, 0x48, 0x07, 0x53, 0x49, + 0x4f, 0x5f, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, + 0x41, 0xd0, 0x0c, 0x02, 0x08, 0x5f, 0x43, 0x52, + 0x53, 0x11, 0x42, 0x06, 0x0a, 0x5e, 0x47, 0x01, + 0x60, 0x00, 0x60, 0x00, 0x01, 0x01, 0x47, 0x01, + 0x64, 0x00, 0x64, 0x00, 0x01, 0x01, 0x47, 0x01, + 0x20, 0x02, 0x20, 0x02, 0x01, 0x04, 0x47, 0x01, + 0x24, 0x02, 0x24, 0x02, 0x01, 0x04, 0x86, 0x09, + 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, + 0x00, 0x10, 0x47, 0x01, 0xd0, 0x04, 0xd0, 0x04, + 0x01, 0x02, 0x47, 0x01, 0x61, 0x00, 0x61, 0x00, + 0x01, 0x01, 0x47, 0x01, 0x00, 0x04, 0x00, 0x04, + 0x01, 0x08, 0x47, 0x01, 0xb2, 0x00, 0xb2, 0x00, + 0x01, 0x01, 0x47, 0x01, 0x84, 0x00, 0x84, 0x00, + 0x01, 0x01, 0x47, 0x01, 0x72, 0x00, 0x72, 0x00, + 0x01, 0x06, 0x79, 0x00, 0x5b, 0x82, 0x2c, 0x43, + 0x4f, 0x4d, 0x31, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0c, 0x41, 0xd0, 0x05, 0x01, 0x08, 0x5f, 0x55, + 0x49, 0x44, 0x0a, 0x01, 0x08, 0x5f, 0x43, 0x52, + 0x53, 0x11, 0x10, 0x0a, 0x0d, 0x47, 0x01, 0xf8, + 0x03, 0xf8, 0x03, 0x01, 0x08, 0x22, 0x10, 0x00, + 0x79, 0x00, 0x5b, 0x82, 0x2c, 0x43, 0x4f, 0x4d, + 0x32, 0x08, 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, + 0xd0, 0x05, 0x01, 0x08, 0x5f, 0x55, 0x49, 0x44, + 0x0a, 0x02, 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, + 0x10, 0x0a, 0x0d, 0x47, 0x01, 0xf8, 0x02, 0xf8, + 0x02, 0x01, 0x08, 0x22, 0x08, 0x00, 0x79, 0x00, + 0x5b, 0x82, 0x25, 0x52, 0x54, 0x43, 0x5f, 0x08, + 0x5f, 0x48, 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x0b, + 0x00, 0x08, 0x5f, 0x43, 0x52, 0x53, 0x11, 0x10, + 0x0a, 0x0d, 0x47, 0x01, 0x70, 0x00, 0x70, 0x00, + 0x01, 0x02, 0x22, 0x00, 0x01, 0x79, 0x00, 0x5b, + 0x82, 0x2b, 0x50, 0x49, 0x43, 0x5f, 0x08, 0x5f, + 0x48, 0x49, 0x44, 0x0b, 0x41, 0xd0, 0x08, 0x5f, + 0x43, 0x52, 0x53, 0x11, 0x18, 0x0a, 0x15, 0x47, + 0x01, 0x20, 0x00, 0x20, 0x00, 0x01, 0x02, 0x47, + 0x01, 0xa0, 0x00, 0xa0, 0x00, 0x01, 0x02, 0x22, + 0x04, 0x00, 0x79, 0x00, 0x5b, 0x82, 0x25, 0x54, + 0x49, 0x4d, 0x52, 0x08, 0x5f, 0x48, 0x49, 0x44, + 0x0c, 0x41, 0xd0, 0x01, 0x00, 0x08, 0x5f, 0x43, + 0x52, 0x53, 0x11, 0x10, 0x0a, 0x0d, 0x47, 0x01, + 0x40, 0x00, 0x40, 0x00, 0x01, 0x04, 0x22, 0x01, + 0x00, 0x79, 0x00, 0x10, 0x39, 0x2e, 0x5f, 0x53, + 0x42, 0x5f, 0x50, 0x43, 0x30, 0x30, 0x5b, 0x82, + 0x2d, 0x48, 0x50, 0x45, 0x54, 0x08, 0x5f, 0x48, + 0x49, 0x44, 0x0c, 0x41, 0xd0, 0x01, 0x03, 0x08, + 0x5f, 0x55, 0x49, 0x44, 0x0a, 0x00, 0x08, 0x5f, + 0x43, 0x52, 0x53, 0x11, 0x11, 0x0a, 0x0e, 0x86, + 0x09, 0x00, 0x01, 0x00, 0x00, 0xd0, 0xfe, 0x00, + 0x04, 0x00, 0x00, 0x79 + }; + + dsdt = (void *) (((uintptr_t) tb) + DSDT_OFFSET); + /* copy DSDT template to guest memory */ + memcpy(dsdt, dsdt_tmpl, 2604); + + pci_write_dsdt(); + + /* write checksum */ + acpitbl_write8(dsdt, 0x9, acpitbl_checksum(dsdt, 2604)); +} + +int +acpi_build(int ncpu) +{ + int err; + + acpi_ncpu = ncpu; + tb = paddr_guest2host(XHYVE_ACPI_BASE, XHYVE_ACPI_SIZE); + if (tb == NULL) { + return (EFAULT); + } + + err = xh_vm_get_hpet_capabilities(&hpet_capabilities); + if (err != 0) { + return (err); + } + + acpitbl_build_rdsp(); + acpitbl_build_rsdt(); + acpitbl_build_xsdt(); + acpitbl_build_madt(); + acpitbl_build_fadt(); + acpitbl_build_hpet(); + acpitbl_build_mcfg(); + acpitbl_build_facs(); + acpitbl_build_dsdt(); + + return 0; +} diff --git a/src/dsdt.asl b/src/dsdt.asl new file mode 100644 index 0000000..387951d --- /dev/null +++ b/src/dsdt.asl @@ -0,0 +1,841 @@ +/* + * bhyve DSDT template + */ +DefinitionBlock ("bhyve_dsdt.aml", "DSDT", 2,"BHYVE ", "BVDSDT ", 0x00000001) +{ + Name (_S5, Package () + { + 0x05, + Zero, + }) + Name (PICM, 0x00) + Method (_PIC, 1, NotSerialized) + { + Store (Arg0, PICM) + } + + Scope (_SB) + { + Device (PC00) + { + Name (_HID, EisaId ("PNP0A03")) + Name (_ADR, Zero) + Method (_BBN, 0, NotSerialized) + { + Return (0x00000000) + } + Name (_CRS, ResourceTemplate () + { + WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, + 0x0000, // Granularity + 0x0000, // Range Minimum + 0x0000, // Range Maximum + 0x0000, // Translation Offset + 0x0001, // Length + ,, ) + IO (Decode16, + 0x0CF8, // Range Minimum + 0x0CF8, // Range Maximum + 0x01, // Alignment + 0x08, // Length + ) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Granularity + 0x0000, // Range Minimum + 0x0CF7, // Range Maximum + 0x0000, // Translation Offset + 0x0CF8, // Length + ,, , TypeStatic) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Granularity + 0x0D00, // Range Minimum + 0x1FFF, // Range Maximum + 0x0000, // Translation Offset + 0x1300, // Length + ,, , TypeStatic) + WordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, + 0x0000, // Granularity + 0x0000, // Range Minimum + 0x0000, // Range Maximum + 0x0000, // Translation Offset + 0x0020, // Length + ,, , TypeStatic) + DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x00000000, // Granularity + 0x00000000, // Range Minimum + + 0x00000000, // Range Maximum + + 0x00000000, // Translation Offset + 0x00000000, // Length + + ,, , AddressRangeMemory, TypeStatic) + QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, + 0x0000000000000000, // Granularity + 0x0000000000000000, // Range Minimum + + 0x0000000000000000, // Range Maximum + + 0x0000000000000000, // Translation Offset + 0x0000000000000000, // Length + + ,, , AddressRangeMemory, TypeStatic) + }) + Name (PPRT, Package () + { + Package () + { + 0x1FFFF, + 0x00, + \_SB.PC00.ISA.LNKA,, + 0x00 + }, + Package () + { + 0x2FFFF, + 0x00, + \_SB.PC00.ISA.LNKB,, + 0x00 + }, + Package () + { + 0x3FFFF, + 0x00, + \_SB.PC00.ISA.LNKC,, + 0x00 + }, + Package () + { + 0x4FFFF, + 0x00, + \_SB.PC00.ISA.LNKD,, + 0x00 + }, + Package () + { + 0x5FFFF, + 0x00, + \_SB.PC00.ISA.LNKE,, + 0x00 + }, + Package () + { + 0x6FFFF, + 0x00, + \_SB.PC00.ISA.LNKF,, + 0x00 + }, + Package () + { + 0x7FFFF, + 0x00, + \_SB.PC00.ISA.LNKG,, + 0x00 + }, + Package () + { + 0x8FFFF, + 0x00, + \_SB.PC00.ISA.LNKH,, + 0x00 + }, + }) + Name (APRT, Package () + { + Package () + { + 0x1FFFF, + 0x00, + Zero, + 0x10 + }, + Package () + { + 0x2FFFF, + 0x00, + Zero, + 0x11 + }, + Package () + { + 0x3FFFF, + 0x00, + Zero, + 0x12 + }, + Package () + { + 0x4FFFF, + 0x00, + Zero, + 0x13 + }, + Package () + { + 0x5FFFF, + 0x00, + Zero, + 0x14 + }, + Package () + { + 0x6FFFF, + 0x00, + Zero, + 0x15 + }, + Package () + { + 0x7FFFF, + 0x00, + Zero, + 0x16 + }, + Package () + { + 0x8FFFF, + 0x00, + Zero, + 0x17 + }, + }) + Method (_PRT, 0, NotSerialized) + { + If (PICM) + { + Return (APRT) + } + Else + { + Return (PPRT) + } + } + + Device (ISA) + { + Name (_ADR, 0x001F0000) + OperationRegion (LPCR, PCI_Config, 0x00, 0x100) + Field (LPCR, AnyAcc, NoLock, Preserve) + { + Offset (0x60), + PIRA, 8, + PIRB, 8, + PIRC, 8, + PIRD, 8, + Offset (0x68), + PIRE, 8, + PIRF, 8, + PIRG, 8, + PIRH, 8 + } + + + Method (PIRV, 1, NotSerialized) + { + If (And (Arg0, 0x80)) + { + Return (0x00) + } + And (Arg0, 0x0F, Local0) + If (LLess (Local0, 0x03)) + { + Return (0x00) + } + If (LEqual (Local0, 0x08)) + { + Return (0x00) + } + If (LEqual (Local0, 0x0D)) + { + Return (0x00) + } + Return (0x01) + } + + Device (LNKA) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x01) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRA)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB01, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB01, 0x01, CIRA) + Method (_CRS, 0, NotSerialized) + { + And (PIRA, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRA) + } + Else + { + Store (0x00, CIRA) + } + Return (CB01) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRA) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRA) + FindSetRightBit (SIRA, Local0) + Store (Decrement (Local0), PIRA) + } + } + + Device (LNKB) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x02) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRB)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB02, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB02, 0x01, CIRB) + Method (_CRS, 0, NotSerialized) + { + And (PIRB, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRB) + } + Else + { + Store (0x00, CIRB) + } + Return (CB02) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRB) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRB) + FindSetRightBit (SIRB, Local0) + Store (Decrement (Local0), PIRB) + } + } + + Device (LNKC) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x03) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRC)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB03, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB03, 0x01, CIRC) + Method (_CRS, 0, NotSerialized) + { + And (PIRC, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRC) + } + Else + { + Store (0x00, CIRC) + } + Return (CB03) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRC) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRC) + FindSetRightBit (SIRC, Local0) + Store (Decrement (Local0), PIRC) + } + } + + Device (LNKD) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x04) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRD)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB04, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB04, 0x01, CIRD) + Method (_CRS, 0, NotSerialized) + { + And (PIRD, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRD) + } + Else + { + Store (0x00, CIRD) + } + Return (CB04) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRD) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRD) + FindSetRightBit (SIRD, Local0) + Store (Decrement (Local0), PIRD) + } + } + + Device (LNKE) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x05) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRE)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB05, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB05, 0x01, CIRE) + Method (_CRS, 0, NotSerialized) + { + And (PIRE, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRE) + } + Else + { + Store (0x00, CIRE) + } + Return (CB05) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRE) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRE) + FindSetRightBit (SIRE, Local0) + Store (Decrement (Local0), PIRE) + } + } + + Device (LNKF) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x06) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRF)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB06, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB06, 0x01, CIRF) + Method (_CRS, 0, NotSerialized) + { + And (PIRF, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRF) + } + Else + { + Store (0x00, CIRF) + } + Return (CB06) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRF) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRF) + FindSetRightBit (SIRF, Local0) + Store (Decrement (Local0), PIRF) + } + } + + Device (LNKG) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x07) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRG)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB07, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB07, 0x01, CIRG) + Method (_CRS, 0, NotSerialized) + { + And (PIRG, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRG) + } + Else + { + Store (0x00, CIRG) + } + Return (CB07) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRG) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRG) + FindSetRightBit (SIRG, Local0) + Store (Decrement (Local0), PIRG) + } + } + + Device (LNKH) + { + Name (_HID, EisaId ("PNP0C0F")) + Name (_UID, 0x08) + Method (_STA, 0, NotSerialized) + { + If (PIRV (PIRH)) + { + Return (0x0B) + } + Else + { + Return (0x09) + } + } + Name (_PRS, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {3,4,5,6,7,9,10,11,12,14,15} + }) + Name (CB08, ResourceTemplate () + { + IRQ (Level, ActiveLow, Shared, ) + {} + }) + CreateWordField (CB08, 0x01, CIRH) + Method (_CRS, 0, NotSerialized) + { + And (PIRH, 0x8F, Local0) + If (PIRV (Local0)) + { + ShiftLeft (0x01, Local0, CIRH) + } + Else + { + Store (0x00, CIRH) + } + Return (CB08) + } + Method (_DIS, 0, NotSerialized) + { + Store (0x80, PIRH) + } + Method (_SRS, 1, NotSerialized) + { + CreateWordField (Arg0, 0x01, SIRH) + FindSetRightBit (SIRH, Local0) + Store (Decrement (Local0), PIRH) + } + } + + Device (SIO) + { + Name (_HID, EisaId ("PNP0C02")) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0060, // Range Minimum + 0x0060, // Range Maximum + 0x01, // Alignment + 0x01, // Length + ) + IO (Decode16, + 0x0064, // Range Minimum + 0x0064, // Range Maximum + 0x01, // Alignment + 0x01, // Length + ) + IO (Decode16, + 0x0220, // Range Minimum + 0x0220, // Range Maximum + 0x01, // Alignment + 0x04, // Length + ) + IO (Decode16, + 0x0224, // Range Minimum + 0x0224, // Range Maximum + 0x01, // Alignment + 0x04, // Length + ) + Memory32Fixed (ReadWrite, + 0xE0000000, // Address Base + 0x10000000, // Address Length + ) + IO (Decode16, + 0x04D0, // Range Minimum + 0x04D0, // Range Maximum + 0x01, // Alignment + 0x02, // Length + ) + IO (Decode16, + 0x0061, // Range Minimum + 0x0061, // Range Maximum + 0x01, // Alignment + 0x01, // Length + ) + IO (Decode16, + 0x0400, // Range Minimum + 0x0400, // Range Maximum + 0x01, // Alignment + 0x08, // Length + ) + IO (Decode16, + 0x00B2, // Range Minimum + 0x00B2, // Range Maximum + 0x01, // Alignment + 0x01, // Length + ) + IO (Decode16, + 0x0084, // Range Minimum + 0x0084, // Range Maximum + 0x01, // Alignment + 0x01, // Length + ) + IO (Decode16, + 0x0072, // Range Minimum + 0x0072, // Range Maximum + 0x01, // Alignment + 0x06, // Length + ) + }) + } + + Device (COM1) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, 1) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x03F8, // Range Minimum + 0x03F8, // Range Maximum + 0x01, // Alignment + 0x08, // Length + ) + IRQNoFlags () + {4} + }) + } + + Device (COM2) + { + Name (_HID, EisaId ("PNP0501")) + Name (_UID, 2) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x02F8, // Range Minimum + 0x02F8, // Range Maximum + 0x01, // Alignment + 0x08, // Length + ) + IRQNoFlags () + {3} + }) + } + + Device (RTC) + { + Name (_HID, EisaId ("PNP0B00")) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0070, // Range Minimum + 0x0070, // Range Maximum + 0x01, // Alignment + 0x02, // Length + ) + IRQNoFlags () + {8} + }) + } + + Device (PIC) + { + Name (_HID, EisaId ("PNP0000")) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0020, // Range Minimum + 0x0020, // Range Maximum + 0x01, // Alignment + 0x02, // Length + ) + IO (Decode16, + 0x00A0, // Range Minimum + 0x00A0, // Range Maximum + 0x01, // Alignment + 0x02, // Length + ) + IRQNoFlags () + {2} + }) + } + + Device (TIMR) + { + Name (_HID, EisaId ("PNP0100")) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0040, // Range Minimum + 0x0040, // Range Maximum + 0x01, // Alignment + 0x04, // Length + ) + IRQNoFlags () + {0} + }) + } + } + } + } + + Scope (_SB.PC00) + { + Device (HPET) + { + Name (_HID, EISAID("PNP0103")) + Name (_UID, 0) + Name (_CRS, ResourceTemplate () + { + Memory32Fixed (ReadWrite, + 0xFED00000, // Address Base + 0x00000400, // Address Length + ) + }) + } + } +} diff --git a/src/firmware/fbsd.c b/src/firmware/fbsd.c new file mode 100644 index 0000000..6a2d2bc --- /dev/null +++ b/src/firmware/fbsd.c @@ -0,0 +1,1006 @@ +/*- + * Copyright (c) 2011 NetApp, Inc. + * Copyright (c) 2015 xhyve developers + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/*- + * Copyright (c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define I386_TSS_SIZE 104 + +#define DESC_PRESENT 0x00000080 +#define DESC_DEF32 0x00004000 +#define DESC_GRAN 0x00008000 +#define DESC_UNUSABLE 0x00010000 + +#define GUEST_CODE_SEL 1 +#define GUEST_DATA_SEL 2 +#define GUEST_TSS_SEL 3 +#define GUEST_GDTR_LIMIT64 (3 * 8 - 1) + +#define BSP 0 +#define NDISKS 32 + +static struct { + char *userboot; + char *bootvolume; + char *kernelenv; + char *cons; +} config; + +static char *host_base; +static struct termios term, oldterm; +static int disk_fd[NDISKS]; +static int ndisks; +static int consin_fd, consout_fd; +static jmp_buf exec_done; + +static uint64_t vcpu_gdt_base, vcpu_cr3, vcpu_rsp, vcpu_rip; + +typedef void (*func_t)(struct loader_callbacks *, void *, int, int); + +static void cb_exit(void); + +static struct segment_descriptor i386_gdt[] = { + { .sd_lolimit = 0, .sd_type = 0, /* NULL */ + .sd_p = 0, .sd_hilimit = 0, .sd_def32 = 0, .sd_gran = 0}, + + { .sd_lolimit = 0xffff, .sd_type = SDT_MEMER, /* CODE */ + .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, + + { .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW, /* DATA */ + .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, + + { .sd_lolimit = I386_TSS_SIZE - 1, /* TSS */ + .sd_type = SDT_SYS386TSS, .sd_p = 1 } +}; + +static int +fbsd_set_regs_i386(uint32_t eip, uint32_t gdt_base, uint32_t esp) +{ + uint64_t cr0, rflags, desc_base; + uint32_t desc_access, desc_limit, tss_base; + uint16_t gsel; + struct segment_descriptor *gdt; + int error; + + cr0 = CR0_PE | CR0_NE; + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CR0, cr0)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CR4, 0)) != 0) + goto done; + + /* + * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest + * mode" entry control. + */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_EFER, 0))) + goto done; + + gdt = xh_vm_map_gpa(gdt_base, 0x1000); + if (gdt == NULL) + return (EFAULT); + memcpy(gdt, i386_gdt, sizeof(i386_gdt)); + desc_base = gdt_base; + desc_limit = sizeof(i386_gdt) - 1; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_GDTR, desc_base, desc_limit, 0); + if (error != 0) + goto done; + + /* Place the TSS one page above the GDT. */ + tss_base = gdt_base + 0x1000; + gdt[3].sd_lobase = tss_base; + + rflags = 0x2; + error = xh_vm_set_register(BSP, VM_REG_GUEST_RFLAGS, rflags); + if (error) + goto done; + + desc_base = 0; + desc_limit = 0xffffffff; + desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_CS, desc_base, desc_limit, + desc_access); + + desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_DS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_ES, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_FS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_GS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_SS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + desc_base = tss_base; + desc_limit = I386_TSS_SIZE - 1; + desc_access = DESC_PRESENT | SDT_SYS386BSY; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_TR, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_LDTR, 0, 0, DESC_UNUSABLE); + if (error) + goto done; + + gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CS, gsel)) != 0) + goto done; + + gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_DS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_ES, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_FS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_GS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_SS, gsel)) != 0) + goto done; + + gsel = GSEL(GUEST_TSS_SEL, SEL_KPL); + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_TR, gsel)) != 0) + goto done; + + /* LDTR is pointing to the null selector */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_LDTR, 0)) != 0) + goto done; + + /* entry point */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_RIP, eip)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_RSP, esp)) != 0) + goto done; + + error = 0; +done: + return (error); +} + +static int +fbsd_set_regs(uint64_t rip, uint64_t cr3, uint64_t gdt_base, uint64_t rsp) +{ + int error; + uint64_t cr0, cr4, efer, rflags, desc_base; + uint32_t desc_access, desc_limit; + uint16_t gsel; + + cr0 = CR0_PE | CR0_PG | CR0_NE; + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CR0, cr0)) != 0) + goto done; + + cr4 = CR4_PAE; + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CR4, cr4)) != 0) + goto done; + + efer = EFER_LME | EFER_LMA; + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_EFER, efer))) + goto done; + + rflags = 0x2; + error = xh_vm_set_register(BSP, VM_REG_GUEST_RFLAGS, rflags); + if (error) + goto done; + + desc_base = 0; + desc_limit = 0; + desc_access = 0x0000209B; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_CS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + desc_access = 0x00000093; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_DS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_ES, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_FS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_GS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_SS, desc_base, desc_limit, + desc_access); + + if (error) + goto done; + + /* + * XXX TR is pointing to null selector even though we set the + * TSS segment to be usable with a base address and limit of 0. + */ + desc_access = 0x0000008b; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_TR, 0, 0, desc_access); + if (error) + goto done; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_LDTR, 0, 0, DESC_UNUSABLE); + if (error) + goto done; + + gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CS, gsel)) != 0) + goto done; + + gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_DS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_ES, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_FS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_GS, gsel)) != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_SS, gsel)) != 0) + goto done; + + /* XXX TR is pointing to the null selector */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_TR, 0)) != 0) + goto done; + + /* LDTR is pointing to the null selector */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_LDTR, 0)) != 0) + goto done; + + /* entry point */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_RIP, rip)) != 0) + goto done; + + /* page table base */ + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_CR3, cr3)) != 0) + goto done; + + desc_base = gdt_base; + desc_limit = GUEST_GDTR_LIMIT64; + error = xh_vm_set_desc(BSP, VM_REG_GUEST_GDTR, desc_base, desc_limit, 0); + if (error != 0) + goto done; + + if ((error = xh_vm_set_register(BSP, VM_REG_GUEST_RSP, rsp)) != 0) + goto done; + + error = 0; +done: + return (error); +} + +/* + * Console i/o callbacks + */ + +static void +cb_putc(UNUSED void *arg, int ch) +{ + char c = (char) ch; + + (void) write(consout_fd, &c, 1); +} + +static int +cb_getc(UNUSED void *arg) +{ + char c; + + if (read(consin_fd, &c, 1) == 1) + return (c); + return (-1); +} + +static int +cb_poll(UNUSED void *arg) +{ + int n; + + if (ioctl(consin_fd, FIONREAD, &n) >= 0) + return (n > 0); + return (0); +} + +/* + * Host filesystem i/o callbacks + */ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +struct cb_file { + int cf_isdir; + size_t cf_size; + struct stat cf_stat; + union { + int fd; + DIR *dir; + } cf_u; +}; +#pragma clang diagnostic pop + +static int +cb_open(UNUSED void *arg, const char *filename, void **hp) +{ + struct stat st; + struct cb_file *cf; + char path[PATH_MAX]; + + if (!host_base) + return (ENOENT); + + strlcpy(path, host_base, PATH_MAX); + if (path[strlen(path) - 1] == '/') + path[strlen(path) - 1] = 0; + strlcat(path, filename, PATH_MAX); + cf = malloc(sizeof(struct cb_file)); + if (stat(path, &cf->cf_stat) < 0) { + free(cf); + return (errno); + } + + cf->cf_size = (size_t) st.st_size; + if (S_ISDIR(cf->cf_stat.st_mode)) { + cf->cf_isdir = 1; + cf->cf_u.dir = opendir(path); + if (!cf->cf_u.dir) + goto out; + *hp = cf; + return (0); + } + if (S_ISREG(cf->cf_stat.st_mode)) { + cf->cf_isdir = 0; + cf->cf_u.fd = open(path, O_RDONLY); + if (cf->cf_u.fd < 0) + goto out; + *hp = cf; + return (0); + } + +out: + free(cf); + return (EINVAL); +} + +// static int +// cb_close(UNUSED void *arg, void *h) +// { +// struct cb_file *cf = h; +// +// if (cf->cf_isdir) +// closedir(cf->cf_u.dir); +// else +// close(cf->cf_u.fd); +// free(cf); +// +// return (0); +// } + +// static int +// cb_isdir(UNUSED void *arg, void *h) +// { +// struct cb_file *cf = h; +// +// return (cf->cf_isdir); +// } + +// static int +// cb_read(UNUSED void *arg, void *h, void *buf, size_t size, size_t *resid) +// { +// struct cb_file *cf = h; +// ssize_t sz; +// +// if (cf->cf_isdir) +// return (EINVAL); +// sz = read(cf->cf_u.fd, buf, size); +// if (sz < 0) +// return (EINVAL); +// *resid = size - ((size_t) sz); +// return (0); +// } + +//static int +//cb_readdir(UNUSED void *arg, void *h, uint32_t *fileno_return, +// uint8_t *type_return, size_t *namelen_return, char *name) +//{ +// struct cb_file *cf = h; +// struct dirent *dp; +// +// if (!cf->cf_isdir) +// return (EINVAL); +// +// dp = readdir(cf->cf_u.dir); +// if (!dp) +// return (ENOENT); +// +// /* +// * Note: d_namlen is in the range 0..255 and therefore less +// * than PATH_MAX so we don't need to test before copying. +// */ +// *fileno_return = dp->d_fileno; +// *type_return = dp->d_type; +// *namelen_return = dp->d_namlen; +// memcpy(name, dp->d_name, dp->d_namlen); +// name[dp->d_namlen] = 0; +// +// return (0); +//} + +// static int +// cb_seek(UNUSED void *arg, void *h, uint64_t offset, int whence) +// { +// struct cb_file *cf = h; +// +// if (cf->cf_isdir) +// return (EINVAL); +// if (lseek(cf->cf_u.fd, ((off_t) offset), whence) < 0) +// return (errno); +// return (0); +// } + +// static int +// cb_stat(UNUSED void *arg, void *h, int *mode, int *uid, int *gid, +// uint64_t *size) +// { +// struct cb_file *cf = h; +// +// *mode = cf->cf_stat.st_mode; +// *uid = (int) cf->cf_stat.st_uid; +// *gid = (int) cf->cf_stat.st_gid; +// *size = (uint64_t) cf->cf_stat.st_size; +// return (0); +// } + +/* + * Disk image i/o callbacks + */ + +static int +cb_diskread(UNUSED void *arg, int unit, uint64_t from, void *to, size_t size, + size_t *resid) +{ + ssize_t n; + + if (unit < 0 || unit >= ndisks ) + return (EIO); + n = pread(disk_fd[unit], to, size, ((off_t) from)); + if (n < 0) + return (errno); + *resid = size - ((size_t) n); + return (0); +} + +#define DIOCGSECTORSIZE _IOR('d', 128, u_int) +#define DIOCGMEDIASIZE _IOR('d', 129, off_t) + +static int +cb_diskioctl(UNUSED void *arg, int unit, u_long cmd, void *data) +{ + struct stat sb; + + if (unit < 0 || unit >= ndisks) + return (EBADF); + + switch (cmd) { + case DIOCGSECTORSIZE: + *(u_int *)data = 512; + break; + case DIOCGMEDIASIZE: + if (fstat(disk_fd[unit], &sb) == 0) + *(off_t *)data = sb.st_size; + else + return (ENOTTY); + break; + default: + abort(); + return (ENOTTY); + } + + return (0); +} + +/* + * Guest virtual machine i/o callbacks + */ +static int +cb_copyin(UNUSED void *arg, const void *from, uint64_t to, size_t size) +{ + char *ptr; + + to &= 0x7fffffff; + + ptr = xh_vm_map_gpa(to, size); + if (ptr == NULL) + return (EFAULT); + + memcpy(ptr, from, size); + return (0); +} + +static int +cb_copyout(UNUSED void *arg, uint64_t from, void *to, size_t size) +{ + char *ptr; + + from &= 0x7fffffff; + + ptr = xh_vm_map_gpa(from, size); + if (ptr == NULL) + return (EFAULT); + + memcpy(to, ptr, size); + return (0); +} + +static void +cb_setreg(UNUSED void *arg, int r, uint64_t v) +{ + int error; + enum vm_reg_name vmreg; + + vmreg = VM_REG_LAST; + + switch (r) { + case 4: + vmreg = VM_REG_GUEST_RSP; + vcpu_rsp = v; + break; + default: + break; + } + + if (vmreg == VM_REG_LAST) { + abort(); + } + + error = xh_vm_set_register(BSP, vmreg, v); + if (error) { + perror("xh_vm_set_register"); + cb_exit(); + } +} + +static void +cb_setmsr(UNUSED void *arg, int r, uint64_t v) +{ + int error; + enum vm_reg_name vmreg; + + vmreg = VM_REG_LAST; + + switch (r) { + case MSR_EFER: + vmreg = VM_REG_GUEST_EFER; + break; + default: + break; + } + + if (vmreg == VM_REG_LAST) { + abort(); + } + + error = xh_vm_set_register(BSP, vmreg, v); + if (error) { + perror("xh_vm_set_msr"); + cb_exit(); + } +} + +static void +cb_setcr(UNUSED void *arg, int r, uint64_t v) +{ + int error; + enum vm_reg_name vmreg; + + vmreg = VM_REG_LAST; + + switch (r) { + case 0: + vmreg = VM_REG_GUEST_CR0; + break; + case 3: + vmreg = VM_REG_GUEST_CR3; + vcpu_cr3 = v; + break; + case 4: + vmreg = VM_REG_GUEST_CR4; + break; + default: + break; + } + + if (vmreg == VM_REG_LAST) { + fprintf(stderr, "test_setcr(%d): not implemented\n", r); + cb_exit(); + } + + error = xh_vm_set_register(BSP, vmreg, v); + if (error) { + perror("vm_set_cr"); + cb_exit(); + } +} + +static void +cb_setgdt(UNUSED void *arg, uint64_t base, size_t size) +{ + int error; + + error = xh_vm_set_desc(BSP, VM_REG_GUEST_GDTR, base, + ((uint32_t) (size - 1)), 0); + + if (error != 0) { + perror("vm_set_desc(gdt)"); + cb_exit(); + } + + vcpu_gdt_base = base; +} + +__attribute__ ((noreturn)) static void +cb_exec(UNUSED void *arg, uint64_t rip) +{ + int error; + + if (vcpu_cr3 == 0) { + error = fbsd_set_regs_i386(((uint32_t) rip), ((uint32_t) vcpu_gdt_base), + ((uint32_t) vcpu_rsp)); + } else { + error = fbsd_set_regs(rip, vcpu_cr3, vcpu_gdt_base, vcpu_rsp); + } + + if (error) { + perror("fbsd_set_regs"); + cb_exit(); + } + + vcpu_rip = rip; + + longjmp(exec_done, 1); +} + +/* + * Misc + */ + +static void +cb_delay(UNUSED void *arg, int usec) +{ + usleep((useconds_t) usec); +} + +__attribute__ ((noreturn)) static void +cb_exit(void) +{ + tcsetattr(consout_fd, TCSAFLUSH, &oldterm); + fprintf(stderr, "fbsd: error\n"); + exit(-1); +} + +static void +cb_getmem(UNUSED void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem) +{ + *ret_lowmem = xh_vm_get_lowmem_size(); + *ret_highmem = xh_vm_get_highmem_size(); +} + +struct env { + const char *str; /* name=value */ + SLIST_ENTRY(env) next; +}; + +static SLIST_HEAD(envhead, env) envhead; + +static void +addenv(const char *str) +{ + struct env *env; + + env = malloc(sizeof(struct env)); + env->str = str; + SLIST_INSERT_HEAD(&envhead, env, next); +} + +static const char * +cb_getenv(UNUSED void *arg, int num) +{ + int i; + struct env *env; + + i = 0; + SLIST_FOREACH(env, &envhead, next) { + if (i == num) + return (env->str); + i++; + } + + return (NULL); +} + +static struct loader_callbacks cb = { + .getc = cb_getc, + .putc = cb_putc, + .poll = cb_poll, + + // .open = cb_open, + // .close = cb_close, + // .isdir = cb_isdir, + // .read = cb_read, + // .readdir = cb_readdir, + // .seek = cb_seek, + // .stat = cb_stat, + + .open = cb_open, + .close = NULL, + .isdir = NULL, + .read = NULL, + .readdir = NULL, + .seek = NULL, + .stat = NULL, + + .diskread = cb_diskread, + .diskioctl = cb_diskioctl, + + .copyin = cb_copyin, + .copyout = cb_copyout, + .setreg = cb_setreg, + .setmsr = cb_setmsr, + .setcr = cb_setcr, + .setgdt = cb_setgdt, + .exec = cb_exec, + + .delay = cb_delay, + .exit = cb_exit, + .getmem = cb_getmem, + + .getenv = cb_getenv, +}; + +static int +altcons_open(char *path) +{ + struct stat sb; + int err; + int fd; + + /* + * Allow stdio to be passed in so that the same string + * can be used for the bhyveload console and bhyve com-port + * parameters + */ + if (!strcmp(path, "stdio")) + return (0); + + err = stat(path, &sb); + if (err == 0) { + if (!S_ISCHR(sb.st_mode)) + err = ENOTSUP; + else { + fd = open(path, O_RDWR | O_NONBLOCK); + if (fd < 0) + err = errno; + else + consin_fd = consout_fd = fd; + } + } + + return (err); +} + +static int +disk_open(char *path) +{ + int err, fd; + + if (ndisks >= NDISKS) + return (ERANGE); + + err = 0; + fd = open(path, O_RDONLY); + + if (fd > 0) { + disk_fd[ndisks] = fd; + ndisks++; + } else + err = errno; + + return (err); +} + +void +fbsd_init(char *userboot_path, char *bootvolume_path, char *kernelenv, + char *cons) +{ + config.userboot = userboot_path; + config.bootvolume = bootvolume_path; + config.kernelenv = kernelenv; + config.cons = cons; +} + +uint64_t +fbsd_load(void) +{ + void *h; + int i; + func_t func; + + host_base = NULL; + consin_fd = STDIN_FILENO; + consout_fd = STDOUT_FILENO; + + if (config.cons) { + altcons_open(config.cons); + } + + if (config.bootvolume) { + disk_open(config.bootvolume); + } else { + fprintf(stderr, "fbsd: no boot volume\n"); + exit(-1); + } + + if (config.kernelenv) { + addenv(config.kernelenv); + } + + //host_base = optarg h + + tcgetattr(consout_fd, &term); + oldterm = term; + cfmakeraw(&term); + term.c_cflag |= CLOCAL; + + tcsetattr(consout_fd, TCSAFLUSH, &term); + + h = dlopen(config.userboot, RTLD_LOCAL); + if (!h) { + fprintf(stderr, "%s\n", dlerror()); + exit(-1); + } + + func = (func_t) dlsym(h, "loader_main"); + if (!func) { + fprintf(stderr, "%s\n", dlerror()); + exit(-1); + } + + addenv("smbios.bios.vendor=BHYVE"); + addenv("boot_serial=1"); + + if (!setjmp(exec_done)) { + func(&cb, NULL, USERBOOT_VERSION_3, ndisks); + } + + for (i = 0; i < ndisks; i++) { + close(disk_fd[i]); + } + + if (config.cons) { + assert(consin_fd == consout_fd); + close(consin_fd); + } + + return vcpu_rip; +} diff --git a/src/pci_emul.c b/src/pci_emul.c index 10a09f9..5b34694 100644 --- a/src/pci_emul.c +++ b/src/pci_emul.c @@ -1217,6 +1217,32 @@ pci_pirq_prt_entry(UNUSED int bus, int slot, int pin, int pirq_pin, * A bhyve virtual machine has a flat PCI hierarchy with a root port * corresponding to each PCI bus. */ +#if ACPITBL_AML +static void +pci_bus_write_dsdt(int bus) +{ + struct businfo *bi; + + /* + * If there are no devices on this 'bus' then just return. + */ + if ((bi = pci_businfo[bus]) == NULL) { + /* + * Bus 0 is special because it decodes the I/O ports used + * for PCI config space access even if there are no devices + * on it. + */ + if (bus != 0) + return; + } + + dsdt_fixup(bus, bi->iobase, bi->iolimit, bi->membase32, bi->memlimit32, + bi->membase64, bi->memlimit64); + + (void) pci_pirq_prt_entry; + (void) pci_apic_prt_entry; +} +#else static void pci_bus_write_dsdt(int bus) { @@ -1365,7 +1391,19 @@ pci_bus_write_dsdt(int bus) done: dsdt_line(" }"); } +#endif +#if ACPITBL_AML +void +pci_write_dsdt(void) +{ + int bus; + + for (bus = 0; bus < MAXBUSES; bus++) { + pci_bus_write_dsdt(bus); + } +} +#else void pci_write_dsdt(void) { @@ -1385,6 +1423,7 @@ pci_write_dsdt(void) dsdt_line("}"); dsdt_unindent(1); } +#endif int pci_bus_configured(int bus) diff --git a/src/xhyve.c b/src/xhyve.c index 0348219..79cd767 100644 --- a/src/xhyve.c +++ b/src/xhyve.c @@ -66,6 +66,7 @@ #include #include +#include #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ @@ -116,6 +117,8 @@ static struct mt_vmm_info { } mt_vmm_info[VM_MAXCPU]; #pragma clang diagnostic pop +static uint64_t (*fw_func)(void); + __attribute__ ((noreturn)) static void usage(int code) { @@ -237,7 +240,7 @@ vcpu_thread(void *param) assert(error == 0); if (vcpu == BSP) { - rip_entry = kexec(); + rip_entry = fw_func(); } else { rip_entry = vmexit[vcpu].rip; spinup_ap_realmode(vcpu, &rip_entry); @@ -703,45 +706,57 @@ parse_memsize(const char *opt, size_t *ret_memsize) static int firmware_parse(const char *opt) { - char *fw, *kernel, *initrd, *cmdline, *cp; + char *fw, *opt1, *opt2, *opt3, *cp; fw = strdup(opt); - if (strncmp(fw, "kexec", strlen("kexec")) != 0) { - goto fail; - } - - if ((cp = strchr(fw, ',')) != NULL) { - *cp = '\0'; - kernel = cp + 1; + if (strncmp(fw, "kexec", strlen("kexec")) == 0) { + fw_func = kexec; + } else if (strncmp(fw, "fbsd", strlen("fbsd")) == 0) { + fw_func = fbsd_load; } else { goto fail; } - if ((cp = strchr(kernel, ',')) != NULL) { - *cp = '\0'; - initrd = cp + 1; + if ((cp = strchr(fw, ',')) != NULL) { + *cp = '\0'; + opt1 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt1, ',')) != NULL) { + *cp = '\0'; + opt2 = cp + 1; + } else { + goto fail; + } + + if ((cp = strchr(opt2, ',')) != NULL) { + *cp = '\0'; + opt3 = cp + 1; + } else { + goto fail; + } + + opt2 = strlen(opt2) ? opt2 : NULL; + opt3 = strlen(opt3) ? opt3 : NULL; + + if (fw_func == kexec) { + kexec_init(opt1, opt2, opt3); + } else if (fw_func == fbsd_load) { + /* FIXME: let user set boot-loader serial device */ + fbsd_init(opt1, opt2, opt3, NULL); } else { goto fail; } - if ((cp = strchr(initrd, ',')) != NULL) { - *cp = '\0'; - cmdline = cp + 1; - } else { - goto fail; - } - - initrd = strlen(initrd) ? initrd : NULL; - cmdline = strlen(cmdline) ? cmdline : NULL; - - kexec_init(kernel, initrd, cmdline); - return 0; fail: fprintf(stderr, "Invalid firmare argument\n" - " -f kexec,'kernel','initrd','\"cmdline\"'\n"); + " -f kexec,'kernel','initrd','\"cmdline\"'\n" + " -f fbsd,'userboot','boot volume','\"kernel env\"'\n"); return -1; } @@ -908,7 +923,6 @@ main(int argc, char *argv[]) error = acpi_build(guest_ncpus); assert(error == 0); } - rip = 0; diff --git a/test/userboot.so b/test/userboot.so new file mode 100755 index 0000000..75833f8 Binary files /dev/null and b/test/userboot.so differ diff --git a/test/userboot.txt b/test/userboot.txt new file mode 100644 index 0000000..20aa597 --- /dev/null +++ b/test/userboot.txt @@ -0,0 +1,8 @@ +userboot.so is the FreeBSD user-mode bootloader + +So, this is a bit horrible but it works: + + - userboot is compiled on FreeBSD with '-target x86_64-apple-darwin14' CFLAGS + - same for the dependencies of userboot (stand, ficl, zfs) + - you have to use the MachO linker set header (include/xhyve/support/linker_set.h) + - the resulting object files are linked on OS X with 'clang -dead_strip -shared -o userboot.so *.o *.So' diff --git a/xhyverun.sh b/xhyverun.sh index 5d2e147..b9fb8f1 100755 --- a/xhyverun.sh +++ b/xhyverun.sh @@ -1,9 +1,15 @@ #!/bin/sh +# Linux KERNEL="test/vmlinuz" INITRD="test/initrd.gz" CMDLINE="earlyprintk=serial console=ttyS0 acpi=off" +# FreeBSD +#USERBOOT="test/userboot.so" +#BOOTVOLUME="/somepath/somefile.{img | iso}" +#KERNELENV="" + MEM="-m 1G" #SMP="-c 2" #NET="-s 2:0,virtio-net" @@ -13,4 +19,8 @@ PCI_DEV="-s 0:0,hostbridge -s 31,lpc" LPC_DEV="-l com1,stdio" #UUID="-U deadbeef-dead-dead-dead-deaddeafbeef" +# Linux build/xhyve $MEM $SMP $PCI_DEV $LPC_DEV $NET $IMG_CD $IMG_HDD $UUID -f kexec,$KERNEL,$INITRD,"$CMDLINE" + +# FreeBSD +#build/xhyve -A $MEM $SMP $PCI_DEV $LPC_DEV $NET $IMG_CD $IMG_HDD $UUID -f fbsd,$USERBOOT,$BOOTVOLUME,"$KERNELENV"