Remove drive->desc field.
[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" // func defs
14 #include "cmos.h" // inb_cmos
15 #include "paravirt.h" // romfile_loadfile
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, const char *desc, int prio)
219 {
220     bootentry_add(IPL_TYPE_FLOPPY, defPrio(prio, DefaultFloppyPrio)
221                   , (u32)drive_g, desc);
222 }
223
224 void
225 boot_add_hd(struct drive_s *drive_g, const char *desc, int prio)
226 {
227     bootentry_add(IPL_TYPE_HARDDISK, defPrio(prio, DefaultHDPrio)
228                   , (u32)drive_g, desc);
229 }
230
231 void
232 boot_add_cd(struct drive_s *drive_g, const char *desc, int prio)
233 {
234     bootentry_add(IPL_TYPE_CDROM, defPrio(prio, DefaultCDPrio)
235                   , (u32)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 // BEV (Boot Execution Vector) list
310 struct bev_s {
311     int type;
312     u32 vector;
313 };
314 static struct bev_s BEV[20];
315 static int BEVCount;
316 static int HaveHDBoot, HaveFDBoot;
317
318 static void
319 add_bev(int type, u32 vector)
320 {
321     if (type == IPL_TYPE_HARDDISK && HaveHDBoot++)
322         return;
323     if (type == IPL_TYPE_FLOPPY && HaveFDBoot++)
324         return;
325     if (BEVCount >= ARRAY_SIZE(BEV))
326         return;
327     struct bev_s *bev = &BEV[BEVCount++];
328     bev->type = type;
329     bev->vector = vector;
330 }
331
332 // Prepare for boot - show menu and run bcvs.
333 void
334 boot_prep(void)
335 {
336     if (! CONFIG_BOOT) {
337         wait_threads();
338         return;
339     }
340
341     // XXX - show available drives?
342
343     // Allow user to modify BCV/IPL order.
344     interactive_bootmenu();
345     wait_threads();
346
347     // Map drives and populate BEV list
348     struct bootentry_s *pos = BootList;
349     while (pos) {
350         switch (pos->type) {
351         case IPL_TYPE_BCV:
352             call_bcv(pos->vector.seg, pos->vector.offset);
353             add_bev(IPL_TYPE_HARDDISK, 0);
354             break;
355         case IPL_TYPE_FLOPPY:
356             map_floppy_drive(pos->drive);
357             add_bev(IPL_TYPE_FLOPPY, 0);
358             break;
359         case IPL_TYPE_HARDDISK:
360             map_hd_drive(pos->drive);
361             add_bev(IPL_TYPE_HARDDISK, 0);
362             break;
363         case IPL_TYPE_CDROM:
364             map_cd_drive(pos->drive);
365             // NO BREAK
366         default:
367             add_bev(pos->type, pos->data);
368             break;
369         }
370         pos = pos->next;
371     }
372
373     // If nothing added a floppy/hd boot - add it manually.
374     add_bev(IPL_TYPE_FLOPPY, 0);
375     add_bev(IPL_TYPE_HARDDISK, 0);
376 }
377
378
379 /****************************************************************
380  * Boot code (int 18/19)
381  ****************************************************************/
382
383 // Jump to a bootup entry point.
384 static void
385 call_boot_entry(struct segoff_s bootsegip, u8 bootdrv)
386 {
387     dprintf(1, "Booting from %04x:%04x\n", bootsegip.seg, bootsegip.offset);
388     struct bregs br;
389     memset(&br, 0, sizeof(br));
390     br.flags = F_IF;
391     br.code = bootsegip;
392     // Set the magic number in ax and the boot drive in dl.
393     br.dl = bootdrv;
394     br.ax = 0xaa55;
395     call16(&br);
396 }
397
398 // Boot from a disk (either floppy or harddrive)
399 static void
400 boot_disk(u8 bootdrv, int checksig)
401 {
402     u16 bootseg = 0x07c0;
403
404     // Read sector
405     struct bregs br;
406     memset(&br, 0, sizeof(br));
407     br.flags = F_IF;
408     br.dl = bootdrv;
409     br.es = bootseg;
410     br.ah = 2;
411     br.al = 1;
412     br.cl = 1;
413     call16_int(0x13, &br);
414
415     if (br.flags & F_CF) {
416         printf("Boot failed: could not read the boot disk\n\n");
417         return;
418     }
419
420     if (checksig) {
421         struct mbr_s *mbr = (void*)0;
422         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
423             printf("Boot failed: not a bootable disk\n\n");
424             return;
425         }
426     }
427
428     /* Canonicalize bootseg:bootip */
429     u16 bootip = (bootseg & 0x0fff) << 4;
430     bootseg &= 0xf000;
431
432     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
433 }
434
435 // Boot from a CD-ROM
436 static void
437 boot_cdrom(struct drive_s *drive_g)
438 {
439     if (! CONFIG_CDROM_BOOT)
440         return;
441     printf("Booting from DVD/CD...\n");
442
443     int status = cdrom_boot(drive_g);
444     if (status) {
445         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
446         return;
447     }
448
449     u16 ebda_seg = get_ebda_seg();
450     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_extdrive);
451     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
452     /* Canonicalize bootseg:bootip */
453     u16 bootip = (bootseg & 0x0fff) << 4;
454     bootseg &= 0xf000;
455
456     call_boot_entry(SEGOFF(bootseg, bootip), bootdrv);
457 }
458
459 // Boot from a CBFS payload
460 static void
461 boot_cbfs(struct cbfs_file *file)
462 {
463     if (!CONFIG_COREBOOT || !CONFIG_COREBOOT_FLASH)
464         return;
465     printf("Booting from CBFS...\n");
466     cbfs_run_payload(file);
467 }
468
469 // Boot from a BEV entry on an optionrom.
470 static void
471 boot_rom(u32 vector)
472 {
473     printf("Booting from ROM...\n");
474     struct segoff_s so;
475     so.segoff = vector;
476     call_boot_entry(so, 0);
477 }
478
479 // Determine next boot method and attempt a boot using it.
480 static void
481 do_boot(u16 seq_nr)
482 {
483     if (! CONFIG_BOOT)
484         panic("Boot support not compiled in.\n");
485
486     if (seq_nr >= BEVCount) {
487         printf("No bootable device.\n");
488         // Loop with irqs enabled - this allows ctrl+alt+delete to work.
489         for (;;)
490             wait_irq();
491     }
492
493     // Boot the given BEV type.
494     struct bev_s *ie = &BEV[seq_nr];
495     switch (ie->type) {
496     case IPL_TYPE_FLOPPY:
497         printf("Booting from Floppy...\n");
498         boot_disk(0x00, CheckFloppySig);
499         break;
500     case IPL_TYPE_HARDDISK:
501         printf("Booting from Hard Disk...\n");
502         boot_disk(0x80, 1);
503         break;
504     case IPL_TYPE_CDROM:
505         boot_cdrom((void*)ie->vector);
506         break;
507     case IPL_TYPE_CBFS:
508         boot_cbfs((void*)ie->vector);
509         break;
510     case IPL_TYPE_BEV:
511         boot_rom(ie->vector);
512         break;
513     }
514
515     // Boot failed: invoke the boot recovery function
516     struct bregs br;
517     memset(&br, 0, sizeof(br));
518     br.flags = F_IF;
519     call16_int(0x18, &br);
520 }
521
522 // Boot Failure recovery: try the next device.
523 void VISIBLE32FLAT
524 handle_18(void)
525 {
526     debug_serial_setup();
527     debug_enter(NULL, DEBUG_HDL_18);
528     u16 ebda_seg = get_ebda_seg();
529     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
530     SET_EBDA2(ebda_seg, boot_sequence, seq);
531     do_boot(seq);
532 }
533
534 // INT 19h Boot Load Service Entry Point
535 void VISIBLE32FLAT
536 handle_19(void)
537 {
538     debug_serial_setup();
539     debug_enter(NULL, DEBUG_HDL_19);
540     SET_EBDA(boot_sequence, 0);
541     do_boot(0);
542 }