Simplify 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 *ie = &IPL.table[0];
34     ie->type = IPL_TYPE_FLOPPY;
35     ie->description = "Floppy";
36     ie++;
37
38     // First HDD
39     ie->type = IPL_TYPE_HARDDISK;
40     ie->description = "Hard Disk";
41     ie++;
42
43     // CDROM
44     if (CONFIG_CDROM_BOOT) {
45         ie->type = IPL_TYPE_CDROM;
46         ie->description = "CD-Rom";
47         ie++;
48     }
49
50     IPL.count = ie - IPL.table;
51     SET_EBDA(boot_sequence, 0xffff);
52     if (CONFIG_COREBOOT) {
53         // XXX - hardcode defaults for coreboot.
54         IPL.bootorder = 0x00000231;
55         IPL.checkfloppysig = 1;
56     } else {
57         // On emulators, get boot order from nvram.
58         IPL.bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
59                          | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
60         if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1))
61             IPL.checkfloppysig = 1;
62     }
63 }
64
65 // Add a BEV vector for a given pnp compatible option rom.
66 void
67 add_bev(u16 seg, u16 bev, u16 desc)
68 {
69     // Found a device that thinks it can boot the system.  Record
70     // its BEV and product name string.
71
72     if (! CONFIG_BOOT)
73         return;
74
75     if (IPL.count >= ARRAY_SIZE(IPL.table))
76         return;
77
78     struct ipl_entry_s *ie = &IPL.table[IPL.count];
79     ie->type = IPL_TYPE_BEV;
80     ie->vector = (seg << 16) | bev;
81     if (desc)
82         ie->description = MAKE_FLATPTR(seg, desc);
83
84     IPL.count++;
85 }
86
87
88 /****************************************************************
89  * Boot menu
90  ****************************************************************/
91
92 void
93 interactive_bootmenu()
94 {
95     if (! CONFIG_BOOTMENU)
96         return;
97
98     while (get_keystroke(0) >= 0)
99         ;
100
101     printf("Press F12 for boot menu.\n\n");
102
103     int scan_code = get_keystroke(2500);
104     if (scan_code != 0x86)
105         /* not F12 */
106         return;
107
108     while (get_keystroke(0) >= 0)
109         ;
110
111     printf("Select boot device:\n\n");
112
113     int count = IPL.count;
114     int i;
115     for (i = 0; i < count; i++) {
116         struct ipl_entry_s *ie = &IPL.table[i];
117         char desc[33];
118         printf("%d. %s\n", i+1
119                , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
120     }
121
122     for (;;) {
123         scan_code = get_keystroke(1000);
124         if (scan_code == 0x01)
125             // ESC
126             break;
127         if (scan_code >= 0 && scan_code <= count + 1) {
128             // Add user choice to the boot order.
129             u16 choice = scan_code - 1;
130             u32 bootorder = IPL.bootorder;
131             IPL.bootorder = (bootorder << 4) | choice;
132             break;
133         }
134     }
135     printf("\n");
136 }
137
138
139 /****************************************************************
140  * Boot code (int 18/19)
141  ****************************************************************/
142
143 static void
144 call_boot_entry(u16 bootseg, u16 bootip, u8 bootdrv)
145 {
146     dprintf(1, "Booting from %x:%x\n", bootseg, bootip);
147
148     struct bregs br;
149     memset(&br, 0, sizeof(br));
150     br.ip = bootip;
151     br.cs = bootseg;
152     // Set the magic number in ax and the boot drive in dl.
153     br.dl = bootdrv;
154     br.ax = 0xaa55;
155     call16(&br);
156 }
157
158 // Boot from a disk (either floppy or harddrive)
159 static void
160 boot_disk(u8 bootdrv, int checksig)
161 {
162     u16 bootseg = 0x07c0;
163
164     // Read sector
165     struct bregs br;
166     memset(&br, 0, sizeof(br));
167     br.dl = bootdrv;
168     br.es = bootseg;
169     br.ah = 2;
170     br.al = 1;
171     br.cl = 1;
172     call16_int(0x13, &br);
173
174     if (br.flags & F_CF) {
175         printf("Boot failed: could not read the boot disk\n\n");
176         return;
177     }
178
179     if (checksig) {
180         struct mbr_s *mbr = (void*)0;
181         if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
182             printf("Boot failed: not a bootable disk\n\n");
183             return;
184         }
185     }
186
187     /* Canonicalize bootseg:bootip */
188     u16 bootip = (bootseg & 0x0fff) << 4;
189     bootseg &= 0xf000;
190
191     call_boot_entry(bootseg, bootip, bootdrv);
192 }
193
194 // Boot from a CD-ROM
195 static void
196 boot_cdrom()
197 {
198     if (! CONFIG_CDROM_BOOT)
199         return;
200     int status = cdrom_boot();
201     if (status) {
202         printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
203         return;
204     }
205
206     u16 ebda_seg = get_ebda_seg();
207     u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
208     u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
209     /* Canonicalize bootseg:bootip */
210     u16 bootip = (bootseg & 0x0fff) << 4;
211     bootseg &= 0xf000;
212
213     call_boot_entry(bootseg, bootip, bootdrv);
214 }
215
216 static void
217 do_boot(u16 seq_nr)
218 {
219     if (! CONFIG_BOOT)
220         BX_PANIC("Boot support not compiled in.\n");
221
222     u32 bootdev = IPL.bootorder;
223     bootdev >>= 4 * seq_nr;
224     bootdev &= 0xf;
225
226     if (bootdev == 0)
227         BX_PANIC("No bootable device.\n");
228
229     /* Translate bootdev to an IPL table offset by subtracting 1 */
230     bootdev -= 1;
231
232     if (bootdev >= IPL.count) {
233         dprintf(1, "Invalid boot device (0x%x)\n", bootdev);
234         goto fail;
235     }
236
237     /* Do the loading, and set up vector as a far pointer to the boot
238      * address, and bootdrv as the boot drive */
239     struct ipl_entry_s *ie = &IPL.table[bootdev];
240     char desc[33];
241     printf("Booting from %s...\n"
242            , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
243
244     switch(ie->type) {
245     case IPL_TYPE_FLOPPY:
246         boot_disk(0x00, IPL.checkfloppysig);
247         break;
248     case IPL_TYPE_HARDDISK:
249         boot_disk(0x80, 1);
250         break;
251     case IPL_TYPE_CDROM:
252         boot_cdrom();
253         break;
254     case IPL_TYPE_BEV:
255         call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
256         break;
257     }
258
259     // Boot failed: invoke the boot recovery function
260     struct bregs br;
261 fail:
262     memset(&br, 0, sizeof(br));
263     call16_int(0x18, &br);
264 }
265
266 // Boot Failure recovery: try the next device.
267 void VISIBLE32
268 handle_18()
269 {
270     debug_serial_setup();
271     debug_enter(NULL, DEBUG_HDL_18);
272     u16 ebda_seg = get_ebda_seg();
273     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
274     SET_EBDA2(ebda_seg, boot_sequence, seq);
275     do_boot(seq);
276 }
277
278 // INT 19h Boot Load Service Entry Point
279 void VISIBLE32
280 handle_19()
281 {
282     debug_serial_setup();
283     debug_enter(NULL, DEBUG_HDL_19);
284     SET_EBDA(boot_sequence, 0);
285     do_boot(0);
286 }
287
288 // Ughh - some older gcc compilers have a bug which causes VISIBLE32
289 // functions to not be exported as global variables.
290 asm(".global handle_18, handle_19");