Reorganize boot code.
[seabios.git] / src / boot.c
1 // Code to load disk image and start system boot.
2 //
3 // Copyright (C) 2008  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" // irq_enable
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
16 struct ipl_s IPL;
17
18
19 /****************************************************************
20  * IPL handlers
21  ****************************************************************/
22
23 void
24 boot_setup()
25 {
26     if (! CONFIG_BOOT)
27         return;
28     dprintf(3, "init boot device ordering\n");
29
30     memset(&IPL, 0, sizeof(IPL));
31
32     // Floppy drive
33     struct ipl_entry_s *ip = &IPL.table[0];
34     ip->type = IPL_TYPE_FLOPPY;
35     ip++;
36
37     // First HDD
38     ip->type = IPL_TYPE_HARDDISK;
39     ip++;
40
41     // CDROM
42     if (CONFIG_CDROM_BOOT) {
43         ip->type = IPL_TYPE_CDROM;
44         ip++;
45     }
46
47     IPL.count = ip - IPL.table;
48     SET_EBDA(boot_sequence, 0xffff);
49     if (CONFIG_COREBOOT) {
50         // XXX - hardcode defaults for coreboot.
51         IPL.bootorder = 0x00000231;
52         IPL.checkfloppysig = 1;
53     } else {
54         // On emulators, get boot order from nvram.
55         IPL.bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
56                          | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
57         if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1))
58             IPL.checkfloppysig = 1;
59     }
60 }
61
62 // Add a BEV vector for a given pnp compatible option rom.
63 void
64 add_bev(u16 seg, u16 bev, u16 desc)
65 {
66     // Found a device that thinks it can boot the system.  Record
67     // its BEV and product name string.
68
69     if (! CONFIG_BOOT)
70         return;
71
72     if (IPL.count >= ARRAY_SIZE(IPL.table))
73         return;
74
75     struct ipl_entry_s *ip = &IPL.table[IPL.count];
76     ip->type = IPL_TYPE_BEV;
77     ip->vector = (seg << 16) | bev;
78     if (desc)
79         ip->description = MAKE_FLATPTR(seg, desc);
80
81     IPL.count++;
82 }
83
84
85 /****************************************************************
86  * Printing helpers
87  ****************************************************************/
88
89 static const char drivetypes[][10]={
90     "", "Floppy", "Hard Disk", "CD-Rom", "Network"
91 };
92
93 // display a device name
94 static void
95 printf_bootdev(u16 bootdev)
96 {
97     u16 type = IPL.table[bootdev].type;
98
99     /* NIC appears as type 0x80 */
100     if (type == IPL_TYPE_BEV)
101         type = 0x4;
102     if (type == 0 || type > 0x4) {
103         printf("Unknown");
104         return;
105     }
106     printf("%s", drivetypes[type]);
107
108     /* print product string if BEV */
109     char *description_fl = IPL.table[bootdev].description;
110     if (type == 4 && description_fl != 0) {
111         char description[33];
112         /* first 32 bytes are significant */
113         memcpy(description, description_fl, 32);
114         /* terminate string */
115         description[32] = 0;
116         printf(" [%.s]", description);
117     }
118 }
119
120 // display the boot device
121 static void
122 print_boot_device(u16 bootdev)
123 {
124     printf("Booting from ");
125     printf_bootdev(bootdev);
126     printf("...\n");
127 }
128
129 // display the reason why a boot failed
130 static void
131 print_boot_failure(u16 type, u8 reason)
132 {
133     printf("Boot failed");
134     if (type < 4) {
135         /* Report the reason too */
136         if (reason==0)
137             printf(": not a bootable disk");
138         else
139             printf(": could not read the boot disk");
140     }
141     printf("\n\n");
142 }
143
144
145 /****************************************************************
146  * Boot menu
147  ****************************************************************/
148
149 void
150 interactive_bootmenu()
151 {
152     if (! CONFIG_BOOTMENU)
153         return;
154
155     while (get_keystroke(0) >= 0)
156         ;
157
158     printf("Press F12 for boot menu.\n\n");
159
160     int scan_code = get_keystroke(2500);
161     if (scan_code != 0x86)
162         /* not F12 */
163         return;
164
165     while (get_keystroke(0) >= 0)
166         ;
167
168     printf("Select boot device:\n\n");
169
170     int count = IPL.count;
171     int i;
172     for (i = 0; i < count; i++) {
173         printf("%d. ", i+1);
174         printf_bootdev(i);
175         printf("\n");
176     }
177
178     for (;;) {
179         scan_code = get_keystroke(1000);
180         if (scan_code == 0x01)
181             // ESC
182             break;
183         if (scan_code >= 0 && scan_code <= count + 1) {
184             // Add user choice to the boot order.
185             u16 choice = scan_code - 1;
186             u32 bootorder = IPL.bootorder;
187             IPL.bootorder = (bootorder << 4) | choice;
188             break;
189         }
190     }
191     printf("\n");
192 }
193
194
195 /****************************************************************
196  * Boot code (int 18/19)
197  ****************************************************************/
198
199 static void
200 try_boot(u16 seq_nr)
201 {
202     if (! CONFIG_BOOT)
203         BX_PANIC("Boot support not compiled in.\n");
204
205     u32 bootdev = IPL.bootorder;
206     bootdev >>= 4 * seq_nr;
207     bootdev &= 0xf;
208
209     if (bootdev == 0)
210         BX_PANIC("No bootable device.\n");
211
212     /* Translate bootdev to an IPL table offset by subtracting 1 */
213     bootdev -= 1;
214
215     if (bootdev >= IPL.count) {
216         dprintf(1, "Invalid boot device (0x%x)\n", bootdev);
217         return;
218     }
219
220     /* Do the loading, and set up vector as a far pointer to the boot
221      * address, and bootdrv as the boot drive */
222     print_boot_device(bootdev);
223
224     u16 type = IPL.table[bootdev].type;
225
226     u16 bootseg, bootip;
227     u8 bootdrv = 0;
228     struct bregs cr;
229     switch(type) {
230     case IPL_TYPE_FLOPPY:
231     case IPL_TYPE_HARDDISK:
232
233         bootdrv = (type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
234         bootseg = 0x07c0;
235
236         // Read sector
237         memset(&cr, 0, sizeof(cr));
238         cr.dl = bootdrv;
239         cr.es = bootseg;
240         cr.ah = 2;
241         cr.al = 1;
242         cr.cl = 1;
243         call16_int(0x13, &cr);
244
245         if (cr.flags & F_CF) {
246             print_boot_failure(type, 1);
247             return;
248         }
249
250         /* Always check the signature on a HDD boot sector; on FDD,
251          * only do the check if configured for it */
252         if (type != IPL_TYPE_FLOPPY || IPL.checkfloppysig) {
253             struct mbr_s *mbr = (void*)0;
254             if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
255                 print_boot_failure(type, 0);
256                 return;
257             }
258         }
259
260         /* Canonicalize bootseg:bootip */
261         bootip = (bootseg & 0x0fff) << 4;
262         bootseg &= 0xf000;
263         break;
264     case IPL_TYPE_CDROM: {
265         /* CD-ROM */
266         if (! CONFIG_CDROM_BOOT)
267             return;
268         int status = cdrom_boot();
269         if (status) {
270             printf("CDROM boot failure code : %04x\n", status);
271             print_boot_failure(type, 1);
272             return;
273         }
274
275         u16 ebda_seg = get_ebda_seg();
276         bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
277         bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
278         /* Canonicalize bootseg:bootip */
279         bootip = (bootseg & 0x0fff) << 4;
280         bootseg &= 0xf000;
281         break;
282     }
283     case IPL_TYPE_BEV: {
284         /* Expansion ROM with a Bootstrap Entry Vector (a far
285          * pointer) */
286         u32 vector = IPL.table[bootdev].vector;
287         bootseg = vector >> 16;
288         bootip = vector & 0xffff;
289         break;
290     }
291     default:
292         return;
293     }
294
295     /* Debugging info */
296     dprintf(1, "Booting from %x:%x\n", bootseg, bootip);
297
298     memset(&cr, 0, sizeof(cr));
299     cr.ip = bootip;
300     cr.cs = bootseg;
301     // Set the magic number in ax and the boot drive in dl.
302     cr.dl = bootdrv;
303     cr.ax = 0xaa55;
304     call16(&cr);
305 }
306
307 static void
308 do_boot(u16 seq_nr)
309 {
310     try_boot(seq_nr);
311
312     // Boot failed: invoke the boot recovery function
313     struct bregs br;
314     memset(&br, 0, sizeof(br));
315     call16_int(0x18, &br);
316 }
317
318 // Boot Failure recovery: try the next device.
319 void VISIBLE32
320 handle_18()
321 {
322     debug_serial_setup();
323     debug_enter(NULL, DEBUG_HDL_18);
324     u16 ebda_seg = get_ebda_seg();
325     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
326     SET_EBDA2(ebda_seg, boot_sequence, seq);
327     do_boot(seq);
328 }
329
330 // INT 19h Boot Load Service Entry Point
331 void VISIBLE32
332 handle_19()
333 {
334     debug_serial_setup();
335     debug_enter(NULL, DEBUG_HDL_19);
336     SET_EBDA(boot_sequence, 0);
337     do_boot(0);
338 }
339
340 // Ughh - some older gcc compilers have a bug which causes VISIBLE32
341 // functions to not be exported as global variables.
342 asm(".global handle_18, handle_19");