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