Move IPL.checkfloppysig to a global (CheckFloppySig) 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
147 static struct bootentry_s *BootList;
148
149 static void
150 bootentry_add(int type, int prio, u32 data, const char *desc)
151 {
152     if (! CONFIG_BOOT)
153         return;
154     struct bootentry_s *be = malloc_tmp(sizeof(*be));
155     if (!be) {
156         warn_noalloc();
157         return;
158     }
159     be->type = type;
160     be->priority = prio;
161     be->data = data;
162     be->description = desc;
163
164     // Add entry in sorted order.
165     struct bootentry_s **pprev;
166     for (pprev = &BootList; *pprev; pprev = &(*pprev)->next) {
167         struct bootentry_s *pos = *pprev;
168         if (be->priority < pos->priority)
169             break;
170         if (be->priority > pos->priority)
171             continue;
172         if (be->type < pos->type)
173             break;
174         if (be->type > pos->type)
175             continue;
176         if (be->type <= IPL_TYPE_CDROM
177             && (be->drive->type < pos->drive->type
178                 || (be->drive->type == pos->drive->type
179                     && be->drive->cntl_id < pos->drive->cntl_id)))
180             break;
181     }
182     be->next = *pprev;
183     *pprev = be;
184 }
185
186 // Return the given priority if it's set - defaultprio otherwise.
187 static inline int defPrio(int priority, int defaultprio) {
188     return (priority < 0) ? defaultprio : priority;
189 }
190
191 // Add a BEV vector for a given pnp compatible option rom.
192 void
193 boot_add_bev(u16 seg, u16 bev, u16 desc, int prio)
194 {
195     bootentry_add(IPL_TYPE_BEV, defPrio(prio, DefaultBEVPrio)
196                   , SEGOFF(seg, bev).segoff
197                   , desc ? MAKE_FLATPTR(seg, desc) : "Unknown");
198     DefaultBEVPrio = DEFAULT_PRIO;
199 }
200
201 // Add a bcv entry for an expansion card harddrive or legacy option rom
202 void
203 boot_add_bcv(u16 seg, u16 ip, u16 desc, int prio)
204 {
205     bootentry_add(IPL_TYPE_BCV, defPrio(prio, DEFAULT_PRIO)
206                   , SEGOFF(seg, ip).segoff
207                   , desc ? MAKE_FLATPTR(seg, desc) : "Legacy option rom");
208 }
209
210 void
211 boot_add_floppy(struct drive_s *drive_g, int prio)
212 {
213     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
214                   , (u32)drive_g, drive_g->desc);
215 }
216
217 void
218 boot_add_hd(struct drive_s *drive_g, int prio)
219 {
220     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
221                   , (u32)drive_g, drive_g->desc);
222 }
223
224 void
225 boot_add_cd(struct drive_s *drive_g, int prio)
226 {
227     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
228                   , (u32)drive_g, drive_g->desc);
229 }
230
231 // Add a CBFS payload entry
232 void
233 boot_add_cbfs(void *data, const char *desc, int prio)
234 {
235     bootentry_add(IPL_TYPE_CBFS, defPrio(prio, DEFAULT_PRIO), (u32)data, desc);
236 }
237
238
239 /****************************************************************
240  * Boot menu and BCV execution
241  ****************************************************************/
242
243 // Show IPL option menu.
244 static void
245 interactive_bootmenu(void)
246 {
247     if (! CONFIG_BOOTMENU || ! qemu_cfg_show_boot_menu())
248         return;
249
250     while (get_keystroke(0) >= 0)
251         ;
252
253     printf("Press F12 for boot menu.\n\n");
254
255     enable_bootsplash();
256     int scan_code = get_keystroke(CONFIG_BOOTMENU_WAIT);
257     disable_bootsplash();
258     if (scan_code != 0x86)
259         /* not F12 */
260         return;
261
262     while (get_keystroke(0) >= 0)
263         ;
264
265     printf("Select boot device:\n\n");
266     wait_threads();
267
268     // Show menu items
269     struct bootentry_s *pos = BootList;
270     int maxmenu = 0;
271     while (pos) {
272         char desc[60];
273         maxmenu++;
274         printf("%d. %s\n", maxmenu
275                , strtcpy(desc, pos->description, ARRAY_SIZE(desc)));
276         pos = pos->next;
277     }
278
279     // Get key press
280     for (;;) {
281         scan_code = get_keystroke(1000);
282         if (scan_code >= 1 && scan_code <= maxmenu+1)
283             break;
284     }
285     printf("\n");
286     if (scan_code == 0x01)
287         // ESC
288         return;
289
290     // Find entry and make top priority.
291     int choice = scan_code - 1;
292     struct bootentry_s **pprev = &BootList;
293     while (--choice)
294         pprev = &(*pprev)->next;
295     pos = *pprev;
296     *pprev = pos->next;
297     pos->next = BootList;
298     BootList = pos;
299     pos->priority = 0;
300 }
301
302 static int HaveHDBoot, HaveFDBoot;
303
304 static void
305 add_bev(int type, u32 vector)
306 {
307     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
308         return;
309     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
310         return;
311     if (IPL.bevcount >= ARRAY_SIZE(IPL.bev))
312         return;
313     struct ipl_entry_s *bev = &IPL.bev[IPL.bevcount++];
314     bev->type = type;
315     bev->vector = vector;
316 }
317
318 // Prepare for boot - show menu and run bcvs.
319 void
320 boot_prep(void)
321 {
322     if (! CONFIG_BOOT) {
323         wait_threads();
324         return;
325     }
326
327     // XXX - show available drives?
328
329     // Allow user to modify BCV/IPL order.
330     interactive_bootmenu();
331     wait_threads();
332
333     // Map drives and populate BEV list
334     struct bootentry_s *pos = BootList;
335     while (pos) {
336         switch (pos->type) {
337         case IPL_TYPE_BCV:
338             call_bcv(pos->vector.seg, pos->vector.offset);
339             add_bev(IPL_TYPE_HARDDISK, 0);
340             break;
341         case IPL_TYPE_FLOPPY:
342             map_floppy_drive(pos->drive);
343             add_bev(IPL_TYPE_FLOPPY, 0);
344             break;
345         case IPL_TYPE_HARDDISK:
346             map_hd_drive(pos->drive);
347             add_bev(IPL_TYPE_HARDDISK, 0);
348             break;
349         case IPL_TYPE_CDROM:
350             map_cd_drive(pos->drive);
351             // NO BREAK
352         default:
353             add_bev(pos->type, pos->data);
354             break;
355         }
356         pos = pos->next;
357     }
358
359     // If nothing added a floppy/hd boot - add it manually.
360     add_bev(IPL_TYPE_FLOPPY, 0);
361     add_bev(IPL_TYPE_HARDDISK, 0);
362 }
363
364
365 /****************************************************************
366  * Boot code (int 18/19)
367  ****************************************************************/
368
369 // Jump to a bootup entry point.
370 static void
371 call_boot_entry(u16 bootseg, u16 bootip, u8 bootdrv)
372 {
373     dprintf(1, "Booting from %04x:%04x\n", bootseg, bootip);
374     struct bregs br;
375     memset(&br, 0, sizeof(br));
376     br.flags = F_IF;
377     br.code = SEGOFF(bootseg, bootip);
378     // Set the magic number in ax and the boot drive in dl.
379     br.dl = bootdrv;
380     br.ax = 0xaa55;
381     call16(&br);
382 }
383
384 // Boot from a disk (either floppy or harddrive)
385 static void
386 boot_disk(u8 bootdrv, int checksig)
387 {
388     u16 bootseg = 0x07c0;
389
390     // Read sector
391     struct bregs br;
392     memset(&br, 0, sizeof(br));
393     br.flags = F_IF;
394     br.dl = bootdrv;
395     br.es = bootseg;
396     br.ah = 2;
397     br.al = 1;
398     br.cl = 1;
399     call16_int(0x13, &br);
400
401     if (br.flags & F_CF) {
402         printf("Boot failed: could not read the boot disk\n\n");
403         return;
404     }
405
406     if (checksig) {
407         struct mbr_s *mbr = (void*)0;
408         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
409             printf("Boot failed: not a bootable disk\n\n");
410             return;
411         }
412     }
413
414     /* Canonicalize bootseg:bootip */
415     u16 bootip = (bootseg & 0x0fff) << 4;
416     bootseg &= 0xf000;
417
418     call_boot_entry(bootseg, bootip, bootdrv);
419 }
420
421 // Boot from a CD-ROM
422 static void
423 boot_cdrom(struct ipl_entry_s *ie)
424 {
425     if (! CONFIG_CDROM_BOOT)
426         return;
427
428     if (!ie->vector)
429         return;
430     printf("Booting from DVD/CD...\n");
431
432     struct drive_s *drive_g = (void*)ie->vector;
433     int status = cdrom_boot(drive_g);
434     if (status) {
435         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
436         return;
437     }
438
439     u16 ebda_seg = get_ebda_seg();
440     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
441     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
442     /* Canonicalize bootseg:bootip */
443     u16 bootip = (bootseg & 0x0fff) << 4;
444     bootseg &= 0xf000;
445
446     call_boot_entry(bootseg, bootip, bootdrv);
447 }
448
449 // Boot from a CBFS payload
450 static void
451 boot_cbfs(struct ipl_entry_s *ie)
452 {
453     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
454         return;
455     printf("Booting from CBFS...\n");
456     cbfs_run_payload((void*)ie->vector);
457 }
458
459 static void
460 do_boot(u16 seq_nr)
461 {
462     if (! CONFIG_BOOT)
463         panic("Boot support not compiled in.\n");
464
465     if (seq_nr >= IPL.bevcount) {
466         printf("No bootable device.\n");
467         // Loop with irqs enabled - this allows ctrl+alt+delete to work.
468         for (;;)
469             wait_irq();
470     }
471
472     // Boot the given BEV type.
473     struct ipl_entry_s *ie = &IPL.bev[seq_nr];
474     switch (ie->type) {
475     case IPL_TYPE_FLOPPY:
476         printf("Booting from Floppy...\n");
477         boot_disk(0x00, CheckFloppySig);
478         break;
479     case IPL_TYPE_HARDDISK:
480         printf("Booting from Hard Disk...\n");
481         boot_disk(0x80, 1);
482         break;
483     case IPL_TYPE_CDROM:
484         boot_cdrom(ie);
485         break;
486     case IPL_TYPE_CBFS:
487         boot_cbfs(ie);
488         break;
489     case IPL_TYPE_BEV:
490         printf("Booting from ROM...\n");
491         call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
492         break;
493     }
494
495     // Boot failed: invoke the boot recovery function
496     struct bregs br;
497     memset(&br, 0, sizeof(br));
498     br.flags = F_IF;
499     call16_int(0x18, &br);
500 }
501
502 // Boot Failure recovery: try the next device.
503 void VISIBLE32FLAT
504 handle_18(void)
505 {
506     debug_serial_setup();
507     debug_enter(NULL, DEBUG_HDL_18);
508     u16 ebda_seg = get_ebda_seg();
509     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
510     SET_EBDA2(ebda_seg, boot_sequence, seq);
511     do_boot(seq);
512 }
513
514 // INT 19h Boot Load Service Entry Point
515 void VISIBLE32FLAT
516 handle_19(void)
517 {
518     debug_serial_setup();
519     debug_enter(NULL, DEBUG_HDL_19);
520     SET_EBDA(boot_sequence, 0);
521     do_boot(0);
522 }