diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -234,6 +234,8 @@ enum pcibar_type type, uint64_t size); int pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size, void **const addr); +int pci_emul_add_boot_device(struct pci_devinst *const pi, + const int bootindex); int pci_emul_add_msicap(struct pci_devinst *pi, int msgnum); int pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type); void pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -62,6 +62,7 @@ #include "pci_irq.h" #include "pci_lpc.h" #include "pci_passthru.h" +#include "qemu_fwcfg.h" #define CONF1_ADDR_PORT 0x0cf8 #define CONF1_DATA_PORT 0x0cfc @@ -121,6 +122,14 @@ static TAILQ_HEAD(pci_bar_list, pci_bar_allocation) pci_bars = TAILQ_HEAD_INITIALIZER(pci_bars); +struct boot_device { + TAILQ_ENTRY(boot_device) boot_device_chain; + struct pci_devinst *pdi; + int bootindex; +}; +static TAILQ_HEAD(boot_list, boot_device) boot_devices = TAILQ_HEAD_INITIALIZER( + boot_devices); + #define PCI_EMUL_IOBASE 0x2000 #define PCI_EMUL_IOLIMIT 0x10000 @@ -955,6 +964,43 @@ return (0); } +int +pci_emul_add_boot_device(struct pci_devinst *pi, int bootindex) +{ + /* don't permit a negative bootindex */ + if (bootindex < 0) { + errx(4, "Invalid bootindex %d for %s", bootindex, pi->pi_name); + } + /* alloc new boot device */ + struct boot_device *new_device = calloc(1, sizeof(struct boot_device)); + if (new_device == NULL) { + return (-ENOMEM); + } + new_device->pdi = pi; + new_device->bootindex = bootindex; + + /* search for boot device with higher boot index */ + struct boot_device *device; + TAILQ_FOREACH(device, &boot_devices, boot_device_chain) { + if (device->bootindex == bootindex) { + errx(4, + "Could not set bootindex %d for %s. Bootindex already occupied by %s\n", + bootindex, pi->pi_name, device->pdi->pi_name); + } else if (device->bootindex > bootindex) { + break; + } + } + + /* add boot device to queue */ + if (device == NULL) { + TAILQ_INSERT_TAIL(&boot_devices, new_device, boot_device_chain); + } else { + TAILQ_INSERT_BEFORE(device, new_device, boot_device_chain); + } + + return (0); +} + #define CAP_START_OFFSET 0x40 static int pci_emul_add_capability(struct pci_devinst *pi, u_char *capdata, int caplen) @@ -1361,6 +1407,40 @@ return (PCI_EMUL_ECFG_BASE); } +static int +init_bootorder() +{ + if (TAILQ_EMPTY(&boot_devices)) { + return (0); + } + + struct boot_device *device; + uint8_t *bootorder = NULL; + uint64_t bootorder_len = 0; + TAILQ_FOREACH(device, &boot_devices, boot_device_chain) { + /* get bootpath string */ + uint8_t bootpath[NAME_MAX]; + snprintf(bootpath, NAME_MAX, "/pci@i0cf8/pci@%d,%d\n", + device->pdi->pi_slot, device->pdi->pi_func); + + /* realloc bootorder string */ + const uint64_t bootpath_len = strlen(bootpath); + bootorder = realloc(bootorder, bootorder_len + bootpath_len); + if (bootorder == NULL) { + return (-ENOMEM); + } + + /* copy new entry to bootorder */ + memcpy(&bootorder[bootorder_len], bootpath, bootpath_len); + bootorder_len += bootpath_len; + } + /* bootorder should be '\0' terminated */ + bootorder[bootorder_len - 1] = '\0'; + + return qemu_fwcfg_add_file("bootorder", bootorder_len, + (void *)bootorder); +} + #define BUSIO_ROUNDUP 32 #define BUSMEM32_ROUNDUP (1024 * 1024) #define BUSMEM64_ROUNDUP (512 * 1024 * 1024) @@ -1390,6 +1470,8 @@ pci_emul_membase64 = roundup2(pci_emul_membase64, PCI_EMUL_MEMSIZE64); pci_emul_memlim64 = pci_emul_membase64 + PCI_EMUL_MEMSIZE64; + TAILQ_INIT(&boot_devices); + for (bus = 0; bus < MAXBUSES; bus++) { snprintf(node_name, sizeof(node_name), "pci.%d", bus); nvl = find_config_node(node_name); @@ -1497,6 +1579,14 @@ } lpc_pirq_routed(); + /* + * Set up boot order + */ + if ((error = init_bootorder()) != 0) { + warnx("%s: Unable to init bootorder\n", __func__); + return (-1); + } + /* * The guest physical memory map looks like the following: * [0, lowmem) guest system memory