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