// Code to load disk image and start system boot. // // Copyright (C) 2008 Kevin O'Connor // Copyright (C) 2002 MandrakeSoft S.A. // // This file may be distributed under the terms of the GNU LGPLv3 license. #include "util.h" // irq_enable #include "biosvar.h" // GET_EBDA #include "config.h" // CONFIG_* #include "disk.h" // cdrom_boot #include "bregs.h" // struct bregs #include "boot.h" // struct ipl_s #include "cmos.h" // inb_cmos struct ipl_s IPL; /**************************************************************** * IPL and BCV handlers ****************************************************************/ void boot_setup() { if (! CONFIG_BOOT) return; dprintf(3, "init boot device ordering\n"); memset(&IPL, 0, sizeof(IPL)); // Floppy drive struct ipl_entry_s *ie = &IPL.bev[0]; ie->type = IPL_TYPE_FLOPPY; ie->description = "Floppy"; ie++; // First HDD ie->type = IPL_TYPE_HARDDISK; ie->description = "Hard Disk"; ie++; // CDROM if (CONFIG_CDROM_BOOT) { ie->type = IPL_TYPE_CDROM; ie->description = "CD-Rom"; ie++; } if (CONFIG_COREBOOT_FLASH) { ie->type = IPL_TYPE_CBFS; ie->description = "CBFS"; ie++; } IPL.bevcount = ie - IPL.bev; SET_EBDA(boot_sequence, 0xffff); if (CONFIG_COREBOOT) { // XXX - hardcode defaults for coreboot. IPL.bootorder = 0x87654231; IPL.checkfloppysig = 1; } else { // On emulators, get boot order from nvram. IPL.bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2) | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4)); if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1)) IPL.checkfloppysig = 1; } } // Add a BEV vector for a given pnp compatible option rom. void add_bev(u16 seg, u16 bev, u16 desc) { if (! CONFIG_BOOT) return; if (IPL.bevcount >= ARRAY_SIZE(IPL.bev)) return; struct ipl_entry_s *ie = &IPL.bev[IPL.bevcount++]; ie->type = IPL_TYPE_BEV; ie->vector = (seg << 16) | bev; const char *d = "Unknown"; if (desc) d = MAKE_FLATPTR(seg, desc); ie->description = d; } // Add a bcv entry for an expansion card harddrive or legacy option rom void add_bcv(u16 seg, u16 ip, u16 desc) { if (! CONFIG_BOOT) return; if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv)) return; struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++]; ie->type = IPL_TYPE_BEV; ie->vector = (seg << 16) | ip; const char *d = "Legacy option rom"; if (desc) d = MAKE_FLATPTR(seg, desc); ie->description = d; } // Add a bcv entry for an ata harddrive void add_bcv_hd(int driveid, const char *desc) { if (! CONFIG_BOOT) return; if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv)) return; struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++]; ie->type = IPL_TYPE_HARDDISK; ie->vector = driveid; ie->description = desc; } /**************************************************************** * Boot menu and BCV execution ****************************************************************/ // Show a generic menu item static int menu_show_default(struct ipl_entry_s *ie, int menupos) { char desc[33]; printf("%d. %s\n", menupos , strtcpy(desc, ie->description, ARRAY_SIZE(desc))); return 1; } // Show floppy menu item - but only if there exists a floppy drive. static int menu_show_floppy(struct ipl_entry_s *ie, int menupos) { int i; for (i = 0; i < Drives.floppycount; i++) { printf("%d. floppy %d\n", menupos + i, i+1); } return Drives.floppycount; } // Show menu items from BCV list. static int menu_show_harddisk(struct ipl_entry_s *ie, int menupos) { int i; for (i = 0; i < IPL.bcvcount; i++) { struct ipl_entry_s *ie = &IPL.bcv[i]; switch (ie->type) { case IPL_TYPE_HARDDISK: printf("%d. ata%d-%d %s\n", menupos + i , ie->vector / 2, ie->vector % 2, ie->description); break; default: menu_show_default(ie, menupos+i); break; } } return IPL.bcvcount; } // Show cdrom menu item - but only if there exists a cdrom drive. static int menu_show_cdrom(struct ipl_entry_s *ie, int menupos) { int i; for (i = 0; i < Drives.cdcount; i++) { int driveid = Drives.idmap[EXTTYPE_CD][i]; printf("%d. CD-Rom [ata%d-%d %s]\n", menupos + i , driveid / 2, driveid % 2, Drives.drives[driveid].model); } return Drives.cdcount; } // Show coreboot-fs menu item. static int menu_show_cbfs(struct ipl_entry_s *ie, int menupos) { int count = 0; struct cbfs_file *file; for (;;) { file = cbfs_findprefix("img/", file); if (!file) break; const char *filename = cbfs_filename(file); printf("%d. Payload [%s]\n", menupos + count, &filename[4]); count++; if (count > 8) break; } return count; } // Show IPL option menu. static void interactive_bootmenu() { if (! CONFIG_BOOTMENU) return; while (get_keystroke(0) >= 0) ; printf("Press F12 for boot menu.\n\n"); int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT); if (scan_code != 0x86) /* not F12 */ return; while (get_keystroke(0) >= 0) ; printf("Select boot device:\n\n"); int subcount[ARRAY_SIZE(IPL.bev)]; int menupos = 1; int i; for (i = 0; i < IPL.bevcount; i++) { struct ipl_entry_s *ie = &IPL.bev[i]; int sc = 1; switch (ie->type) { case IPL_TYPE_FLOPPY: sc = menu_show_floppy(ie, menupos); break; case IPL_TYPE_HARDDISK: sc = menu_show_harddisk(ie, menupos); break; case IPL_TYPE_CDROM: sc = menu_show_cdrom(ie, menupos); break; case IPL_TYPE_CBFS: sc = menu_show_cbfs(ie, menupos); break; default: sc = menu_show_default(ie, menupos); break; } subcount[i] = sc; menupos += sc; } for (;;) { scan_code = get_keystroke(1000); if (scan_code == 0x01) // ESC break; if (scan_code < 1 || scan_code > menupos) continue; int choice = scan_code - 1; // Find out which IPL this was for. int bev = 0; while (choice > subcount[bev]) { choice -= subcount[bev]; bev++; } IPL.bev[bev].subchoice = choice-1; // Add user choice to the boot order. IPL.bootorder = (IPL.bootorder << 4) | (bev+1); break; } printf("\n"); } // Run the specified bcv. static void run_bcv(struct ipl_entry_s *ie) { switch (ie->type) { case IPL_TYPE_HARDDISK: map_hd_drive(ie->vector); break; case IPL_TYPE_BEV: call_bcv(ie->vector >> 16, ie->vector & 0xffff); break; } } // Prepare for boot - show menu and run bcvs. void boot_prep() { if (! CONFIG_BOOT) return; // Allow user to modify BCV/IPL order. interactive_bootmenu(); // Setup floppy boot order int override = IPL.bev[0].subchoice; int tmp = Drives.idmap[EXTTYPE_FLOPPY][0]; Drives.idmap[EXTTYPE_FLOPPY][0] = Drives.idmap[EXTTYPE_FLOPPY][override]; Drives.idmap[EXTTYPE_FLOPPY][override] = tmp; // Run BCVs override = IPL.bev[1].subchoice; if (override < IPL.bcvcount) run_bcv(&IPL.bcv[override]); int i; for (i=0; isignature) != 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(bootseg, bootip, bootdrv); } // Boot from a CD-ROM static void boot_cdrom(struct ipl_entry_s *ie) { if (! CONFIG_CDROM_BOOT) return; int status = cdrom_boot(ie->subchoice); 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(bootseg, bootip, bootdrv); } // Boot from a CBFS payload static void boot_cbfs(struct ipl_entry_s *ie) { if (! CONFIG_COREBOOT_FLASH) return; int count = ie->subchoice; struct cbfs_file *file; for (;;) { file = cbfs_findprefix("img/", file); if (!file) return; if (count--) continue; cbfs_run_payload(file); } } static void do_boot(u16 seq_nr) { if (! CONFIG_BOOT) panic("Boot support not compiled in.\n"); u32 bootdev = IPL.bootorder; bootdev >>= 4 * seq_nr; bootdev &= 0xf; /* Translate bootdev to an IPL table offset by subtracting 1 */ bootdev -= 1; if (bootdev >= IPL.bevcount) { printf("No bootable device.\n"); // Loop with irqs enabled - this allows ctrl+alt+delete to work. for (;;) usleep(1000000); } /* Do the loading, and set up vector as a far pointer to the boot * address, and bootdrv as the boot drive */ struct ipl_entry_s *ie = &IPL.bev[bootdev]; char desc[33]; printf("Booting from %s...\n" , strtcpy(desc, ie->description, ARRAY_SIZE(desc))); switch(ie->type) { case IPL_TYPE_FLOPPY: boot_disk(0x00, IPL.checkfloppysig); break; case IPL_TYPE_HARDDISK: boot_disk(0x80, 1); break; case IPL_TYPE_CDROM: boot_cdrom(ie); break; case IPL_TYPE_CBFS: boot_cbfs(ie); break; case IPL_TYPE_BEV: call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0); break; } // Boot failed: invoke the boot recovery function struct bregs br; memset(&br, 0, sizeof(br)); call16_int(0x18, &br); } // Boot Failure recovery: try the next device. void VISIBLE32 handle_18() { 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 VISIBLE32 handle_19() { debug_serial_setup(); debug_enter(NULL, DEBUG_HDL_19); SET_EBDA(boot_sequence, 0); do_boot(0); }