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