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