Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


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