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