1 // Code to load disk image and start system boot.
3 // Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002 MandrakeSoft S.A.
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
8 #include "util.h" // dprintf
9 #include "biosvar.h" // GET_EBDA
10 #include "config.h" // CONFIG_*
11 #include "disk.h" // cdrom_boot
12 #include "bregs.h" // struct bregs
13 #include "boot.h" // struct ipl_s
14 #include "cmos.h" // inb_cmos
20 /****************************************************************
22 ****************************************************************/
27 char *f = romfile_loadfile("bootorder", NULL);
32 IPL.fw_bootorder_count = 1;
35 IPL.fw_bootorder_count++;
38 IPL.fw_bootorder = malloc_tmphigh(IPL.fw_bootorder_count*sizeof(char*));
39 if (!IPL.fw_bootorder) {
45 dprintf(3, "boot order:\n");
48 IPL.fw_bootorder[i] = f;
53 dprintf(3, "%d: %s\n", i, IPL.fw_bootorder[i]);
64 dprintf(3, "init boot device ordering\n");
66 memset(&IPL, 0, sizeof(IPL));
67 struct ipl_entry_s *ie = &IPL.bev[0];
70 ie->type = IPL_TYPE_FLOPPY;
71 ie->description = "Floppy";
75 ie->type = IPL_TYPE_HARDDISK;
76 ie->description = "Hard Disk";
80 if (CONFIG_CDROM_BOOT) {
81 ie->type = IPL_TYPE_CDROM;
82 ie->description = "DVD/CD";
86 if (CONFIG_COREBOOT && CONFIG_COREBOOT_FLASH) {
87 ie->type = IPL_TYPE_CBFS;
88 ie->description = "CBFS";
92 IPL.bevcount = ie - IPL.bev;
93 SET_EBDA(boot_sequence, 0xffff);
94 if (CONFIG_COREBOOT) {
95 // XXX - hardcode defaults for coreboot.
96 IPL.bootorder = 0x87654231;
97 IPL.checkfloppysig = 1;
99 // On emulators, get boot order from nvram.
100 IPL.bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
101 | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
102 if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1))
103 IPL.checkfloppysig = 1;
110 /****************************************************************
111 * IPL and BCV handlers
112 ****************************************************************/
114 // Add a BEV vector for a given pnp compatible option rom.
116 add_bev(u16 seg, u16 bev, u16 desc)
120 if (IPL.bevcount >= ARRAY_SIZE(IPL.bev))
123 struct ipl_entry_s *ie = &IPL.bev[IPL.bevcount++];
124 ie->type = IPL_TYPE_BEV;
125 ie->vector = (seg << 16) | bev;
126 const char *d = "Unknown";
128 d = MAKE_FLATPTR(seg, desc);
132 // Add a IPL entry for BAID cdrom.
134 add_baid_cdrom(struct drive_s *drive_g)
136 if (! CONFIG_CDROM_BOOT)
139 /* put first cdrom into ipl 3 for compatability with qemu */
140 struct ipl_entry_s *ie = &IPL.bev[2];
141 if (IPL.bevcount >= ARRAY_SIZE(IPL.bev) && ie->vector)
145 ie = &IPL.bev[IPL.bevcount++];
146 ie->type = IPL_TYPE_CDROM;
147 ie->vector = (u32)drive_g;
148 ie->description = "DVD/CD";
151 // Add a bcv entry for an expansion card harddrive or legacy option rom
153 add_bcv(u16 seg, u16 ip, u16 desc)
157 if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv))
160 struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++];
161 ie->type = BCV_TYPE_EXTERNAL;
162 ie->vector = (seg << 16) | ip;
163 const char *d = "Legacy option rom";
165 d = MAKE_FLATPTR(seg, desc);
169 // Add a bcv entry for an internal harddrive
171 add_bcv_internal(struct drive_s *drive_g)
175 if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv))
178 struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++];
179 if (CONFIG_THREADS) {
180 // Add to bcv list with assured drive order.
181 struct ipl_entry_s *end = ie;
183 struct ipl_entry_s *prev = ie - 1;
184 if (prev < IPL.bcv || prev->type != BCV_TYPE_INTERNAL)
186 struct drive_s *prevdrive = (void*)prev->vector;
187 if (prevdrive->type < drive_g->type
188 || (prevdrive->type == drive_g->type
189 && prevdrive->cntl_id < drive_g->cntl_id))
194 memmove(ie+1, ie, (void*)end-(void*)ie);
196 ie->type = BCV_TYPE_INTERNAL;
197 ie->vector = (u32)drive_g;
198 ie->description = "";
202 /****************************************************************
203 * Boot menu and BCV execution
204 ****************************************************************/
206 // Show a generic menu item
208 menu_show_default(struct ipl_entry_s *ie, int menupos)
211 printf("%d. %s\n", menupos
212 , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
216 // Show floppy menu item - but only if there exists a floppy drive.
218 menu_show_floppy(struct ipl_entry_s *ie, int menupos)
221 for (i = 0; i < Drives.floppycount; i++) {
222 struct drive_s *drive_g = getDrive(EXTTYPE_FLOPPY, i);
223 printf("%d. Floppy [%s]\n", menupos + i, drive_g->desc);
225 return Drives.floppycount;
228 // Show menu items from BCV list.
230 menu_show_harddisk(struct ipl_entry_s *ie, int menupos)
233 for (i = 0; i < IPL.bcvcount; i++) {
234 struct ipl_entry_s *ie = &IPL.bcv[i];
235 struct drive_s *drive_g = (void*)ie->vector;
237 case BCV_TYPE_INTERNAL:
238 printf("%d. %s\n", menupos + i, drive_g->desc);
241 menu_show_default(ie, menupos+i);
248 // Show cdrom menu item - but only if there exists a cdrom drive.
250 menu_show_cdrom(struct ipl_entry_s *ie, int menupos)
252 struct drive_s *drive_g = (void*)ie->vector;
255 printf("%d. DVD/CD [%s]\n", menupos, drive_g->desc);
259 // Show coreboot-fs menu item.
261 menu_show_cbfs(struct ipl_entry_s *ie, int menupos)
264 struct cbfs_file *file = NULL;
266 file = cbfs_findprefix("img/", file);
269 const char *filename = cbfs_filename(file);
270 printf("%d. Payload [%s]\n", menupos + count, &filename[4]);
278 // Show IPL option menu.
280 interactive_bootmenu(void)
282 if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
285 while (get_keystroke(0) >= 0)
288 printf("Press F12 for boot menu.\n\n");
291 int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT);
292 disable_bootsplash();
293 if (scan_code != 0x86)
297 while (get_keystroke(0) >= 0)
300 printf("Select boot device:\n\n");
303 int subcount[ARRAY_SIZE(IPL.bev)];
306 for (i = 0; i < IPL.bevcount; i++) {
307 struct ipl_entry_s *ie = &IPL.bev[i];
310 case IPL_TYPE_FLOPPY:
311 sc = menu_show_floppy(ie, menupos);
313 case IPL_TYPE_HARDDISK:
314 sc = menu_show_harddisk(ie, menupos);
317 sc = menu_show_cdrom(ie, menupos);
320 sc = menu_show_cbfs(ie, menupos);
323 sc = menu_show_default(ie, menupos);
331 scan_code = get_keystroke(1000);
332 if (scan_code == 0x01)
335 if (scan_code < 1 || scan_code > menupos)
337 int choice = scan_code - 1;
339 // Find out which IPL this was for.
341 while (choice > subcount[bev]) {
342 choice -= subcount[bev];
345 IPL.bev[bev].subchoice = choice-1;
347 // Add user choice to the boot order.
348 IPL.bootorder = (IPL.bootorder << 4) | (bev+1);
354 // Run the specified bcv.
356 run_bcv(struct ipl_entry_s *ie)
359 case BCV_TYPE_INTERNAL:
360 map_hd_drive((void*)ie->vector);
362 case BCV_TYPE_EXTERNAL:
363 call_bcv(ie->vector >> 16, ie->vector & 0xffff);
368 // Prepare for boot - show menu and run bcvs.
377 // XXX - show available drives?
379 // Allow user to modify BCV/IPL order.
380 interactive_bootmenu();
383 // Setup floppy boot order
384 int override = IPL.bev[0].subchoice;
385 struct drive_s *tmp = Drives.idmap[EXTTYPE_FLOPPY][0];
386 Drives.idmap[EXTTYPE_FLOPPY][0] = Drives.idmap[EXTTYPE_FLOPPY][override];
387 Drives.idmap[EXTTYPE_FLOPPY][override] = tmp;
390 override = IPL.bev[1].subchoice;
391 if (override < IPL.bcvcount)
392 run_bcv(&IPL.bcv[override]);
394 for (i=0; i<IPL.bcvcount; i++)
396 run_bcv(&IPL.bcv[i]);
400 /****************************************************************
401 * Boot code (int 18/19)
402 ****************************************************************/
404 // Jump to a bootup entry point.
406 call_boot_entry(u16 bootseg, u16 bootip, u8 bootdrv)
408 dprintf(1, "Booting from %04x:%04x\n", bootseg, bootip);
410 memset(&br, 0, sizeof(br));
412 br.code = SEGOFF(bootseg, bootip);
413 // Set the magic number in ax and the boot drive in dl.
419 // Boot from a disk (either floppy or harddrive)
421 boot_disk(u8 bootdrv, int checksig)
423 u16 bootseg = 0x07c0;
427 memset(&br, 0, sizeof(br));
434 call16_int(0x13, &br);
436 if (br.flags & F_CF) {
437 printf("Boot failed: could not read the boot disk\n\n");
442 struct mbr_s *mbr = (void*)0;
443 if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
444 printf("Boot failed: not a bootable disk\n\n");
449 /* Canonicalize bootseg:bootip */
450 u16 bootip = (bootseg & 0x0fff) << 4;
453 call_boot_entry(bootseg, bootip, bootdrv);
456 // Boot from a CD-ROM
458 boot_cdrom(struct ipl_entry_s *ie)
460 if (! CONFIG_CDROM_BOOT)
466 struct drive_s *drive_g = (void*)ie->vector;
467 int status = cdrom_boot(drive_g);
469 printf("Boot failed: Could not read from CDROM %s (code %04x)\n", drive_g->desc, status);
473 u16 ebda_seg = get_ebda_seg();
474 u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
475 u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
476 /* Canonicalize bootseg:bootip */
477 u16 bootip = (bootseg & 0x0fff) << 4;
480 call_boot_entry(bootseg, bootip, bootdrv);
483 // Boot from a CBFS payload
485 boot_cbfs(struct ipl_entry_s *ie)
487 if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
489 int count = ie->subchoice;
490 struct cbfs_file *file = NULL;
492 file = cbfs_findprefix("img/", file);
497 cbfs_run_payload(file);
505 panic("Boot support not compiled in.\n");
507 u32 bootdev = IPL.bootorder;
508 bootdev >>= 4 * seq_nr;
511 /* Translate bootdev to an IPL table offset by subtracting 1 */
514 if (bootdev >= IPL.bevcount) {
515 printf("No bootable device.\n");
516 // Loop with irqs enabled - this allows ctrl+alt+delete to work.
521 /* Do the loading, and set up vector as a far pointer to the boot
522 * address, and bootdrv as the boot drive */
523 struct ipl_entry_s *ie = &IPL.bev[bootdev];
525 printf("Booting from %s...\n"
526 , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
529 case IPL_TYPE_FLOPPY:
530 boot_disk(0x00, IPL.checkfloppysig);
532 case IPL_TYPE_HARDDISK:
542 call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
546 // Boot failed: invoke the boot recovery function
548 memset(&br, 0, sizeof(br));
550 call16_int(0x18, &br);
553 // Boot Failure recovery: try the next device.
557 debug_serial_setup();
558 debug_enter(NULL, DEBUG_HDL_18);
559 u16 ebda_seg = get_ebda_seg();
560 u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
561 SET_EBDA2(ebda_seg, boot_sequence, seq);
565 // INT 19h Boot Load Service Entry Point
569 debug_serial_setup();
570 debug_enter(NULL, DEBUG_HDL_19);
571 SET_EBDA(boot_sequence, 0);