With patch below (goes on top of bev per CD patch) I can specify boot
order from qemu command line between several NICs, CDROMs, floppy and
disk. With qemu command like this:
qemu -drive file=nonbootable.iso,if=none,id=d1,media=cdrom
-device ide-drive,drive=d1,bootindex=0
-drive file=rhel6.iso,if=none,id=d2,media=cdrom
-device ide-drive,drive=d2,bootindex=80
-drive if=none,file=fda.img,id=fda -drive if=none,file=fdb.img,id=fdb
-global isa-fdc.bootindexB=30 -global isa-fdc.bootindexA=40
-global isa-fdc.driveA=fda -global isa-fdc.driveB=fdb
-netdev type=user,id=net1 -device virtio-pci-net,netdev=net1,bootindex=20
-netdev type=user,id=net2 -device e1000,netdev=net2,bootindex=10
-drive file=/home/gleb_local/debian-testing1.img,if=none,id=d3
-device virtio-blk-pci,multifunction=on,drive=d3,bootindex=100
Seabios tries to boot from nonbootable.iso (ata1-0) first, then from
virtio-net nic, then from e1000 nic, then from floppy and finally from
rhel6.iso (ata1-1) at which point boot succeeds.
Signed-off-by: Gleb Natapov <gleb(a)redhat.com>
diff --git a/src/ahci.c b/src/ahci.c
index ee404d4..1dcbec1 100644
--- a/src/ahci.c
+++ b/src/ahci.c
@@ -376,7 +376,7 @@ ahci_port_init(struct ahci_ctrl_s *ctrl, u32 pnr)
// Setup disk geometry translation.
setup_translation(&port->drive);
// Register with bcv system.
- add_bcv_internal(&port->drive);
+ add_bcv_internal(&port->drive, -1);
} else {
// found cdrom (atapi)
port->drive.blksize = CDROM_SECTOR_SIZE;
diff --git a/src/ata.c b/src/ata.c
index bdec225..d4ba3e0 100644
--- a/src/ata.c
+++ b/src/ata.c
@@ -780,8 +780,11 @@ init_drive_atapi(struct atadrive_s *dummy, u16 *buffer)
// fill cdidmap
if (iscd) {
+ int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_bdf,
+ adrive_g->chan_gf->chanid,
+ adrive_g->slave);
map_cd_drive(&adrive_g->drive);
- add_baid_cdrom(&adrive_g->drive);
+ add_baid_cdrom(&adrive_g->drive, prio);
}
return adrive_g;
@@ -831,8 +834,11 @@ init_drive_ata(struct atadrive_s *dummy, u16 *buffer)
// Setup disk geometry translation.
setup_translation(&adrive_g->drive);
+ int prio = bootprio_find_ata_device(adrive_g->chan_gf->pci_bdf,
+ adrive_g->chan_gf->chanid,
+ adrive_g->slave);
// Register with bcv system.
- add_bcv_internal(&adrive_g->drive);
+ add_bcv_internal(&adrive_g->drive, prio);
return adrive_g;
}
diff --git a/src/boot.c b/src/boot.c
index 9c94439..39ab78f 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -13,10 +13,21 @@
#include "boot.h" // struct ipl_s
#include "cmos.h" // inb_cmos
#include "paravirt.h"
+#include "pci.h" //pci_bdf_to_*
struct ipl_s IPL;
+static u8 get_boot_priority(int prio)
+{
+ static u8 index = 0;
+
+ if (prio < 0)
+ return IPL.fw_bootorder_count + index++;
+
+ return (u8)prio;
+}
+
/****************************************************************
* IPL and BCV handlers
****************************************************************/
@@ -67,11 +78,56 @@ boot_setup(void)
if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1))
IPL.checkfloppysig = 1;
}
+
+ u32 file = romfile_find("bootorder");
+ if (!file)
+ return;
+
+ int filesize = romfile_size(file);
+ dprintf(3, "bootorder file found (len %d)\n", filesize);
+
+ if (filesize == 0)
+ return;
+
+ char *f = malloc_tmphigh(filesize);
+
+ if (!f) {
+ warn_noalloc();
+ return;
+ }
+
+ romfile_copy(file, f, filesize);
+ int i;
+ IPL.fw_bootorder_count = 1;
+ while(f[i]) {
+ if (f[i] == '\n')
+ IPL.fw_bootorder_count++;
+ i++;
+ }
+ IPL.fw_bootorder = malloc_tmphigh(IPL.fw_bootorder_count*sizeof(char*));
+ if (!IPL.fw_bootorder) {
+ warn_noalloc();
+ free(f);
+ return;
+ }
+
+ dprintf(3, "boot order:\n");
+ i = 0;
+ do {
+ IPL.fw_bootorder[i] = f;
+ f = strchr(f, '\n');
+ if (f) {
+ *f = '\0';
+ f++;
+ dprintf(3, "%d: %s\n", i, IPL.fw_bootorder[i]);
+ i++;
+ }
+ } while(f);
}
// Add a BEV vector for a given pnp compatible option rom.
void
-add_bev(u16 seg, u16 bev, u16 desc)
+add_bev(u16 seg, u16 bev, u16 desc, int prio)
{
if (! CONFIG_BOOT)
return;
@@ -85,11 +141,12 @@ add_bev(u16 seg, u16 bev, u16 desc)
if (desc)
d = MAKE_FLATPTR(seg, desc);
ie->description = d;
+ ie->prio = get_boot_priority(prio);
}
// Add a IPL entry for BAID cdrom.
void
-add_baid_cdrom(struct drive_s *drive_g)
+add_baid_cdrom(struct drive_s *drive_g, int prio)
{
if (! CONFIG_CDROM_BOOT)
return;
@@ -104,11 +161,18 @@ add_baid_cdrom(struct drive_s *drive_g)
ie->type = IPL_TYPE_CDROM;
ie->vector = (u32)drive_g;
ie->description = "DVD/CD";
+ ie->prio = get_boot_priority(prio);
+}
+
+void set_floppy_baid_prio(int prio)
+{
+ struct ipl_entry_s *ie = &IPL.bev[0];
+ ie->prio = get_boot_priority(prio);
}
// Add a bcv entry for an expansion card harddrive or legacy option rom
void
-add_bcv(u16 seg, u16 ip, u16 desc)
+add_bcv(u16 seg, u16 ip, u16 desc, int prio)
{
if (! CONFIG_BOOT)
return;
@@ -122,11 +186,12 @@ add_bcv(u16 seg, u16 ip, u16 desc)
if (desc)
d = MAKE_FLATPTR(seg, desc);
ie->description = d;
+ ie->prio = get_boot_priority(prio);
}
// Add a bcv entry for an internal harddrive
void
-add_bcv_internal(struct drive_s *drive_g)
+add_bcv_internal(struct drive_s *drive_g, int prio)
{
if (! CONFIG_BOOT)
return;
@@ -154,6 +219,7 @@ add_bcv_internal(struct drive_s *drive_g)
ie->type = BCV_TYPE_INTERNAL;
ie->vector = (u32)drive_g;
ie->description = "";
+ ie->prio = get_boot_priority(prio);
}
@@ -323,6 +389,40 @@ run_bcv(struct ipl_entry_s *ie)
}
}
+/* Bubble sort! Should be good enough for 8 elements */
+static void sort_ipls(struct ipl_entry_s *ipls, int iplscount)
+{
+ int stop;
+ if (iplscount == 0)
+ return;
+
+ do {
+ int i;
+ stop = 1;
+ for (i = 0; i < iplscount - 1; i++) {
+ if (ipls[i].prio > ipls[i+1].prio) {
+ struct ipl_entry_s tmp;
+ tmp = ipls[i];
+ ipls[i] = ipls[i+1];
+ ipls[i+1] = tmp;
+ stop = 0;
+ }
+ }
+ } while (!stop);
+}
+
+static void order_boot_devices(void)
+{
+ if (IPL.fw_bootorder_count == 0)
+ return;
+
+ sort_ipls(IPL.bcv, IPL.bcvcount);
+ /* Hard disk IPL inherits priority of the bootanle bcv */
+ IPL.bev[1].prio = IPL.bcv[0].prio;
+ sort_ipls(IPL.bev, IPL.bevcount);
+ IPL.bootorder = 0x87654321;
+}
+
// Prepare for boot - show menu and run bcvs.
void
boot_prep(void)
@@ -334,6 +434,8 @@ boot_prep(void)
// XXX - show available drives?
+ order_boot_devices();
+
// Allow user to modify BCV/IPL order.
interactive_bootmenu();
wait_threads();
@@ -529,3 +631,156 @@ handle_19(void)
SET_EBDA(boot_sequence, 0);
do_boot(0);
}
+
+/*
+ * function returns string representing firts device path element in 'dp'
+ * and puts pointer to the rest of the device path into 'end'
+ */
+static char *dev_path_get_node(const char *dp, const char **end)
+{
+ int len;
+ char *node;
+
+ dp += 1; /* skip '/' */
+
+ *end = strchr(dp, '/');
+
+ if (*end == NULL) {
+ len = strlen(dp); /* last path element */
+ *end = dp + len;
+ } else
+ len = *end - dp;
+
+ if (len == 0)
+ return NULL;
+
+ node = malloc_tmphigh(len + 1);
+ if (!node) {
+ warn_noalloc();
+ return NULL;
+ }
+ memcpy(node, dp, len);
+ node[len] = '\0';
+
+ return node;
+}
+
+static int match_unit_address(const char *pe, const char *unit_address)
+{
+ char *s = strchr(pe, '@');
+
+ if (s == NULL)
+ return 0;
+
+ return !strcmp(s + 1, unit_address);
+}
+
+static int match_next_node_address(const char *path, const char *addr,
+ const char **rest)
+{
+ char *node = dev_path_get_node(path, rest);
+
+ if (!node)
+ return 0;
+
+ int r = match_unit_address(node, addr);
+ free(node);
+
+ return r;
+}
+
+static int match_pci_node(const char *path, int bdf, const char **rest)
+{
+ char pci[7];
+ int dev = pci_bdf_to_dev(bdf), fn = pci_bdf_to_fn(bdf);
+
+ if (!fn)
+ snprintf(pci, sizeof(pci), "%x", dev);
+ else
+ snprintf(pci, sizeof(pci), "%x,%x", dev, fn);
+
+ return match_next_node_address(path, pci, rest);
+}
+
+#define FW_PCI_DOMAIN "/pci@i0cf8"
+
+static int find_pci_device(int bdf, int start_idx, const char **rest)
+{
+ int i;
+
+ for (i = start_idx; i < IPL.fw_bootorder_count; i++) {
+ const char *path = IPL.fw_bootorder[i];
+
+ /* is pci domain? */
+ if (memcmp(path, FW_PCI_DOMAIN, strlen(FW_PCI_DOMAIN)))
+ continue;
+
+ if (match_pci_node(path + strlen(FW_PCI_DOMAIN), bdf, rest))
+ return i;
+ }
+
+ return -1;
+}
+
+int bootprio_find_pci_device(int bdf)
+{
+ const char *rest;
+
+ return find_pci_device(bdf, 0, &rest);
+}
+
+int bootprio_find_ata_device(int bdf, int chanid, int slave)
+{
+ const char *rest;
+ int idx = -1;
+
+ if (bdf == -1)
+ return -1; /* support only pci machine for now */
+
+ while((idx = find_pci_device(bdf, idx + 1, &rest)) >= 0) {
+ if (match_next_node_address(rest, chanid ? "1" : "0", &rest) &&
+ match_next_node_address(rest, slave ? "1" : "0", &rest))
+ break;
+ }
+
+ return idx;
+}
+
+
+int bootprio_find_fdc_device(int bdf, int port, int fdid)
+{
+ const char *rest;
+ int idx = -1;
+ char addr[5];
+
+ if (bdf == -1)
+ return -1; /* support only pci machine for now */
+
+ snprintf(addr, sizeof addr, "%04x", port);
+
+ while((idx = find_pci_device(bdf, idx + 1, &rest)) >= 0) {
+ if (match_next_node_address(rest, addr, &rest) &&
+ match_next_node_address(rest, fdid ? "1" : "0", &rest))
+ break;
+ }
+
+ return idx;
+}
+
+int bootprio_find_named_rom(const char *name)
+{
+ int i;
+
+ for (i = 0; i < IPL.fw_bootorder_count; i++) {
+ const char *path = IPL.fw_bootorder[i];
+
+ /* is this rom file? */
+ if (memcmp(path, "/rom@", strlen("/rom@")))
+ continue;
+
+ if (strcmp(path + strlen("/rom@"), name))
+ return i;
+ }
+
+ return -1;
+}
diff --git a/src/boot.h b/src/boot.h
index f751d71..931d140 100644
--- a/src/boot.h
+++ b/src/boot.h
@@ -12,6 +12,7 @@ struct ipl_entry_s {
u16 subchoice;
u32 vector;
const char *description;
+ u8 prio;
};
struct ipl_s {
@@ -20,6 +21,8 @@ struct ipl_s {
int bevcount, bcvcount;
u32 bootorder;
int checkfloppysig;
+ char **fw_bootorder;
+ int fw_bootorder_count;
};
#define IPL_TYPE_FLOPPY 0x01
@@ -39,12 +42,17 @@ struct ipl_s {
// boot.c
extern struct ipl_s IPL;
void boot_setup(void);
-void add_bev(u16 seg, u16 bev, u16 desc);
-void add_bcv(u16 seg, u16 ip, u16 desc);
+void add_bev(u16 seg, u16 bev, u16 desc, int prio);
+void add_bcv(u16 seg, u16 ip, u16 desc, int prio);
struct drive_s;
-void add_bcv_internal(struct drive_s *drive_g);
-void add_baid_cdrom(struct drive_s *drive_g);
+void add_bcv_internal(struct drive_s *drive_g, int prio);
+void add_baid_cdrom(struct drive_s *drive_g, int prio);
+void set_floppy_baid_prio(int prio);
void boot_prep(void);
+int bootprio_find_pci_device(int bdf);
+int bootprio_find_ata_device(int bdf, int chanid, int slave);
+int bootprio_find_fdc_device(int bfd, int port, int fdid);
+int bootprio_find_named_rom(const char *name);
#endif // __BOOT_H
diff --git a/src/config.h b/src/config.h
index db3e578..0b575a9 100644
--- a/src/config.h
+++ b/src/config.h
@@ -16,9 +16,9 @@
#define CONFIG_COREBOOT 0
// Control how verbose debug output is.
-#define CONFIG_DEBUG_LEVEL 1
+#define CONFIG_DEBUG_LEVEL 5
// Send debugging information to serial port
-#define CONFIG_DEBUG_SERIAL 0
+#define CONFIG_DEBUG_SERIAL 1
// Screen writes are also sent to debug ports.
#define CONFIG_SCREEN_AND_DEBUG 1
diff --git a/src/floppy.c b/src/floppy.c
index 6491b96..d91ac24 100644
--- a/src/floppy.c
+++ b/src/floppy.c
@@ -13,6 +13,8 @@
#include "cmos.h" // inb_cmos
#include "pic.h" // eoi_pic1
#include "bregs.h" // struct bregs
+#include "pci.h" // pci_to_bdf
+#include "boot.h" // bootprio_find_fdc_device
#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
@@ -131,10 +133,25 @@ floppy_setup(void)
// XXX - disable floppies on coreboot for now.
} else {
u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
- if (type & 0xf0)
+ unsigned int prio0 = -1, prio1 = -1;
+ int bdf = pci_to_bdf(0, 1, 0); /* isa-to-pci bridge */
+
+ if (type & 0xf0) {
addFloppy(0, type >> 4, DTYPE_FLOPPY);
- if (type & 0x0f)
+ prio0 = bootprio_find_fdc_device(bdf, 0x3f0, 0);
+ }
+ if (type & 0x0f) {
addFloppy(1, type & 0x0f, DTYPE_FLOPPY);
+ prio1 = bootprio_find_fdc_device(bdf, 0x3f0, 1);
+ }
+ if (prio1 < prio0) {
+ /* if B has lower boot priority swap it with A */
+ struct drive_s *tmp = Drives.idmap[EXTTYPE_FLOPPY][0];
+ Drives.idmap[EXTTYPE_FLOPPY][0] = Drives.idmap[EXTTYPE_FLOPPY][1];
+ Drives.idmap[EXTTYPE_FLOPPY][1] = tmp;
+ set_floppy_baid_prio(prio1);
+ } else
+ set_floppy_baid_prio(prio0);
}
outb(0x02, PORT_DMA1_MASK_REG);
diff --git a/src/optionroms.c b/src/optionroms.c
index 854c33f..4b2ae67 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -74,6 +74,34 @@ struct pnp_data {
// The end of the last deployed rom.
u32 RomEnd = BUILD_ROM_START;
+static struct rom_dev {
+ u32 addr;
+ int idx;
+} rom2dev[92];
+
+static int rom2dev_cnt;
+
+static void map_rom2dev(void *addr, int idx)
+{
+ if (rom2dev_cnt < ARRAY_SIZE(rom2dev)) {
+ rom2dev[rom2dev_cnt].addr = (u32)addr;
+ rom2dev[rom2dev_cnt].idx = idx;
+ dprintf(3, "pci rom at memory address %x has boot prio index %d\n",
+ rom2dev[rom2dev_cnt].addr, rom2dev[rom2dev_cnt].idx);
+ rom2dev_cnt++;
+ }
+}
+
+static int rom2dev_idx(u32 addr)
+{
+ int i;
+
+ for (i = 0; i < rom2dev_cnt; i++)
+ if (rom2dev[i].addr == addr)
+ return rom2dev[i].idx;
+
+ return -1;
+}
/****************************************************************
* Helper functions
@@ -243,13 +271,18 @@ static void
run_file_roms(const char *prefix, int isvga)
{
u32 file = 0;
+
for (;;) {
file = romfile_findprefix(prefix, file);
if (!file)
break;
int ret = romfile_copy(file, (void*)RomEnd, max_rom() - RomEnd);
- if (ret > 0)
- init_optionrom((void*)RomEnd, 0, isvga);
+ if (ret > 0) {
+ void *rom = (void*)RomEnd;
+ ret = init_optionrom(rom, 0, isvga);
+ if (!ret && is_valid_rom(rom))
+ map_rom2dev(rom, bootprio_find_named_rom(romfile_name(file)));
+ }
}
}
@@ -342,7 +375,15 @@ init_pcirom(u16 bdf, int isvga)
if (! rom)
// No ROM present.
return -1;
- return init_optionrom(rom, bdf, isvga);
+
+ int r = init_optionrom(rom, bdf, isvga);
+
+ if (r || !is_valid_rom(rom))
+ return r;
+
+ map_rom2dev(rom, bootprio_find_pci_device(bdf));
+
+ return r;
}
@@ -394,21 +435,22 @@ optionrom_setup(void)
pos += OPTION_ROM_ALIGN;
continue;
}
+ int devidx = rom2dev_idx(pos);
pos += ALIGN(rom->size * 512, OPTION_ROM_ALIGN);
struct pnp_data *pnp = get_pnp_rom(rom);
if (! pnp) {
// Legacy rom.
- add_bcv(FLATPTR_TO_SEG(rom), OPTION_ROM_INITVECTOR, 0);
+ add_bcv(FLATPTR_TO_SEG(rom), OPTION_ROM_INITVECTOR, 0, devidx);
continue;
}
// PnP rom.
if (pnp->bev)
// Can boot system - add to IPL list.
- add_bev(FLATPTR_TO_SEG(rom), pnp->bev, pnp->productname);
+ add_bev(FLATPTR_TO_SEG(rom), pnp->bev, pnp->productname, devidx);
else
// Check for BCV (there may be multiple).
while (pnp && pnp->bcv) {
- add_bcv(FLATPTR_TO_SEG(rom), pnp->bcv, pnp->productname);
+ add_bcv(FLATPTR_TO_SEG(rom), pnp->bcv, pnp->productname, devidx);
pnp = get_pnp_next(rom, pnp);
}
}
diff --git a/src/paravirt.c b/src/paravirt.c
index ca646d4..308c809 100644
--- a/src/paravirt.c
+++ b/src/paravirt.c
@@ -345,6 +345,14 @@ int qemu_cfg_size_file(u32 select)
return ntohl(LastFile.size);
}
+
+const char* qemu_cfg_name_file(u32 select)
+{
+ if (select != ntohs(LastFile.select))
+ return NULL;
+ return LastFile.name;
+}
+
int qemu_cfg_read_file(u32 select, void *dst, u32 maxlen)
{
if (!qemu_cfg_present)
diff --git a/src/paravirt.h b/src/paravirt.h
index 7d4bc02..99c473b 100644
--- a/src/paravirt.h
+++ b/src/paravirt.h
@@ -71,6 +71,7 @@ struct e820_reservation {
u32 qemu_cfg_next_prefix_file(const char *prefix, u32 prevselect);
u32 qemu_cfg_find_file(const char *name);
int qemu_cfg_size_file(u32 select);
+const char* qemu_cfg_name_file(u32 select);
int qemu_cfg_read_file(u32 select, void *dst, u32 maxlen);
// Wrappers that select cbfs or qemu_cfg file interface.
@@ -94,6 +95,11 @@ static inline int romfile_copy(u32 fileid, void *dst, u32 maxlen) {
return cbfs_copyfile((void*)fileid, dst, maxlen);
return qemu_cfg_read_file(fileid, dst, maxlen);
}
+static inline const char* romfile_name(u32 fileid) {
+ if (CONFIG_COREBOOT)
+ return cbfs_filename((void*)fileid);
+ return qemu_cfg_name_file(fileid);
+}
u32 qemu_cfg_e820_entries(void);
void* qemu_cfg_e820_load_next(void *addr);
diff --git a/src/usb-msc.c b/src/usb-msc.c
index 968cae3..f6253ca 100644
--- a/src/usb-msc.c
+++ b/src/usb-msc.c
@@ -168,7 +168,7 @@ setup_drive_hd(struct disk_op_s *op)
setup_translation(op->drive_g);
// Register with bcv system.
- add_bcv_internal(op->drive_g);
+ add_bcv_internal(op->drive_g, -1);
return 0;
}
diff --git a/src/util.c b/src/util.c
index f9a7533..3892c3c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -262,6 +262,17 @@ strtcpy(char *dest, const char *src, size_t len)
return dest;
}
+// locate first occurance of character c in the string s
+char *
+strchr(const char *s, int c)
+{
+ int i = 0;
+
+ while(s[i] && s[i] != c)
+ i++;
+
+ return s[i] ? (char*)&s[i] : NULL;
+}
/****************************************************************
* Keyboard calls
diff --git a/src/util.h b/src/util.h
index fa7b20f..f5b9446 100644
--- a/src/util.h
+++ b/src/util.h
@@ -209,6 +209,7 @@ void *memcpy(void *d1, const void *s1, size_t len);
void iomemcpy(void *d, const void *s, u32 len);
void *memmove(void *d, const void *s, size_t len);
char *strtcpy(char *dest, const char *src, size_t len);
+char *strchr(const char *s, int c);
int get_keystroke(int msec);
// stacks.c
diff --git a/src/virtio-blk.c b/src/virtio-blk.c
index 7a25826..3d2b7a6 100644
--- a/src/virtio-blk.c
+++ b/src/virtio-blk.c
@@ -153,7 +153,7 @@ init_virtio_blk(u16 bdf)
vdrive_g->drive.pchs.spt = cfg.sectors;
setup_translation(&vdrive_g->drive);
- add_bcv_internal(&vdrive_g->drive);
+ add_bcv_internal(&vdrive_g->drive, bootprio_find_pci_device(bdf));
snprintf(desc, MAXDESCSIZE, "Virtio disk PCI:%x:%x",
pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
--
Gleb.