1a2218ea7a221c611ac3d4cdc41b3b3245d8006d
[external/qemu.git] / roms / seabios / 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 "config.h" // CONFIG_*
10 #include "disk.h" // cdrom_boot
11 #include "bregs.h" // struct bregs
12 #include "boot.h" // func defs
13 #include "cmos.h" // inb_cmos
14 #include "paravirt.h" // qemu_cfg_show_boot_menu
15 #include "pci.h" // pci_bdf_to_*
16 #include "usb.h" // struct usbdevice_s
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_scsi_device(struct pci_device *pci, int target, int lun)
132 {
133     if (!CONFIG_BOOTORDER)
134         return -1;
135     if (!pci)
136         // support only pci machine for now
137         return -1;
138     // Find scsi drive - for example: /pci@i0cf8/scsi@5/channel@0/disk@1,0
139     char desc[256], *p;
140     p = build_pci_path(desc, sizeof(desc), "*", pci);
141     snprintf(p, desc+sizeof(desc)-p, "/*@0/*@%d,%d", target, lun);
142     return find_prio(desc);
143 }
144
145 int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
146 {
147     if (!CONFIG_BOOTORDER)
148         return -1;
149     if (!pci)
150         // support only pci machine for now
151         return -1;
152     // Find ata drive - for example: /pci@i0cf8/ide@1,1/drive@1/disk@0
153     char desc[256], *p;
154     p = build_pci_path(desc, sizeof(desc), "*", pci);
155     snprintf(p, desc+sizeof(desc)-p, "/drive@%x/disk@%x", chanid, slave);
156     return find_prio(desc);
157 }
158
159 int bootprio_find_fdc_device(struct pci_device *pci, int port, int fdid)
160 {
161     if (!CONFIG_BOOTORDER)
162         return -1;
163     if (!pci)
164         // support only pci machine for now
165         return -1;
166     // Find floppy - for example: /pci@i0cf8/isa@1/fdc@03f1/floppy@0
167     char desc[256], *p;
168     p = build_pci_path(desc, sizeof(desc), "isa", pci);
169     snprintf(p, desc+sizeof(desc)-p, "/fdc@%04x/floppy@%x", port, fdid);
170     return find_prio(desc);
171 }
172
173 int bootprio_find_pci_rom(struct pci_device *pci, int instance)
174 {
175     if (!CONFIG_BOOTORDER)
176         return -1;
177     // Find pci rom - for example: /pci@i0cf8/scsi@3:rom2
178     char desc[256], *p;
179     p = build_pci_path(desc, sizeof(desc), "*", pci);
180     if (instance)
181         snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
182     return find_prio(desc);
183 }
184
185 int bootprio_find_named_rom(const char *name, int instance)
186 {
187     if (!CONFIG_BOOTORDER)
188         return -1;
189     // Find named rom - for example: /rom@genroms/linuxboot.bin
190     char desc[256], *p;
191     p = desc + snprintf(desc, sizeof(desc), "/rom@%s", name);
192     if (instance)
193         snprintf(p, desc+sizeof(desc)-p, ":rom%d", instance);
194     return find_prio(desc);
195 }
196
197 static char *
198 build_usb_path(char *buf, int max, struct usbhub_s *hub)
199 {
200     if (!hub->usbdev)
201         // Root hub - nothing to add.
202         return buf;
203     char *p = build_usb_path(buf, max, hub->usbdev->hub);
204     p += snprintf(p, buf+max-p, "/hub@%x", hub->usbdev->port+1);
205     return p;
206 }
207
208 int bootprio_find_usb(struct usbdevice_s *usbdev, int lun)
209 {
210     if (!CONFIG_BOOTORDER)
211         return -1;
212     // Find usb - for example: /pci@i0cf8/usb@1,2/storage@1/channel@0/disk@0,0
213     char desc[256], *p;
214     p = build_pci_path(desc, sizeof(desc), "usb", usbdev->hub->cntl->pci);
215     p = build_usb_path(p, desc+sizeof(desc)-p, usbdev->hub);
216     snprintf(p, desc+sizeof(desc)-p, "/storage@%x/*@0/*@0,%d"
217              , usbdev->port+1, lun);
218     int ret = find_prio(desc);
219     if (ret >= 0)
220         return ret;
221     // Try usb-host/redir - for example: /pci@i0cf8/usb@1,2/usb-host@1
222     snprintf(p, desc+sizeof(desc)-p, "/usb-*@%x", usbdev->port+1);
223     return find_prio(desc);
224 }
225
226
227 /****************************************************************
228  * Boot setup
229  ****************************************************************/
230
231 static int BootRetryTime;
232 static int CheckFloppySig = 1;
233
234 #define DEFAULT_PRIO           9999
235
236 static int DefaultFloppyPrio = 101;
237 static int DefaultCDPrio     = 102;
238 static int DefaultHDPrio     = 103;
239 static int DefaultBEVPrio    = 104;
240
241 void
242 boot_setup(void)
243 {
244     if (! CONFIG_BOOT)
245         return;
246
247     if (!CONFIG_COREBOOT) {
248         // On emulators, get boot order from nvram.
249         if (inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1)
250             CheckFloppySig = 0;
251         u32 bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
252                          | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
253         DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio
254             = DefaultBEVPrio = DEFAULT_PRIO;
255         int i;
256         for (i=101; i<104; i++) {
257             u32 val = bootorder & 0x0f;
258             bootorder >>= 4;
259             switch (val) {
260             case 1: DefaultFloppyPrio = i; break;
261             case 2: DefaultHDPrio = i;     break;
262             case 3: DefaultCDPrio = i;     break;
263             case 4: DefaultBEVPrio = i;    break;
264             }
265         }
266     }
267
268     BootRetryTime = romfile_loadint("etc/boot-fail-wait", 60*1000);
269
270     loadBootOrder();
271 }
272
273
274 /****************************************************************
275  * BootList handling
276  ****************************************************************/
277
278 struct bootentry_s {
279     int type;
280     union {
281         u32 data;
282         struct segoff_s vector;
283         struct drive_s *drive;
284     };
285     int priority;
286     const char *description;
287     struct bootentry_s *next;
288 };
289 static struct bootentry_s *BootList;
290
291 #define IPL_TYPE_FLOPPY      0x01
292 #define IPL_TYPE_HARDDISK    0x02
293 #define IPL_TYPE_CDROM       0x03
294 #define IPL_TYPE_CBFS        0x20
295 #define IPL_TYPE_BEV         0x80
296 #define IPL_TYPE_BCV         0x81
297 #define IPL_TYPE_HALT        0xf0
298
299 static void
300 bootentry_add(int type, int prio, u32 data, const char *desc)
301 {
302     if (! CONFIG_BOOT)
303         return;
304     struct bootentry_s *be = malloc_tmp(sizeof(*be));
305     if (!be) {
306         warn_noalloc();
307         return;
308     }
309     be->type = type;
310     be->priority = prio;
311     be->data = data;
312     be->description = desc ?: "?";
313     dprintf(3, "Registering bootable: %s (type:%d prio:%d data:%x)\n"
314             , be->description, type, prio, data);
315
316     // Add entry in sorted order.
317     struct bootentry_s **pprev;
318     for (pprev = &BootList; *pprev; pprev = &(*pprev)->next) {
319         struct bootentry_s *pos = *pprev;
320         if (be->priority < pos->priority)
321             break;
322         if (be->priority > pos->priority)
323             continue;
324         if (be->type < pos->type)
325             break;
326         if (be->type > pos->type)
327             continue;
328         if (be->type <= IPL_TYPE_CDROM
329             && (be->drive->type < pos->drive->type
330                 || (be->drive->type == pos->drive->type
331                     && be->drive->cntl_id < pos->drive->cntl_id)))
332             break;
333     }
334     be->next = *pprev;
335     *pprev = be;
336 }
337
338 // Return the given priority if it's set - defaultprio otherwise.
339 static inline int defPrio(int priority, int defaultprio) {
340     return (priority < 0) ? defaultprio : priority;
341 }
342
343 // Add a BEV vector for a given pnp compatible option rom.
344 void
345 boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
346 {
347     bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
348                   , SEGOFF(seg, bev).segoff
349                   , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
350     DefaultBEVPrio = DEFAULT_PRIO;
351 }
352
353 // Add a bcv entry for an expansion card harddrive or legacy option rom
354 void
355 boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
356 {
357     bootentry_add(IPL_TYPE_BCV, defPrio(prio, DefaultHDPrio)
358                   , SEGOFF(seg, ip).segoff
359                   , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
360 }
361
362 void
363 boot_add_floppy(struct drive_s *drive_g, const char *desc, int prio)
364 {
365     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
366                   , (u32)drive_g, desc);
367 }
368
369 void
370 boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
371 {
372     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
373                   , (u32)drive_g, desc);
374 }
375
376 void
377 boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
378 {
379     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
380                   , (u32)drive_g, desc);
381 }
382
383 // Add a CBFS payload entry
384 void
385 boot_add_cbfs(void *data, const char *desc, int prio)
386 {
387     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
388 }
389
390
391 /****************************************************************
392  * Boot menu and BCV execution
393  ****************************************************************/
394
395 #define DEFAULT_BOOTMENU_WAIT 2500
396
397 // Show IPL option menu.
398 static void
399 interactive_bootmenu(void)
400 {
401     if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
402         return;
403
404     while (get_keystroke(0) >= 0)
405         ;
406
407     printf("\nPress F12 for boot menu.\n\n");
408
409     u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
410     enable_bootsplash();
411     int scan_code = get_keystroke(menutime);
412     disable_bootsplash();
413     if (scan_code != 0x86)
414         /* not F12 */
415         return;
416
417     while (get_keystroke(0) >= 0)
418         ;
419
420     printf("Select boot device:\n\n");
421     wait_threads();
422
423     // Show menu items
424     struct bootentry_s *pos = BootList;
425     int maxmenu = 0;
426     while (pos) {
427         char desc[60];
428         maxmenu++;
429         printf("%d. %s\n", maxmenu
430                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
431         pos = pos->next;
432     }
433
434     // Get key press
435     for (;;) {
436         scan_code = get_keystroke(1000);
437         if (scan_code >= 1 && scan_code <= maxmenu+1)
438             break;
439     }
440     printf("\n");
441     if (scan_code == 0x01)
442         // ESC
443         return;
444
445     // Find entry and make top priority.
446     int choice = scan_code - 1;
447     struct bootentry_s **pprev = &BootList;
448     while (--choice)
449         pprev = &(*pprev)->next;
450     pos = *pprev;
451     *pprev = pos->next;
452     pos->next = BootList;
453     BootList = pos;
454     pos->priority = 0;
455 }
456
457 // BEV (Boot Execution Vector) list
458 struct bev_s {
459     int type;
460     u32 vector;
461 };
462 static struct bev_s BEV[20];
463 static int BEVCount;
464 static int HaveHDBoot, HaveFDBoot;
465
466 static void
467 add_bev(int type, u32 vector)
468 {
469     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
470         return;
471     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
472         return;
473     if (BEVCount >= ARRAY_SIZE(BEV))
474         return;
475     struct bev_s *bev = &BEV[BEVCount++];
476     bev->type = type;
477     bev->vector = vector;
478 }
479
480 // Prepare for boot - show menu and run bcvs.
481 void
482 boot_prep(void)
483 {
484     if (! CONFIG_BOOT) {
485         wait_threads();
486         return;
487     }
488
489     // XXX - show available drives?
490
491     // Allow user to modify BCV/IPL order.
492     interactive_bootmenu();
493     wait_threads();
494
495     int haltprio = find_prio("HALT");
496     if (haltprio >= 0)
497         bootentry_add(IPL_TYPE_HALT, haltprio, 0, "HALT");
498
499     // Map drives and populate BEV list
500     struct bootentry_s *pos = BootList;
501     while (pos) {
502         switch (pos->type) {
503         case IPL_TYPE_BCV:
504             call_bcv(pos->vector.seg, pos->vector.offset);
505             add_bev(IPL_TYPE_HARDDISK, 0);
506             break;
507         case IPL_TYPE_FLOPPY:
508             map_floppy_drive(pos->drive);
509             add_bev(IPL_TYPE_FLOPPY, 0);
510             break;
511         case IPL_TYPE_HARDDISK:
512             map_hd_drive(pos->drive);
513             add_bev(IPL_TYPE_HARDDISK, 0);
514             break;
515         case IPL_TYPE_CDROM:
516             map_cd_drive(pos->drive);
517             // NO BREAK
518         default:
519             add_bev(pos->type, pos->data);
520             break;
521         }
522         pos = pos->next;
523     }
524
525     // If nothing added a floppy/hd boot - add it manually.
526     add_bev(IPL_TYPE_FLOPPY, 0);
527     add_bev(IPL_TYPE_HARDDISK, 0);
528 }
529
530
531 /****************************************************************
532  * Boot code (int 18/19)
533  ****************************************************************/
534
535 // Jump to a bootup entry point.
536 static void
537 call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
538 {
539     dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
540     struct bregs br;
541     memset(&br, 0, sizeof(br));
542     br.flags = F_IF;
543     br.code = bootsegip;
544     // Set the magic number in ax and the boot drive in dl.
545     br.dl = bootdrv;
546     br.ax = 0xaa55;
547     farcall16(&br);
548 }
549
550 // Boot from a disk (either floppy or harddrive)
551 static void
552 boot_disk(u8 bootdrv, int checksig)
553 {
554     u16 bootseg = 0x07c0;
555
556     // Read sector
557     struct bregs br;
558     memset(&br, 0, sizeof(br));
559     br.flags = F_IF;
560     br.dl = bootdrv;
561     br.es = bootseg;
562     br.ah = 2;
563     br.al = 1;
564     br.cl = 1;
565     call16_int(0x13, &br);
566
567     if (br.flags & F_CF) {
568         printf("Boot failed: could not read the boot disk\n\n");
569         return;
570     }
571
572     if (checksig) {
573         struct mbr_s *mbr = (void*)0;
574         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
575             printf("Boot failed: not a bootable disk\n\n");
576             return;
577         }
578     }
579
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 CD-ROM
588 static void
589 boot_cdrom(struct drive_s *drive_g)
590 {
591     if (! CONFIG_CDROM_BOOT)
592         return;
593     printf("Booting from DVD/CD...\n");
594
595     int status = cdrom_boot(drive_g);
596     if (status) {
597         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
598         return;
599     }
600
601     u8 bootdrv = CDEmu.emulated_extdrive;
602     u16 bootseg = CDEmu.load_segment;
603     /* Canonicalize bootseg:bootip */
604     u16 bootip = (bootseg & 0x0fff) << 4;
605     bootseg &= 0xf000;
606
607     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
608 }
609
610 // Boot from a CBFS payload
611 static void
612 boot_cbfs(struct cbfs_file *file)
613 {
614     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
615         return;
616     printf("Booting from CBFS...\n");
617     cbfs_run_payload(file);
618 }
619
620 // Boot from a BEV entry on an optionrom.
621 static void
622 boot_rom(u32 vector)
623 {
624     printf("Booting from ROM...\n");
625     struct segoff_s so;
626     so.segoff = vector;
627     call_boot_entry(so, 0);
628 }
629
630 // Unable to find bootable device - warn user and eventually retry.
631 static void
632 boot_fail(void)
633 {
634     if (BootRetryTime == (u32)-1)
635         printf("No bootable device.\n");
636     else
637         printf("No bootable device.  Retrying in %d seconds.\n"
638                , BootRetryTime/1000);
639     // Wait for 'BootRetryTime' milliseconds and then reboot.
640     u32 end = calc_future_timer(BootRetryTime);
641     for (;;) {
642         if (BootRetryTime != (u32)-1 && check_timer(end))
643             break;
644         yield_toirq();
645     }
646     printf("Rebooting.\n");
647     struct bregs br;
648     memset(&br, 0, sizeof(br));
649     br.code = SEGOFF(SEG_BIOS, (u32)reset_vector);
650     farcall16big(&br);
651 }
652
653 // Determine next boot method and attempt a boot using it.
654 static void
655 do_boot(int seq_nr)
656 {
657     if (! CONFIG_BOOT)
658         panic("Boot support not compiled in.\n");
659
660     if (seq_nr >= BEVCount)
661         boot_fail();
662
663     // Boot the given BEV type.
664     struct bev_s *ie = &BEV[seq_nr];
665     switch (ie->type) {
666     case IPL_TYPE_FLOPPY:
667         printf("Booting from Floppy...\n");
668         boot_disk(0x00, CheckFloppySig);
669         break;
670     case IPL_TYPE_HARDDISK:
671         printf("Booting from Hard Disk...\n");
672         boot_disk(0x80, 1);
673         break;
674     case IPL_TYPE_CDROM:
675         boot_cdrom((void*)ie->vector);
676         break;
677     case IPL_TYPE_CBFS:
678         boot_cbfs((void*)ie->vector);
679         break;
680     case IPL_TYPE_BEV:
681         boot_rom(ie->vector);
682         break;
683     case IPL_TYPE_HALT:
684         boot_fail();
685         break;
686     }
687
688     // Boot failed: invoke the boot recovery function
689     struct bregs br;
690     memset(&br, 0, sizeof(br));
691     br.flags = F_IF;
692     call16_int(0x18, &br);
693 }
694
695 int BootSequence VARLOW = -1;
696
697 // Boot Failure recovery: try the next device.
698 void VISIBLE32FLAT
699 handle_18(void)
700 {
701     debug_serial_setup();
702     debug_enter(NULL, DEBUG_HDL_18);
703     int seq = BootSequence + 1;
704     BootSequence = seq;
705     do_boot(seq);
706 }
707
708 // INT 19h Boot Load Service Entry Point
709 void VISIBLE32FLAT
710 handle_19(void)
711 {
712     debug_serial_setup();
713     debug_enter(NULL, DEBUG_HDL_19);
714     BootSequence = 0;
715     do_boot(0);
716 }