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