grml...
[seabios.git] / src / boot.c
1 // Code to load disk image and start system boot.
2 //
3 // Copyright (C) 2008-2010  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
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" // func defs
14 #include "cmos.h" // inb_cmos
15 #include "paravirt.h" // romfile_loadfile
16 #include "pci.h" //pci_bdf_to_*
17
18
19 /****************************************************************
20  * Boot priority ordering
21  ****************************************************************/
22
23 static char **Bootorder;
24 static int BootorderCount;
25
26 static void
27 loadBootOrder(void)
28 {
29     if (!CONFIG_BOOTORDER)
30         return;
31
32     char *f = romfile_loadfile("bootorder", NULL);
33     if (!f)
34         return;
35
36     int i = 0;
37     BootorderCount = 1;
38     while (f[i]) {
39         if (f[i] == '\n')
40             BootorderCount++;
41         i++;
42     }
43     Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*));
44     if (!Bootorder) {
45         warn_noalloc();
46         free(f);
47         BootorderCount = 0;
48         return;
49     }
50
51     dprintf(3, "boot order:\n");
52     i = 0;
53     do {
54         Bootorder[i] = f;
55         f = strchr(f, '\n');
56         if (f)
57             *(f++) = '\0';
58         nullTrailingSpace(Bootorder[i]);
59         dprintf(3, "%d: %s\n", i+1, Bootorder[i]);
60         i++;
61     } while (f);
62 }
63
64 // See if 'str' starts with 'glob' - if glob contains an '*' character
65 // it will match any number of characters in str that aren't a '/' or
66 // the next glob character.
67 static char *
68 glob_prefix(const char *glob, const char *str)
69 {
70     for (;;) {
71         if (!*glob && (!*str || *str == '/'))
72             return (char*)str;
73         if (*glob == '*') {
74             if (!*str || *str == '/' || *str == glob[1])
75                 glob++;
76             else
77                 str++;
78             continue;
79         }
80         if (*glob != *str)
81             return NULL;
82         glob++;
83         str++;
84     }
85 }
86
87 // Search the bootorder list for the given glob pattern.
88 static int
89 find_prio(const char *glob)
90 {
91     dprintf(1, "Searching bootorder for: %s\n", glob);
92     int i;
93     for (i = 0; i < BootorderCount; i++)
94         if (glob_prefix(glob, Bootorder[i]))
95             return i+1;
96     return -1;
97 }
98
99 #define FW_PCI_DOMAIN "/pci@i0cf8"
100
101 static char *
102 build_pci_path(char *buf, int max, const char *devname, struct pci_device *pci)
103 {
104     // Build the string path of a bdf - for example: /pci@i0cf8/isa@1,2
105     char *p = buf;
106     if (pci->parent) {
107         p = build_pci_path(p, max, "pci-bridge", pci->parent);
108     } else {
109         if (pci->rootbus)
110             p += snprintf(p, max, "/pci-root@%x", pci->rootbus);
111         p += snprintf(p, buf+max-p, "%s", FW_PCI_DOMAIN);
112     }
113
114     int dev = pci_bdf_to_dev(pci->bdf), fn = pci_bdf_to_fn(pci->bdf);
115     p += snprintf(p, buf+max-p, "/%s@%x", devname, dev);
116     if (fn)
117         p += snprintf(p, buf+max-p, ",%x", fn);
118     return p;
119 }
120
121 int bootprio_find_pci_device(struct pci_device *pci)
122 {
123     if (!CONFIG_BOOTORDER)
124         return -1;
125     // Find pci device - for example: /pci@i0cf8/ethernet@5
126     char desc[256];
127     build_pci_path(desc, sizeof(desc), "*", pci);
128     return find_prio(desc);
129 }
130
131 int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
132 {
133     if (!CONFIG_BOOTORDER)
134         return -1;
135     if (!pci)
136         // support only pci machine for now
137         return -1;
138     // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0
139     char desc[256], *p;
140     p = build_pci_path(desc, sizeof(desc), "*", pci);
141     snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave);
142     return find_prio(desc);
143 }
144
145 int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid)
146 {
147     if (!CONFIG_BOOTORDER)
148         return -1;
149     if (!pci)
150         // support only pci machine for now
151         return -1;
152     // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0
153     char desc[256], *p;
154     p = build_pci_path(desc, sizeof(desc), "isa", pci);
155     snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid);
156     return find_prio(desc);
157 }
158
159 int bootprio_find_pci_rom(struct pci_device *pci, int instance)
160 {
161     if (!CONFIG_BOOTORDER)
162         return -1;
163     // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2
164     char desc[256], *p;
165     p = build_pci_path(desc, sizeof(desc), "*", pci);
166     if (instance)
167         snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
168     return find_prio(desc);
169 }
170
171 int bootprio_find_named_rom(const char *name, int instance)
172 {
173     if (!CONFIG_BOOTORDER)
174         return -1;
175     // Find named rom - for example: /rom@genroms/linuxboot.bin
176     char desc[256], *p;
177     p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name);
178     if (instance)
179         snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
180     return find_prio(desc);
181 }
182
183 int bootprio_find_usb(struct pci_device *pci, u64 path)
184 {
185     if (!CONFIG_BOOTORDER)
186         return -1;
187     // Find usb - for example: /pci@i0cf8/usb@1,2/hub@1/network@0/ethernet@0
188     int i;
189     char desc[256], *p;
190     p = build_pci_path(desc, sizeof(desc), "usb", pci);
191     for (i=56; i>0; i-=8) {
192         int port = (path >> i) & 0xff;
193         if (port != 0xff)
194             p += snprintf(p, desc+sizeof(desc)-p, "/hub@%x", port+1);
195     }
196     snprintf(p, desc+sizeof(desc)-p, "/*@%x", (u32)(path & 0xff)+1);
197     return find_prio(desc);
198 }
199
200
201 /****************************************************************
202  * Boot setup
203  ****************************************************************/
204
205 static int CheckFloppySig = 1;
206
207 #define DEFAULT_PRIO           9999
208
209 static int DefaultFloppyPrio = 101;
210 static int DefaultCDPrio     = 102;
211 static int DefaultHDPrio     = 103;
212 static int DefaultBEVPrio    = 104;
213
214 void
215 boot_setup(void)
216 {
217     if (! CONFIG_BOOT)
218         return;
219
220     SET_EBDA(boot_sequence, 0xffff);
221
222     if (!CONFIG_COREBOOT) {
223         // On emulators, get boot order from nvram.
224         if (inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1)
225             CheckFloppySig = 0;
226         u32 bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
227                          | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
228         DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio
229             = DefaultBEVPrio = DEFAULT_PRIO;
230         int i;
231         for (i=101; i<104; i++) {
232             u32 val = bootorder & 0x0f;
233             bootorder >>= 4;
234             switch (val) {
235             case 1: DefaultFloppyPrio = i; break;
236             case 2: DefaultHDPrio = i;     break;
237             case 3: DefaultCDPrio = i;     break;
238             case 4: DefaultBEVPrio = i;    break;
239             }
240         }
241     }
242
243     loadBootOrder();
244 }
245
246
247 /****************************************************************
248  * BootList handling
249  ****************************************************************/
250
251 struct bootentry_s {
252     int type;
253     union {
254         u32 data;
255         struct segoff_s vector;
256         struct drive_s *drive;
257     };
258     int priority;
259     const char *description;
260     struct bootentry_s *next;
261 };
262 static struct bootentry_s *BootList;
263
264 #define IPL_TYPE_FLOPPY      0x01
265 #define IPL_TYPE_HARDDISK    0x02
266 #define IPL_TYPE_CDROM       0x03
267 #define IPL_TYPE_CBFS        0x20
268 #define IPL_TYPE_BEV         0x80
269 #define IPL_TYPE_BCV         0x81
270
271 static void
272 bootentry_add(int type, int prio, u32 data, const char *desc)
273 {
274     if (! CONFIG_BOOT)
275         return;
276     struct bootentry_s *be = malloc_tmp(sizeof(*be));
277     if (!be) {
278         warn_noalloc();
279         return;
280     }
281     be->type = type;
282     be->priority = prio;
283     be->data = data;
284     be->description = desc ?: "?";
285     dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n"
286             , be->description, type, prio, data);
287
288     // Add entry in sorted order.
289     struct bootentry_s **pprev;
290     for (pprev = &BootList; *pprev; pprev = &(*pprev)->next) {
291         struct bootentry_s *pos = *pprev;
292         if (be->priority < pos->priority)
293             break;
294         if (be->priority > pos->priority)
295             continue;
296         if (be->type < pos->type)
297             break;
298         if (be->type > pos->type)
299             continue;
300         if (be->type <= IPL_TYPE_CDROM
301             && (be->drive->type < pos->drive->type
302                 || (be->drive->type == pos->drive->type
303                     && be->drive->cntl_id < pos->drive->cntl_id)))
304             break;
305     }
306     be->next = *pprev;
307     *pprev = be;
308 }
309
310 // Return the given priority if it's set - defaultprio otherwise.
311 static inline int defPrio(int priority, int defaultprio) {
312     return (priority < 0) ? defaultprio : priority;
313 }
314
315 // Add a BEV vector for a given pnp compatible option rom.
316 void
317 boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
318 {
319     bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
320                   , SEGOFF(seg, bev).segoff
321                   , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
322     DefaultBEVPrio = DEFAULT_PRIO;
323 }
324
325 // Add a bcv entry for an expansion card harddrive or legacy option rom
326 void
327 boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
328 {
329     bootentry_add(IPL_TYPE_BCV, defPrio(prio, DefaultHDPrio)
330                   , SEGOFF(seg, ip).segoff
331                   , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
332 }
333
334 void
335 boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio)
336 {
337     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
338                   , (u32)drive_g, desc);
339 }
340
341 void
342 boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
343 {
344     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
345                   , (u32)drive_g, desc);
346 }
347
348 void
349 boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
350 {
351     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
352                   , (u32)drive_g, desc);
353 }
354
355 // Add a CBFS payload entry
356 void
357 boot_add_cbfs(void *data, const char *desc, int prio)
358 {
359     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
360 }
361
362
363 /****************************************************************
364  * Boot menu and BCV execution
365  ****************************************************************/
366
367 #define DEFAULT_BOOTMENU_WAIT 2500
368
369 // Show IPL option menu.
370 static void
371 interactive_bootmenu(void)
372 {
373     if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
374         return;
375
376     while (get_keystroke(0) >= 0)
377         ;
378
379     printf("Press F12 for boot menu.\n\n");
380
381     u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
382     enable_bootsplash();
383         if (1 == 1) {
384                 return;
385         }
386     int scan_code = get_keystroke(menutime);
387     disable_bootsplash();
388     if (scan_code != 0x86)
389         /* not F12 */
390         return;
391
392     while (get_keystroke(0) >= 0)
393         ;
394
395     printf("Select boot device:\n\n");
396     wait_threads();
397
398     // Show menu items
399     struct bootentry_s *pos = BootList;
400     int maxmenu = 0;
401     while (pos) {
402         char desc[60];
403         maxmenu++;
404         printf("%d. %s\n", maxmenu
405                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
406         pos = pos->next;
407     }
408
409     // Get key press
410     for (;;) {
411         scan_code = get_keystroke(1000);
412         if (scan_code >= 1 && scan_code <= maxmenu+1)
413             break;
414     }
415     printf("\n");
416     if (scan_code == 0x01)
417         // ESC
418         return;
419
420     // Find entry and make top priority.
421     int choice = scan_code - 1;
422     struct bootentry_s **pprev = &BootList;
423     while (--choice)
424         pprev = &(*pprev)->next;
425     pos = *pprev;
426     *pprev = pos->next;
427     pos->next = BootList;
428     BootList = pos;
429     pos->priority = 0;
430 }
431
432 // BEV (Boot Execution Vector) list
433 struct bev_s {
434     int type;
435     u32 vector;
436 };
437 static struct bev_s BEV[20];
438 static int BEVCount;
439 static int HaveHDBoot, HaveFDBoot;
440
441 static void
442 add_bev(int type, u32 vector)
443 {
444     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
445         return;
446     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
447         return;
448     if (BEVCount >= ARRAY_SIZE(BEV))
449         return;
450     struct bev_s *bev = &BEV[BEVCount++];
451     bev->type = type;
452     bev->vector = vector;
453 }
454
455 // Prepare for boot - show menu and run bcvs.
456 void
457 boot_prep(void)
458 {
459     if (! CONFIG_BOOT) {
460         wait_threads();
461         return;
462     }
463
464     // XXX - show available drives?
465
466     // Allow user to modify BCV/IPL order.
467         dprintf(3, "[wurm] bp1\n");
468     interactive_bootmenu();
469         dprintf(3, "[wurm] bp2\n");
470     wait_threads();
471
472     // Map drives and populate BEV list
473     struct bootentry_s *pos = BootList;
474         dprintf(3, "[wurm] bp3\n");
475     while (pos) {
476         switch (pos->type) {
477         case IPL_TYPE_BCV:
478             call_bcv(pos->vector.seg, pos->vector.offset);
479             add_bev(IPL_TYPE_HARDDISK, 0);
480             break;
481         case IPL_TYPE_FLOPPY:
482             map_floppy_drive(pos->drive);
483             add_bev(IPL_TYPE_FLOPPY, 0);
484             break;
485         case IPL_TYPE_HARDDISK:
486             map_hd_drive(pos->drive);
487             add_bev(IPL_TYPE_HARDDISK, 0);
488             break;
489         case IPL_TYPE_CDROM:
490             map_cd_drive(pos->drive);
491             // NO BREAK
492         default:
493             add_bev(pos->type, pos->data);
494             break;
495         }
496         pos = pos->next;
497     }
498         dprintf(3, "[wurm] bp4\n");
499
500     // If nothing added a floppy/hd boot - add it manually.
501     add_bev(IPL_TYPE_FLOPPY, 0);
502     add_bev(IPL_TYPE_HARDDISK, 0);
503         dprintf(3, "[wurm] bp5\n");
504 }
505
506
507 /****************************************************************
508  * Boot code (int 18/19)
509  ****************************************************************/
510
511 // Jump to a bootup entry point.
512 static void
513 call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
514 {
515     dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
516     struct bregs br;
517     memset(&br, 0, sizeof(br));
518     br.flags = F_IF;
519     br.code = bootsegip;
520     // Set the magic number in ax and the boot drive in dl.
521     br.dl = bootdrv;
522     br.ax = 0xaa55;
523     call16(&br);
524 }
525
526 // Boot from a disk (either floppy or harddrive)
527 static void
528 boot_disk(u8 bootdrv, int checksig)
529 {
530     u16 bootseg = 0x07c0;
531
532     // Read sector
533     struct bregs br;
534     memset(&br, 0, sizeof(br));
535     br.flags = F_IF;
536     br.dl = bootdrv;
537     br.es = bootseg;
538     br.ah = 2;
539     br.al = 1;
540     br.cl = 1;
541     call16_int(0x13, &br);
542
543     if (br.flags & F_CF) {
544         printf("Boot failed: could not read the boot disk\n\n");
545         return;
546     }
547
548     if (checksig) {
549         struct mbr_s *mbr = (void*)0;
550         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
551             printf("Boot failed: not a bootable disk\n\n");
552             return;
553         }
554     }
555
556     /* Canonicalize bootseg:bootip */
557     u16 bootip = (bootseg & 0x0fff) << 4;
558     bootseg &= 0xf000;
559
560     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
561 }
562
563 // Boot from a CD-ROM
564 static void
565 boot_cdrom(struct drive_s *drive_g)
566 {
567     if (! CONFIG_CDROM_BOOT)
568         return;
569     printf("Booting from DVD/CD...\n");
570
571     int status = cdrom_boot(drive_g);
572     if (status) {
573         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
574         return;
575     }
576
577     u16 ebda_seg = get_ebda_seg();
578     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
579     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
580     /* Canonicalize bootseg:bootip */
581     u16 bootip = (bootseg & 0x0fff) << 4;
582     bootseg &= 0xf000;
583
584     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
585 }
586
587 // Boot from a CBFS payload
588 static void
589 boot_cbfs(struct cbfs_file *file)
590 {
591     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
592         return;
593     printf("Booting from CBFS...\n");
594     cbfs_run_payload(file);
595 }
596
597 // Boot from a BEV entry on an optionrom.
598 static void
599 boot_rom(u32 vector)
600 {
601     printf("Booting from ROM...\n");
602     struct segoff_s so;
603     so.segoff = vector;
604     call_boot_entry(so, 0);
605 }
606
607 // Determine next boot method and attempt a boot using it.
608 static void
609 do_boot(u16 seq_nr)
610 {
611     if (! CONFIG_BOOT)
612         panic("Boot support not compiled in.\n");
613
614     if (seq_nr >= BEVCount) {
615         printf("No bootable device.\n");
616         // Loop with irqs enabled - this allows ctrl+alt+delete to work.
617         for (;;)
618             wait_irq();
619     }
620
621     // Boot the given BEV type.
622     struct bev_s *ie = &BEV[seq_nr];
623     switch (ie->type) {
624     case IPL_TYPE_FLOPPY:
625         printf("Booting from Floppy...\n");
626         boot_disk(0x00, CheckFloppySig);
627         break;
628     case IPL_TYPE_HARDDISK:
629         printf("Booting from Hard Disk...\n");
630         boot_disk(0x80, 1);
631         break;
632     case IPL_TYPE_CDROM:
633         boot_cdrom((void*)ie->vector);
634         break;
635     case IPL_TYPE_CBFS:
636         boot_cbfs((void*)ie->vector);
637         break;
638     case IPL_TYPE_BEV:
639         boot_rom(ie->vector);
640         break;
641     }
642
643     // Boot failed: invoke the boot recovery function
644     struct bregs br;
645     memset(&br, 0, sizeof(br));
646     br.flags = F_IF;
647     call16_int(0x18, &br);
648 }
649
650 // Boot Failure recovery: try the next device.
651 void VISIBLE32FLAT
652 handle_18(void)
653 {
654     debug_serial_setup();
655     debug_enter(NULL, DEBUG_HDL_18);
656     u16 ebda_seg = get_ebda_seg();
657     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
658     SET_EBDA2(ebda_seg, boot_sequence, seq);
659     do_boot(seq);
660 }
661
662 // INT 19h Boot Load Service Entry Point
663 void VISIBLE32FLAT
664 handle_19(void)
665 {
666     debug_serial_setup();
667     debug_enter(NULL, DEBUG_HDL_19);
668     SET_EBDA(boot_sequence, 0);
669     do_boot(0);
670 }