Move IPL.fw_bootorder to static variables in boot.c.
[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" // struct ipl_s
14 #include "cmos.h" // inb_cmos
15 #include "paravirt.h"
16
17
18 /****************************************************************
19  * Boot priority ordering
20  ****************************************************************/
21
22 static char **Bootorder;
23 static int BootorderCount;
24
25 static void
26 loadBootOrder(void)
27 {
28     char *f = romfile_loadfile("bootorder", NULL);
29     if (!f)
30         return;
31
32     int i;
33     BootorderCount = 1;
34     while (f[i]) {
35         if (f[i] == '\n')
36             BootorderCount++;
37         i++;
38     }
39     Bootorder = malloc_tmphigh(BootorderCount*sizeof(char*));
40     if (!Bootorder) {
41         warn_noalloc();
42         free(f);
43         return;
44     }
45
46     dprintf(3, "boot order:\n");
47     i = 0;
48     do {
49         Bootorder[i] = f;
50         f = strchr(f, '\n');
51         if (f) {
52             *f = '\0';
53             f++;
54             dprintf(3, "%d: %s\n", i, Bootorder[i]);
55             i++;
56         }
57     } while(f);
58 }
59
60 int bootprio_find_pci_device(int bdf)
61 {
62     return -1;
63 }
64
65 int bootprio_find_ata_device(int bdf, int chanid, int slave)
66 {
67     return -1;
68 }
69
70 int bootprio_find_fdc_device(int bfd, int port, int fdid)
71 {
72     return -1;
73 }
74
75 int bootprio_find_pci_rom(int bdf, int instance)
76 {
77     return -1;
78 }
79
80 int bootprio_find_named_rom(const char *name, int instance)
81 {
82     return -1;
83 }
84
85
86 /****************************************************************
87  * Boot setup
88  ****************************************************************/
89
90 static int CheckFloppySig = 1;
91
92 #define DEFAULT_PRIO           9999
93
94 static int DefaultFloppyPrio = 101;
95 static int DefaultCDPrio     = 102;
96 static int DefaultHDPrio     = 103;
97 static int DefaultBEVPrio    = 104;
98
99 void
100 boot_setup(void)
101 {
102     if (! CONFIG_BOOT)
103         return;
104
105     SET_EBDA(boot_sequence, 0xffff);
106
107     if (!CONFIG_COREBOOT) {
108         // On emulators, get boot order from nvram.
109         if (inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1)
110             CheckFloppySig = 0;
111         u32 bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
112                          | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
113         DefaultFloppyPrio = DefaultCDPrio = DefaultHDPrio
114             = DefaultBEVPrio = DEFAULT_PRIO;
115         int i;
116         for (i=101; i<104; i++) {
117             u32 val = bootorder & 0x0f;
118             bootorder >>= 4;
119             switch (val) {
120             case 1: DefaultFloppyPrio = i; break;
121             case 2: DefaultHDPrio = i;     break;
122             case 3: DefaultCDPrio = i;     break;
123             case 4: DefaultBEVPrio = i;    break;
124             }
125         }
126     }
127
128     loadBootOrder();
129 }
130
131
132 /****************************************************************
133  * BootList handling
134  ****************************************************************/
135
136 struct bootentry_s {
137     int type;
138     union {
139         u32 data;
140         struct segoff_s vector;
141         struct drive_s *drive;
142     };
143     int priority;
144     const char *description;
145     struct bootentry_s *next;
146 };
147 static struct bootentry_s *BootList;
148
149 #define IPL_TYPE_FLOPPY      0x01
150 #define IPL_TYPE_HARDDISK    0x02
151 #define IPL_TYPE_CDROM       0x03
152 #define IPL_TYPE_CBFS        0x20
153 #define IPL_TYPE_BEV         0x80
154 #define IPL_TYPE_BCV         0x81
155
156 static void
157 bootentry_add(int type, int prio, u32 data, const char *desc)
158 {
159     if (! CONFIG_BOOT)
160         return;
161     struct bootentry_s *be = malloc_tmp(sizeof(*be));
162     if (!be) {
163         warn_noalloc();
164         return;
165     }
166     be->type = type;
167     be->priority = prio;
168     be->data = data;
169     be->description = desc;
170
171     // Add entry in sorted order.
172     struct bootentry_s **pprev;
173     for (pprev = &BootList; *pprev; pprev = &(*pprev)->next) {
174         struct bootentry_s *pos = *pprev;
175         if (be->priority < pos->priority)
176             break;
177         if (be->priority > pos->priority)
178             continue;
179         if (be->type < pos->type)
180             break;
181         if (be->type > pos->type)
182             continue;
183         if (be->type <= IPL_TYPE_CDROM
184             && (be->drive->type < pos->drive->type
185                 || (be->drive->type == pos->drive->type
186                     && be->drive->cntl_id < pos->drive->cntl_id)))
187             break;
188     }
189     be->next = *pprev;
190     *pprev = be;
191 }
192
193 // Return the given priority if it's set - defaultprio otherwise.
194 static inline int defPrio(int priority, int defaultprio) {
195     return (priority < 0) ? defaultprio : priority;
196 }
197
198 // Add a BEV vector for a given pnp compatible option rom.
199 void
200 boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
201 {
202     bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
203                   , SEGOFF(seg, bev).segoff
204                   , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
205     DefaultBEVPrio = DEFAULT_PRIO;
206 }
207
208 // Add a bcv entry for an expansion card harddrive or legacy option rom
209 void
210 boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
211 {
212     bootentry_add(IPL_TYPE_BCV, defPrio(prio, DEFAULT_PRIO)
213                   , SEGOFF(seg, ip).segoff
214                   , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
215 }
216
217 void
218 boot_add_floppy(struct drive_s *drive_g, int prio)
219 {
220     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
221                   , (u32)drive_g, drive_g->desc);
222 }
223
224 void
225 boot_add_hd(struct drive_s *drive_g, int prio)
226 {
227     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
228                   , (u32)drive_g, drive_g->desc);
229 }
230
231 void
232 boot_add_cd(struct drive_s *drive_g, int prio)
233 {
234     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
235                   , (u32)drive_g, drive_g->desc);
236 }
237
238 // Add a CBFS payload entry
239 void
240 boot_add_cbfs(void *data, const char *desc, int prio)
241 {
242     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
243 }
244
245
246 /****************************************************************
247  * Boot menu and BCV execution
248  ****************************************************************/
249
250 // Show IPL option menu.
251 static void
252 interactive_bootmenu(void)
253 {
254     if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
255         return;
256
257     while (get_keystroke(0) >= 0)
258         ;
259
260     printf("Press F12 for boot menu.\n\n");
261
262     enable_bootsplash();
263     int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT);
264     disable_bootsplash();
265     if (scan_code != 0x86)
266         /* not F12 */
267         return;
268
269     while (get_keystroke(0) >= 0)
270         ;
271
272     printf("Select boot device:\n\n");
273     wait_threads();
274
275     // Show menu items
276     struct bootentry_s *pos = BootList;
277     int maxmenu = 0;
278     while (pos) {
279         char desc[60];
280         maxmenu++;
281         printf("%d. %s\n", maxmenu
282                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
283         pos = pos->next;
284     }
285
286     // Get key press
287     for (;;) {
288         scan_code = get_keystroke(1000);
289         if (scan_code >= 1 && scan_code <= maxmenu+1)
290             break;
291     }
292     printf("\n");
293     if (scan_code == 0x01)
294         // ESC
295         return;
296
297     // Find entry and make top priority.
298     int choice = scan_code - 1;
299     struct bootentry_s **pprev = &BootList;
300     while (--choice)
301         pprev = &(*pprev)->next;
302     pos = *pprev;
303     *pprev = pos->next;
304     pos->next = BootList;
305     BootList = pos;
306     pos->priority = 0;
307 }
308
309 struct bev_s {
310     int type;
311     u32 vector;
312 };
313 static struct bev_s BEV[20];
314 static int BEVCount;
315 static int HaveHDBoot, HaveFDBoot;
316
317 static void
318 add_bev(int type, u32 vector)
319 {
320     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
321         return;
322     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
323         return;
324     if (BEVCount >= ARRAY_SIZE(BEV))
325         return;
326     struct bev_s *bev = &BEV[BEVCount++];
327     bev->type = type;
328     bev->vector = vector;
329 }
330
331 // Prepare for boot - show menu and run bcvs.
332 void
333 boot_prep(void)
334 {
335     if (! CONFIG_BOOT) {
336         wait_threads();
337         return;
338     }
339
340     // XXX - show available drives?
341
342     // Allow user to modify BCV/IPL order.
343     interactive_bootmenu();
344     wait_threads();
345
346     // Map drives and populate BEV list
347     struct bootentry_s *pos = BootList;
348     while (pos) {
349         switch (pos->type) {
350         case IPL_TYPE_BCV:
351             call_bcv(pos->vector.seg, pos->vector.offset);
352             add_bev(IPL_TYPE_HARDDISK, 0);
353             break;
354         case IPL_TYPE_FLOPPY:
355             map_floppy_drive(pos->drive);
356             add_bev(IPL_TYPE_FLOPPY, 0);
357             break;
358         case IPL_TYPE_HARDDISK:
359             map_hd_drive(pos->drive);
360             add_bev(IPL_TYPE_HARDDISK, 0);
361             break;
362         case IPL_TYPE_CDROM:
363             map_cd_drive(pos->drive);
364             // NO BREAK
365         default:
366             add_bev(pos->type, pos->data);
367             break;
368         }
369         pos = pos->next;
370     }
371
372     // If nothing added a floppy/hd boot - add it manually.
373     add_bev(IPL_TYPE_FLOPPY, 0);
374     add_bev(IPL_TYPE_HARDDISK, 0);
375 }
376
377
378 /****************************************************************
379  * Boot code (int 18/19)
380  ****************************************************************/
381
382 // Jump to a bootup entry point.
383 static void
384 call_boot_entry(u16 bootseg, u16 bootip, u8 bootdrv)
385 {
386     dprintf(1, "Booting from %04x:%04x\n", bootseg, bootip);
387     struct bregs br;
388     memset(&br, 0, sizeof(br));
389     br.flags = F_IF;
390     br.code = SEGOFF(bootseg, bootip);
391     // Set the magic number in ax and the boot drive in dl.
392     br.dl = bootdrv;
393     br.ax = 0xaa55;
394     call16(&br);
395 }
396
397 // Boot from a disk (either floppy or harddrive)
398 static void
399 boot_disk(u8 bootdrv, int checksig)
400 {
401     u16 bootseg = 0x07c0;
402
403     // Read sector
404     struct bregs br;
405     memset(&br, 0, sizeof(br));
406     br.flags = F_IF;
407     br.dl = bootdrv;
408     br.es = bootseg;
409     br.ah = 2;
410     br.al = 1;
411     br.cl = 1;
412     call16_int(0x13, &br);
413
414     if (br.flags & F_CF) {
415         printf("Boot failed: could not read the boot disk\n\n");
416         return;
417     }
418
419     if (checksig) {
420         struct mbr_s *mbr = (void*)0;
421         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
422             printf("Boot failed: not a bootable disk\n\n");
423             return;
424         }
425     }
426
427     /* Canonicalize bootseg:bootip */
428     u16 bootip = (bootseg & 0x0fff) << 4;
429     bootseg &= 0xf000;
430
431     call_boot_entry(bootseg, bootip, bootdrv);
432 }
433
434 // Boot from a CD-ROM
435 static void
436 boot_cdrom(struct bev_s *ie)
437 {
438     if (! CONFIG_CDROM_BOOT)
439         return;
440
441     if (!ie->vector)
442         return;
443     printf("Booting from DVD/CD...\n");
444
445     struct drive_s *drive_g = (void*)ie->vector;
446     int status = cdrom_boot(drive_g);
447     if (status) {
448         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
449         return;
450     }
451
452     u16 ebda_seg = get_ebda_seg();
453     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
454     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
455     /* Canonicalize bootseg:bootip */
456     u16 bootip = (bootseg & 0x0fff) << 4;
457     bootseg &= 0xf000;
458
459     call_boot_entry(bootseg, bootip, bootdrv);
460 }
461
462 // Boot from a CBFS payload
463 static void
464 boot_cbfs(struct bev_s *ie)
465 {
466     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
467         return;
468     printf("Booting from CBFS...\n");
469     cbfs_run_payload((void*)ie->vector);
470 }
471
472 static void
473 do_boot(u16 seq_nr)
474 {
475     if (! CONFIG_BOOT)
476         panic("Boot support not compiled in.\n");
477
478     if (seq_nr >= BEVCount) {
479         printf("No bootable device.\n");
480         // Loop with irqs enabled - this allows ctrl+alt+delete to work.
481         for (;;)
482             wait_irq();
483     }
484
485     // Boot the given BEV type.
486     struct bev_s *ie = &BEV[seq_nr];
487     switch (ie->type) {
488     case IPL_TYPE_FLOPPY:
489         printf("Booting from Floppy...\n");
490         boot_disk(0x00, CheckFloppySig);
491         break;
492     case IPL_TYPE_HARDDISK:
493         printf("Booting from Hard Disk...\n");
494         boot_disk(0x80, 1);
495         break;
496     case IPL_TYPE_CDROM:
497         boot_cdrom(ie);
498         break;
499     case IPL_TYPE_CBFS:
500         boot_cbfs(ie);
501         break;
502     case IPL_TYPE_BEV:
503         printf("Booting from ROM...\n");
504         call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
505         break;
506     }
507
508     // Boot failed: invoke the boot recovery function
509     struct bregs br;
510     memset(&br, 0, sizeof(br));
511     br.flags = F_IF;
512     call16_int(0x18, &br);
513 }
514
515 // Boot Failure recovery: try the next device.
516 void VISIBLE32FLAT
517 handle_18(void)
518 {
519     debug_serial_setup();
520     debug_enter(NULL, DEBUG_HDL_18);
521     u16 ebda_seg = get_ebda_seg();
522     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
523     SET_EBDA2(ebda_seg, boot_sequence, seq);
524     do_boot(seq);
525 }
526
527 // INT 19h Boot Load Service Entry Point
528 void VISIBLE32FLAT
529 handle_19(void)
530 {
531     debug_serial_setup();
532     debug_enter(NULL, DEBUG_HDL_19);
533     SET_EBDA(boot_sequence, 0);
534     do_boot(0);
535 }