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