Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


imported SEABIOS source tree
[palacios.git] / bios / seabios / src / boot.c
diff --git a/bios/seabios/src/boot.c b/bios/seabios/src/boot.c
new file mode 100644 (file)
index 0000000..119f290
--- /dev/null
@@ -0,0 +1,662 @@
+// Code to load disk image and start system boot.
+//
+// Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2002  MandrakeSoft S.A.
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include "util.h" // dprintf
+#include "biosvar.h" // GET_EBDA
+#include "config.h" // CONFIG_*
+#include "disk.h" // cdrom_boot
+#include "bregs.h" // struct bregs
+#include "boot.h" // func defs
+#include "cmos.h" // inb_cmos
+#include "paravirt.h" // romfile_loadfile
+#include "pci.h" //pci_bdf_to_*
+
+
+/****************************************************************
+ * Boot priority ordering
+ ****************************************************************/
+
+static char **Bootorder;
+static int BootorderCount;
+
+static void
+loadBootOrder(void)
+{
+    if (!CONFIG_BOOTORDER)
+        return;
+
+    char *f = romfile_loadfile("bootorder", NULL);
+    if (!f)
+        return;
+
+    int i = 0;
+    BootorderCount = 1;
+    while (f[i]) {
+        if (f[i] == '\n')
+            BootorderCount++;
+        i++;
+    }
+    Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*));
+    if (!Bootorder) {
+        warn_noalloc();
+        free(f);
+        BootorderCount = 0;
+        return;
+    }
+
+    dprintf(3, "boot order:\n");
+    i = 0;
+    do {
+        Bootorder[i] = f;
+        f = strchr(f, '\n');
+        if (f)
+            *(f++) = '\0';
+        nullTrailingSpace(Bootorder[i]);
+        dprintf(3, "%d: %s\n", i+1, Bootorder[i]);
+        i++;
+    } while (f);
+}
+
+// See if 'str' starts with 'glob' - if glob contains an '*' character
+// it will match any number of characters in str that aren't a '/' or
+// the next glob character.
+static char *
+glob_prefix(const char *glob, const char *str)
+{
+    for (;;) {
+        if (!*glob && (!*str || *str == '/'))
+            return (char*)str;
+        if (*glob == '*') {
+            if (!*str || *str == '/' || *str == glob[1])
+                glob++;
+            else
+                str++;
+            continue;
+        }
+        if (*glob != *str)
+            return NULL;
+        glob++;
+        str++;
+    }
+}
+
+// Search the bootorder list for the given glob pattern.
+static int
+find_prio(const char *glob)
+{
+    dprintf(1, "Searching bootorder for: %s\n", glob);
+    int i;
+    for (i = 0; i < BootorderCount; i++)
+        if (glob_prefix(glob, Bootorder[i]))
+            return i+1;
+    return -1;
+}
+
+#define FW_PCI_DOMAIN "/pci@i0cf8"
+
+static char *
+build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci)
+{
+    // Build the string path of a bdf - for example: /pci@i0cf8/isa@1,2
+    char *p = buf;
+    if (pci->parent) {
+        p = build_pci_path(p, max, "pci-bridge", pci->parent);
+    } else {
+        if (pci->rootbus)
+            p += snprintf(p, max, "/pci-root@%x", pci->rootbus);
+        p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN);
+    }
+
+    int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf);
+    p += snprintf(p, buf+max-p, "/%s@%x", devname, dev);
+    if (fn)
+        p += snprintf(p, buf+max-p, ",%x", fn);
+    return p;
+}
+
+int bootprio_find_pci_device(struct pci_device *pci)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    // Find pci device - for example: /pci@i0cf8/ethernet@5
+    char desc[256];
+    build_pci_path(desc, sizeof(desc), "*", pci);
+    return find_prio(desc);
+}
+
+int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    if (!pci)
+        // support only pci machine for now
+        return -1;
+    // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0
+    char desc[256], *p;
+    p = build_pci_path(desc, sizeof(desc), "*", pci);
+    snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave);
+    return find_prio(desc);
+}
+
+int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    if (!pci)
+        // support only pci machine for now
+        return -1;
+    // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0
+    char desc[256], *p;
+    p = build_pci_path(desc, sizeof(desc), "isa", pci);
+    snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid);
+    return find_prio(desc);
+}
+
+int bootprio_find_pci_rom(struct pci_device *pci, int instance)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2
+    char desc[256], *p;
+    p = build_pci_path(desc, sizeof(desc), "*", pci);
+    if (instance)
+        snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
+    return find_prio(desc);
+}
+
+int bootprio_find_named_rom(const char *name, int instance)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    // Find named rom - for example: /rom@genroms/linuxboot.bin
+    char desc[256], *p;
+    p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name);
+    if (instance)
+        snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
+    return find_prio(desc);
+}
+
+int bootprio_find_usb(struct pci_device *pci, u64 path)
+{
+    if (!CONFIG_BOOTORDER)
+        return -1;
+    // Find usb - for example: /pci@i0cf8/usb@1,2/hub@1/network@0/ethernet@0
+    int i;
+    char desc[256], *p;
+    p = build_pci_path(desc, sizeof(desc), "usb", pci);
+    for (i=56; i>0; i-=8) {
+        int port = (path >> i) & 0xff;
+        if (port != 0xff)
+            p += snprintf(p, desc+sizeof(desc)-p, "/hub@%x", port);
+    }
+    snprintf(p, desc+sizeof(desc)-p, "/*@%x", (u32)(path & 0xff));
+    return find_prio(desc);
+}
+
+
+/****************************************************************
+ * Boot setup
+ ****************************************************************/
+
+static int CheckFloppySig = 1;
+
+#define DEFAULT_PRIO           9999
+
+static int DefaultFloppyPrio = 101;
+static int DefaultCDPrio     = 102;
+static int DefaultHDPrio     = 103;
+static int DefaultBEVPrio    = 104;
+
+void
+boot_setup(void)
+{
+    if (! CONFIG_BOOT)
+        return;
+
+    SET_EBDA(boot_sequence, 0xffff);
+
+    if (!CONFIG_COREBOOT) {
+        // On emulators, get boot order from nvram.
+        if (inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1)
+            CheckFloppySig = 0;
+        u32 bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
+                         | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
+        DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio
+            = DefaultBEVPrio = DEFAULT_PRIO;
+        int i;
+        for (i=101; i<104; i++) {
+            u32 val = bootorder & 0x0f;
+            bootorder >>= 4;
+            switch (val) {
+            case 1: DefaultFloppyPrio = i; break;
+            case 2: DefaultHDPrio = i;     break;
+            case 3: DefaultCDPrio = i;     break;
+            case 4: DefaultBEVPrio = i;    break;
+            }
+        }
+    }
+
+    loadBootOrder();
+}
+
+
+/****************************************************************
+ * BootList handling
+ ****************************************************************/
+
+struct bootentry_s {
+    int type;
+    union {
+        u32 data;
+        struct segoff_s vector;
+        struct drive_s *drive;
+    };
+    int priority;
+    const char *description;
+    struct bootentry_s *next;
+};
+static struct bootentry_s *BootList;
+
+#define IPL_TYPE_FLOPPY      0x01
+#define IPL_TYPE_HARDDISK    0x02
+#define IPL_TYPE_CDROM       0x03
+#define IPL_TYPE_CBFS        0x20
+#define IPL_TYPE_BEV         0x80
+#define IPL_TYPE_BCV         0x81
+
+static void
+bootentry_add(int type, int prio, u32 data, const char *desc)
+{
+    if (! CONFIG_BOOT)
+        return;
+    struct bootentry_s *be = malloc_tmp(sizeof(*be));
+    if (!be) {
+        warn_noalloc();
+        return;
+    }
+    be->type = type;
+    be->priority = prio;
+    be->data = data;
+    be->description = desc ?: "?";
+    dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n"
+            , be->description, type, prio, data);
+
+    // Add entry in sorted order.
+    struct bootentry_s **pprev;
+    for (pprev = &BootList; *pprev; pprev = &(*pprev)->next) {
+        struct bootentry_s *pos = *pprev;
+        if (be->priority < pos->priority)
+            break;
+        if (be->priority > pos->priority)
+            continue;
+        if (be->type < pos->type)
+            break;
+        if (be->type > pos->type)
+            continue;
+        if (be->type <= IPL_TYPE_CDROM
+            && (be->drive->type < pos->drive->type
+                || (be->drive->type == pos->drive->type
+                    && be->drive->cntl_id < pos->drive->cntl_id)))
+            break;
+    }
+    be->next = *pprev;
+    *pprev = be;
+}
+
+// Return the given priority if it's set - defaultprio otherwise.
+static inline int defPrio(int priority, int defaultprio) {
+    return (priority < 0) ? defaultprio : priority;
+}
+
+// Add a BEV vector for a given pnp compatible option rom.
+void
+boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
+{
+    bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
+                  , SEGOFF(seg, bev).segoff
+                  , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
+    DefaultBEVPrio = DEFAULT_PRIO;
+}
+
+// Add a bcv entry for an expansion card harddrive or legacy option rom
+void
+boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
+{
+    bootentry_add(IPL_TYPE_BCV, defPrio(prio, DEFAULT_PRIO)
+                  , SEGOFF(seg, ip).segoff
+                  , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
+}
+
+void
+boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio)
+{
+    bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
+                  , (u32)drive_g, desc);
+}
+
+void
+boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
+{
+    bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
+                  , (u32)drive_g, desc);
+}
+
+void
+boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
+{
+    bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
+                  , (u32)drive_g, desc);
+}
+
+// Add a CBFS payload entry
+void
+boot_add_cbfs(void *data, const char *desc, int prio)
+{
+    bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
+}
+
+
+/****************************************************************
+ * Boot menu and BCV execution
+ ****************************************************************/
+
+#define DEFAULT_BOOTMENU_WAIT 2500
+
+// Show IPL option menu.
+static void
+interactive_bootmenu(void)
+{
+    if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
+        return;
+
+    while (get_keystroke(0) >= 0)
+        ;
+
+    printf("Press F12 for boot menu.\n\n");
+
+    u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
+    enable_bootsplash();
+    int scan_code = get_keystroke(menutime);
+    disable_bootsplash();
+    if (scan_code != 0x86)
+        /* not F12 */
+        return;
+
+    while (get_keystroke(0) >= 0)
+        ;
+
+    printf("Select boot device:\n\n");
+    wait_threads();
+
+    // Show menu items
+    struct bootentry_s *pos = BootList;
+    int maxmenu = 0;
+    while (pos) {
+        char desc[60];
+        maxmenu++;
+        printf("%d. %s\n", maxmenu
+               , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
+        pos = pos->next;
+    }
+
+    // Get key press
+    for (;;) {
+        scan_code = get_keystroke(1000);
+        if (scan_code >= 1 && scan_code <= maxmenu+1)
+            break;
+    }
+    printf("\n");
+    if (scan_code == 0x01)
+        // ESC
+        return;
+
+    // Find entry and make top priority.
+    int choice = scan_code - 1;
+    struct bootentry_s **pprev = &BootList;
+    while (--choice)
+        pprev = &(*pprev)->next;
+    pos = *pprev;
+    *pprev = pos->next;
+    pos->next = BootList;
+    BootList = pos;
+    pos->priority = 0;
+}
+
+// BEV (Boot Execution Vector) list
+struct bev_s {
+    int type;
+    u32 vector;
+};
+static struct bev_s BEV[20];
+static int BEVCount;
+static int HaveHDBoot, HaveFDBoot;
+
+static void
+add_bev(int type, u32 vector)
+{
+    if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
+        return;
+    if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
+        return;
+    if (BEVCount >= ARRAY_SIZE(BEV))
+        return;
+    struct bev_s *bev = &BEV[BEVCount++];
+    bev->type = type;
+    bev->vector = vector;
+}
+
+// Prepare for boot - show menu and run bcvs.
+void
+boot_prep(void)
+{
+    if (! CONFIG_BOOT) {
+        wait_threads();
+        return;
+    }
+
+    // XXX - show available drives?
+
+    // Allow user to modify BCV/IPL order.
+    interactive_bootmenu();
+    wait_threads();
+
+    // Map drives and populate BEV list
+    struct bootentry_s *pos = BootList;
+    while (pos) {
+        switch (pos->type) {
+        case IPL_TYPE_BCV:
+            call_bcv(pos->vector.seg, pos->vector.offset);
+            add_bev(IPL_TYPE_HARDDISK, 0);
+            break;
+        case IPL_TYPE_FLOPPY:
+            map_floppy_drive(pos->drive);
+            add_bev(IPL_TYPE_FLOPPY, 0);
+            break;
+        case IPL_TYPE_HARDDISK:
+            map_hd_drive(pos->drive);
+            add_bev(IPL_TYPE_HARDDISK, 0);
+            break;
+        case IPL_TYPE_CDROM:
+            map_cd_drive(pos->drive);
+            // NO BREAK
+        default:
+            add_bev(pos->type, pos->data);
+            break;
+        }
+        pos = pos->next;
+    }
+
+    // If nothing added a floppy/hd boot - add it manually.
+    add_bev(IPL_TYPE_FLOPPY, 0);
+    add_bev(IPL_TYPE_HARDDISK, 0);
+}
+
+
+/****************************************************************
+ * Boot code (int 18/19)
+ ****************************************************************/
+
+// Jump to a bootup entry point.
+static void
+call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
+{
+    dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    br.flags = F_IF;
+    br.code = bootsegip;
+    // Set the magic number in ax and the boot drive in dl.
+    br.dl = bootdrv;
+    br.ax = 0xaa55;
+    call16(&br);
+}
+
+// Boot from a disk (either floppy or harddrive)
+static void
+boot_disk(u8 bootdrv, int checksig)
+{
+    u16 bootseg = 0x07c0;
+
+    // Read sector
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    br.flags = F_IF;
+    br.dl = bootdrv;
+    br.es = bootseg;
+    br.ah = 2;
+    br.al = 1;
+    br.cl = 1;
+    call16_int(0x13, &br);
+
+    if (br.flags & F_CF) {
+        printf("Boot failed: could not read the boot disk\n\n");
+        return;
+    }
+
+    if (checksig) {
+        struct mbr_s *mbr = (void*)0;
+        if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
+            printf("Boot failed: not a bootable disk\n\n");
+            return;
+        }
+    }
+
+    /* Canonicalize bootseg:bootip */
+    u16 bootip = (bootseg & 0x0fff) << 4;
+    bootseg &= 0xf000;
+
+    call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
+}
+
+// Boot from a CD-ROM
+static void
+boot_cdrom(struct drive_s *drive_g)
+{
+    if (! CONFIG_CDROM_BOOT)
+        return;
+    printf("Booting from DVD/CD...\n");
+
+    int status = cdrom_boot(drive_g);
+    if (status) {
+        printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
+        return;
+    }
+
+    u16 ebda_seg = get_ebda_seg();
+    u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
+    u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
+    /* Canonicalize bootseg:bootip */
+    u16 bootip = (bootseg & 0x0fff) << 4;
+    bootseg &= 0xf000;
+
+    call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
+}
+
+// Boot from a CBFS payload
+static void
+boot_cbfs(struct cbfs_file *file)
+{
+    if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
+        return;
+    printf("Booting from CBFS...\n");
+    cbfs_run_payload(file);
+}
+
+// Boot from a BEV entry on an optionrom.
+static void
+boot_rom(u32 vector)
+{
+    printf("Booting from ROM...\n");
+    struct segoff_s so;
+    so.segoff = vector;
+    call_boot_entry(so, 0);
+}
+
+// Determine next boot method and attempt a boot using it.
+static void
+do_boot(u16 seq_nr)
+{
+    if (! CONFIG_BOOT)
+        panic("Boot support not compiled in.\n");
+
+    if (seq_nr >= BEVCount) {
+        printf("No bootable device.\n");
+        // Loop with irqs enabled - this allows ctrl+alt+delete to work.
+        for (;;)
+            wait_irq();
+    }
+
+    // Boot the given BEV type.
+    struct bev_s *ie = &BEV[seq_nr];
+    switch (ie->type) {
+    case IPL_TYPE_FLOPPY:
+        printf("Booting from Floppy...\n");
+        boot_disk(0x00, CheckFloppySig);
+        break;
+    case IPL_TYPE_HARDDISK:
+        printf("Booting from Hard Disk...\n");
+        boot_disk(0x80, 1);
+        break;
+    case IPL_TYPE_CDROM:
+        boot_cdrom((void*)ie->vector);
+        break;
+    case IPL_TYPE_CBFS:
+        boot_cbfs((void*)ie->vector);
+        break;
+    case IPL_TYPE_BEV:
+        boot_rom(ie->vector);
+        break;
+    }
+
+    // Boot failed: invoke the boot recovery function
+    struct bregs br;
+    memset(&br, 0, sizeof(br));
+    br.flags = F_IF;
+    call16_int(0x18, &br);
+}
+
+// Boot Failure recovery: try the next device.
+void VISIBLE32FLAT
+handle_18(void)
+{
+    debug_serial_setup();
+    debug_enter(NULL, DEBUG_HDL_18);
+    u16 ebda_seg = get_ebda_seg();
+    u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
+    SET_EBDA2(ebda_seg, boot_sequence, seq);
+    do_boot(seq);
+}
+
+// INT 19h Boot Load Service Entry Point
+void VISIBLE32FLAT
+handle_19(void)
+{
+    debug_serial_setup();
+    debug_enter(NULL, DEBUG_HDL_19);
+    SET_EBDA(boot_sequence, 0);
+    do_boot(0);
+}