Push use of 'struct pci_device' to bootprio_find_ata_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(struct pci_device *pci)
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), "*", pci);
141     return find_prio(desc);
142 }
143
144 int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave)
145 {
146     if (!CONFIG_BOOTORDER)
147         return -1;
148     if (!pci)
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), "*", pci);
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(struct pci_device *pci, int port, int fdid)
159 {
160     if (!CONFIG_BOOTORDER)
161         return -1;
162     if (!pci)
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", pci);
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(struct pci_device *pci, 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", pci);
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 #define DEFAULT_BOOTMENU_WAIT 2500
381
382 // Show IPL option menu.
383 static void
384 interactive_bootmenu(void)
385 {
386     if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
387         return;
388
389     while (get_keystroke(0) >= 0)
390         ;
391
392     printf("Press F12 for boot menu.\n\n");
393
394     u32 menutime = romfile_loadint("etc/boot-menu-wait", DEFAULT_BOOTMENU_WAIT);
395     enable_bootsplash();
396     int scan_code = get_keystroke(menutime);
397     disable_bootsplash();
398     if (scan_code != 0x86)
399         /* not F12 */
400         return;
401
402     while (get_keystroke(0) >= 0)
403         ;
404
405     printf("Select boot device:\n\n");
406     wait_threads();
407
408     // Show menu items
409     struct bootentry_s *pos = BootList;
410     int maxmenu = 0;
411     while (pos) {
412         char desc[60];
413         maxmenu++;
414         printf("%d. %s\n", maxmenu
415                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
416         pos = pos->next;
417     }
418
419     // Get key press
420     for (;;) {
421         scan_code = get_keystroke(1000);
422         if (scan_code >= 1 && scan_code <= maxmenu+1)
423             break;
424     }
425     printf("\n");
426     if (scan_code == 0x01)
427         // ESC
428         return;
429
430     // Find entry and make top priority.
431     int choice = scan_code - 1;
432     struct bootentry_s **pprev = &BootList;
433     while (--choice)
434         pprev = &(*pprev)->next;
435     pos = *pprev;
436     *pprev = pos->next;
437     pos->next = BootList;
438     BootList = pos;
439     pos->priority = 0;
440 }
441
442 // BEV (Boot Execution Vector) list
443 struct bev_s {
444     int type;
445     u32 vector;
446 };
447 static struct bev_s BEV[20];
448 static int BEVCount;
449 static int HaveHDBoot, HaveFDBoot;
450
451 static void
452 add_bev(int type, u32 vector)
453 {
454     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
455         return;
456     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
457         return;
458     if (BEVCount >= ARRAY_SIZE(BEV))
459         return;
460     struct bev_s *bev = &BEV[BEVCount++];
461     bev->type = type;
462     bev->vector = vector;
463 }
464
465 // Prepare for boot - show menu and run bcvs.
466 void
467 boot_prep(void)
468 {
469     if (! CONFIG_BOOT) {
470         wait_threads();
471         return;
472     }
473
474     // XXX - show available drives?
475
476     // Allow user to modify BCV/IPL order.
477     interactive_bootmenu();
478     wait_threads();
479
480     // Map drives and populate BEV list
481     struct bootentry_s *pos = BootList;
482     while (pos) {
483         switch (pos->type) {
484         case IPL_TYPE_BCV:
485             call_bcv(pos->vector.seg, pos->vector.offset);
486             add_bev(IPL_TYPE_HARDDISK, 0);
487             break;
488         case IPL_TYPE_FLOPPY:
489             map_floppy_drive(pos->drive);
490             add_bev(IPL_TYPE_FLOPPY, 0);
491             break;
492         case IPL_TYPE_HARDDISK:
493             map_hd_drive(pos->drive);
494             add_bev(IPL_TYPE_HARDDISK, 0);
495             break;
496         case IPL_TYPE_CDROM:
497             map_cd_drive(pos->drive);
498             // NO BREAK
499         default:
500             add_bev(pos->type, pos->data);
501             break;
502         }
503         pos = pos->next;
504     }
505
506     // If nothing added a floppy/hd boot - add it manually.
507     add_bev(IPL_TYPE_FLOPPY, 0);
508     add_bev(IPL_TYPE_HARDDISK, 0);
509 }
510
511
512 /****************************************************************
513  * Boot code (int 18/19)
514  ****************************************************************/
515
516 // Jump to a bootup entry point.
517 static void
518 call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
519 {
520     dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
521     struct bregs br;
522     memset(&br, 0, sizeof(br));
523     br.flags = F_IF;
524     br.code = bootsegip;
525     // Set the magic number in ax and the boot drive in dl.
526     br.dl = bootdrv;
527     br.ax = 0xaa55;
528     call16(&br);
529 }
530
531 // Boot from a disk (either floppy or harddrive)
532 static void
533 boot_disk(u8 bootdrv, int checksig)
534 {
535     u16 bootseg = 0x07c0;
536
537     // Read sector
538     struct bregs br;
539     memset(&br, 0, sizeof(br));
540     br.flags = F_IF;
541     br.dl = bootdrv;
542     br.es = bootseg;
543     br.ah = 2;
544     br.al = 1;
545     br.cl = 1;
546     call16_int(0x13, &br);
547
548     if (br.flags & F_CF) {
549         printf("Boot failed: could not read the boot disk\n\n");
550         return;
551     }
552
553     if (checksig) {
554         struct mbr_s *mbr = (void*)0;
555         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
556             printf("Boot failed: not a bootable disk\n\n");
557             return;
558         }
559     }
560
561     /* Canonicalize bootseg:bootip */
562     u16 bootip = (bootseg & 0x0fff) << 4;
563     bootseg &= 0xf000;
564
565     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
566 }
567
568 // Boot from a CD-ROM
569 static void
570 boot_cdrom(struct drive_s *drive_g)
571 {
572     if (! CONFIG_CDROM_BOOT)
573         return;
574     printf("Booting from DVD/CD...\n");
575
576     int status = cdrom_boot(drive_g);
577     if (status) {
578         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
579         return;
580     }
581
582     u16 ebda_seg = get_ebda_seg();
583     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
584     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
585     /* Canonicalize bootseg:bootip */
586     u16 bootip = (bootseg & 0x0fff) << 4;
587     bootseg &= 0xf000;
588
589     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
590 }
591
592 // Boot from a CBFS payload
593 static void
594 boot_cbfs(struct cbfs_file *file)
595 {
596     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
597         return;
598     printf("Booting from CBFS...\n");
599     cbfs_run_payload(file);
600 }
601
602 // Boot from a BEV entry on an optionrom.
603 static void
604 boot_rom(u32 vector)
605 {
606     printf("Booting from ROM...\n");
607     struct segoff_s so;
608     so.segoff = vector;
609     call_boot_entry(so, 0);
610 }
611
612 // Determine next boot method and attempt a boot using it.
613 static void
614 do_boot(u16 seq_nr)
615 {
616     if (! CONFIG_BOOT)
617         panic("Boot support not compiled in.\n");
618
619     if (seq_nr >= BEVCount) {
620         printf("No bootable device.\n");
621         // Loop with irqs enabled - this allows ctrl+alt+delete to work.
622         for (;;)
623             wait_irq();
624     }
625
626     // Boot the given BEV type.
627     struct bev_s *ie = &BEV[seq_nr];
628     switch (ie->type) {
629     case IPL_TYPE_FLOPPY:
630         printf("Booting from Floppy...\n");
631         boot_disk(0x00, CheckFloppySig);
632         break;
633     case IPL_TYPE_HARDDISK:
634         printf("Booting from Hard Disk...\n");
635         boot_disk(0x80, 1);
636         break;
637     case IPL_TYPE_CDROM:
638         boot_cdrom((void*)ie->vector);
639         break;
640     case IPL_TYPE_CBFS:
641         boot_cbfs((void*)ie->vector);
642         break;
643     case IPL_TYPE_BEV:
644         boot_rom(ie->vector);
645         break;
646     }
647
648     // Boot failed: invoke the boot recovery function
649     struct bregs br;
650     memset(&br, 0, sizeof(br));
651     br.flags = F_IF;
652     call16_int(0x18, &br);
653 }
654
655 // Boot Failure recovery: try the next device.
656 void VISIBLE32FLAT
657 handle_18(void)
658 {
659     debug_serial_setup();
660     debug_enter(NULL, DEBUG_HDL_18);
661     u16 ebda_seg = get_ebda_seg();
662     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
663     SET_EBDA2(ebda_seg, boot_sequence, seq);
664     do_boot(seq);
665 }
666
667 // INT 19h Boot Load Service Entry Point
668 void VISIBLE32FLAT
669 handle_19(void)
670 {
671     debug_serial_setup();
672     debug_enter(NULL, DEBUG_HDL_19);
673     SET_EBDA(boot_sequence, 0);
674     do_boot(0);
675 }