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