blob: 80e74ca4aa5c6898b70ae911b50999393e7abbbe [file] [log] [blame]
Kevin O'Connorf13b0082008-08-17 11:26:42 -04001// Code to load disk image and start system boot.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05002//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05008#include "util.h" // irq_enable
Kevin O'Connor9521e262008-07-04 13:04:29 -04009#include "biosvar.h" // GET_EBDA
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -050010#include "config.h" // CONFIG_*
Kevin O'Connor180a9592008-03-04 22:50:53 -050011#include "disk.h" // cdrom_boot
Kevin O'Connor9521e262008-07-04 13:04:29 -040012#include "bregs.h" // struct bregs
Kevin O'Connorc659fde2008-12-28 23:43:20 -050013#include "boot.h" // struct ipl_s
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050014#include "cmos.h" // inb_cmos
Kevin O'Connorc659fde2008-12-28 23:43:20 -050015
16struct ipl_s IPL;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050017
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050018
19/****************************************************************
Kevin O'Connor0a924122009-02-08 19:43:47 -050020 * IPL and BCV handlers
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050021 ****************************************************************/
22
23void
24boot_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
Kevin O'Connor0a924122009-02-08 19:43:47 -050033 struct ipl_entry_s *ie = &IPL.bev[0];
Kevin O'Connor71f036d2009-02-08 16:57:22 -050034 ie->type = IPL_TYPE_FLOPPY;
35 ie->description = "Floppy";
36 ie++;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050037
38 // First HDD
Kevin O'Connor71f036d2009-02-08 16:57:22 -050039 ie->type = IPL_TYPE_HARDDISK;
40 ie->description = "Hard Disk";
41 ie++;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050042
43 // CDROM
44 if (CONFIG_CDROM_BOOT) {
Kevin O'Connor71f036d2009-02-08 16:57:22 -050045 ie->type = IPL_TYPE_CDROM;
46 ie->description = "CD-Rom";
47 ie++;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050048 }
49
Kevin O'Connor0a924122009-02-08 19:43:47 -050050 IPL.bevcount = ie - IPL.bev;
51 IPL.bcv_override = -1;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050052 SET_EBDA(boot_sequence, 0xffff);
53 if (CONFIG_COREBOOT) {
54 // XXX - hardcode defaults for coreboot.
55 IPL.bootorder = 0x00000231;
56 IPL.checkfloppysig = 1;
57 } else {
58 // On emulators, get boot order from nvram.
59 IPL.bootorder = (inb_cmos(CMOS_BIOS_BOOTFLAG2)
60 | ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4));
61 if (!(inb_cmos(CMOS_BIOS_BOOTFLAG1) & 1))
62 IPL.checkfloppysig = 1;
63 }
64}
65
66// Add a BEV vector for a given pnp compatible option rom.
67void
68add_bev(u16 seg, u16 bev, u16 desc)
69{
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050070 if (! CONFIG_BOOT)
71 return;
Kevin O'Connor0a924122009-02-08 19:43:47 -050072 if (IPL.bevcount >= ARRAY_SIZE(IPL.bev))
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050073 return;
74
Kevin O'Connor0a924122009-02-08 19:43:47 -050075 struct ipl_entry_s *ie = &IPL.bev[IPL.bevcount++];
Kevin O'Connor71f036d2009-02-08 16:57:22 -050076 ie->type = IPL_TYPE_BEV;
77 ie->vector = (seg << 16) | bev;
Kevin O'Connor0a924122009-02-08 19:43:47 -050078 const char *d = "Unknown";
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050079 if (desc)
Kevin O'Connor0a924122009-02-08 19:43:47 -050080 d = MAKE_FLATPTR(seg, desc);
81 ie->description = d;
82}
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -050083
Kevin O'Connor0a924122009-02-08 19:43:47 -050084// Add a bcv entry for an expansion card harddrive or legacy option rom
85void
86add_bcv(u16 seg, u16 ip, u16 desc)
87{
88 if (! CONFIG_BOOT)
89 return;
90 if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv))
91 return;
92
93 struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++];
94 ie->type = IPL_TYPE_BEV;
95 ie->vector = (seg << 16) | ip;
96 const char *d = "Legacy option rom";
97 if (desc)
98 d = MAKE_FLATPTR(seg, desc);
99 ie->description = d;
100}
101
102// Add a bcv entry for an ata harddrive
103void
104add_bcv_hd(int driveid, const char *desc)
105{
106 if (! CONFIG_BOOT)
107 return;
108 if (IPL.bcvcount >= ARRAY_SIZE(IPL.bcv))
109 return;
110
111 struct ipl_entry_s *ie = &IPL.bcv[IPL.bcvcount++];
112 ie->type = IPL_TYPE_HARDDISK;
113 ie->vector = driveid;
114 ie->description = desc;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500115}
116
117
118/****************************************************************
Kevin O'Connor0a924122009-02-08 19:43:47 -0500119 * Boot menu and BCV execution
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500120 ****************************************************************/
121
Kevin O'Connor0a924122009-02-08 19:43:47 -0500122// Show a generic menu item
123static int
124menu_show_default(struct ipl_entry_s *ie, int menupos)
125{
126 char desc[33];
127 printf("%d. %s\n", menupos
128 , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
129 return 1;
130}
131
132// Show floppy menu item - but only if there exists a floppy drive.
133static int
134menu_show_floppy(struct ipl_entry_s *ie, int menupos)
135{
136 if (!FloppyCount)
137 return 0;
138 return menu_show_default(ie, menupos);
139}
140
141// Show menu items from BCV list.
142static int
143menu_show_harddisk(struct ipl_entry_s *ie, int menupos)
144{
145 int i;
146 for (i = 0; i < IPL.bcvcount; i++) {
147 struct ipl_entry_s *ie = &IPL.bcv[i];
148 switch (ie->type) {
149 case IPL_TYPE_HARDDISK:
150 printf("%d. ata%d-%d %s\n", menupos + i
151 , ie->vector / 2, ie->vector %2, ie->description);
152 break;
153 default:
154 menu_show_default(ie, menupos+i);
155 break;
156 }
157 }
158 return IPL.bcvcount;
159}
160
161// Show cdrom menu item - but only if there exists a cdrom drive.
162static int
163menu_show_cdrom(struct ipl_entry_s *ie, int menupos)
164{
165 if (!ATA.cdcount)
166 return 0;
167 return menu_show_default(ie, menupos);
168}
169
170// Show IPL option menu.
171static void
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500172interactive_bootmenu()
173{
174 if (! CONFIG_BOOTMENU)
175 return;
176
177 while (get_keystroke(0) >= 0)
178 ;
179
180 printf("Press F12 for boot menu.\n\n");
181
182 int scan_code = get_keystroke(2500);
183 if (scan_code != 0x86)
184 /* not F12 */
185 return;
186
187 while (get_keystroke(0) >= 0)
188 ;
189
190 printf("Select boot device:\n\n");
191
Kevin O'Connor0a924122009-02-08 19:43:47 -0500192 int subcount[ARRAY_SIZE(IPL.bev)];
193 int menupos = 1;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500194 int i;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500195 for (i = 0; i < IPL.bevcount; i++) {
196 struct ipl_entry_s *ie = &IPL.bev[i];
197 int sc = 1;
198 switch (ie->type) {
199 case IPL_TYPE_FLOPPY:
200 sc = menu_show_floppy(ie, menupos);
201 break;
202 case IPL_TYPE_HARDDISK:
203 sc = menu_show_harddisk(ie, menupos);
204 break;
205 case IPL_TYPE_CDROM:
206 sc = menu_show_cdrom(ie, menupos);
207 break;
208 default:
209 sc = menu_show_default(ie, menupos);
210 break;
211 }
212 subcount[i] = sc;
213 menupos += sc;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500214 }
215
216 for (;;) {
217 scan_code = get_keystroke(1000);
218 if (scan_code == 0x01)
219 // ESC
220 break;
Kevin O'Connor0a924122009-02-08 19:43:47 -0500221 if (scan_code < 1 || scan_code > menupos)
222 continue;
223 int choice = scan_code - 1;
224
225 // Find out which IPL this was for.
226 int bev = 0;
227 while (choice > subcount[bev]) {
228 choice -= subcount[bev];
229 bev++;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500230 }
Kevin O'Connor0a924122009-02-08 19:43:47 -0500231
232 // A harddrive request enables a BCV order.
233 if (IPL.bev[bev].type == IPL_TYPE_HARDDISK)
234 IPL.bcv_override = choice-1;
235
236 // Add user choice to the boot order.
237 IPL.bootorder = (IPL.bootorder << 4) | (bev+1);
238 break;
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500239 }
240 printf("\n");
241}
242
Kevin O'Connor0a924122009-02-08 19:43:47 -0500243// Run the specified bcv.
244static void
245run_bcv(struct ipl_entry_s *ie)
246{
247 switch (ie->type) {
248 case IPL_TYPE_HARDDISK:
249 map_drive(ie->vector);
250 break;
251 case IPL_TYPE_BEV:
252 call_bcv(ie->vector >> 16, ie->vector & 0xffff);
253 break;
254 }
255}
256
257// Prepare for boot - show menu and run bcvs.
258void
259boot_prep()
260{
261 if (! CONFIG_BOOT)
262 return;
263
264 // Allow user to modify BCV/IPL order.
265 interactive_bootmenu();
266
267 // Run BCVs
268 int override = IPL.bcv_override;
269 if (override >= 0)
270 run_bcv(&IPL.bcv[override]);
271 int i;
272 for (i=0; i<IPL.bcvcount; i++)
273 if (i != override)
274 run_bcv(&IPL.bcv[i]);
275}
276
Kevin O'Connor9f4e1d92009-02-08 15:44:08 -0500277
278/****************************************************************
279 * Boot code (int 18/19)
280 ****************************************************************/
281
Kevin O'Connor0a924122009-02-08 19:43:47 -0500282// Jump to a bootup entry point.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500283static void
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500284call_boot_entry(u16 bootseg, u16 bootip, u8 bootdrv)
285{
286 dprintf(1, "Booting from %x:%x\n", bootseg, bootip);
287
288 struct bregs br;
289 memset(&br, 0, sizeof(br));
290 br.ip = bootip;
291 br.cs = bootseg;
292 // Set the magic number in ax and the boot drive in dl.
293 br.dl = bootdrv;
294 br.ax = 0xaa55;
295 call16(&br);
296}
297
298// Boot from a disk (either floppy or harddrive)
299static void
300boot_disk(u8 bootdrv, int checksig)
301{
302 u16 bootseg = 0x07c0;
303
304 // Read sector
305 struct bregs br;
306 memset(&br, 0, sizeof(br));
307 br.dl = bootdrv;
308 br.es = bootseg;
309 br.ah = 2;
310 br.al = 1;
311 br.cl = 1;
312 call16_int(0x13, &br);
313
314 if (br.flags & F_CF) {
315 printf("Boot failed: could not read the boot disk\n\n");
316 return;
317 }
318
319 if (checksig) {
320 struct mbr_s *mbr = (void*)0;
321 if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
322 printf("Boot failed: not a bootable disk\n\n");
323 return;
324 }
325 }
326
327 /* Canonicalize bootseg:bootip */
328 u16 bootip = (bootseg & 0x0fff) << 4;
329 bootseg &= 0xf000;
330
331 call_boot_entry(bootseg, bootip, bootdrv);
332}
333
334// Boot from a CD-ROM
335static void
336boot_cdrom()
337{
338 if (! CONFIG_CDROM_BOOT)
339 return;
340 int status = cdrom_boot();
341 if (status) {
342 printf("Boot failed: Could not read from CDROM (code %04x)\n", status);
343 return;
344 }
345
346 u16 ebda_seg = get_ebda_seg();
347 u8 bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
348 u16 bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
349 /* Canonicalize bootseg:bootip */
350 u16 bootip = (bootseg & 0x0fff) << 4;
351 bootseg &= 0xf000;
352
353 call_boot_entry(bootseg, bootip, bootdrv);
354}
355
356static void
357do_boot(u16 seq_nr)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500358{
Kevin O'Connor40967022008-07-21 22:23:05 -0400359 if (! CONFIG_BOOT)
Kevin O'Connore07e18e2009-02-08 17:07:29 -0500360 panic("Boot support not compiled in.\n");
Kevin O'Connor40967022008-07-21 22:23:05 -0400361
Kevin O'Connorc659fde2008-12-28 23:43:20 -0500362 u32 bootdev = IPL.bootorder;
Kevin O'Connor840c5342008-03-29 14:04:34 -0400363 bootdev >>= 4 * seq_nr;
364 bootdev &= 0xf;
Kevin O'Connore0113c92008-04-05 15:51:12 -0400365
Kevin O'Connor840c5342008-03-29 14:04:34 -0400366 if (bootdev == 0)
Kevin O'Connore07e18e2009-02-08 17:07:29 -0500367 panic("No bootable device.\n");
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500368
Kevin O'Connorabc75972008-05-18 00:20:53 -0400369 /* Translate bootdev to an IPL table offset by subtracting 1 */
Kevin O'Connor840c5342008-03-29 14:04:34 -0400370 bootdev -= 1;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500371
Kevin O'Connor0a924122009-02-08 19:43:47 -0500372 if (bootdev >= IPL.bevcount) {
Kevin O'Connorac8df8c2008-05-24 23:46:33 -0400373 dprintf(1, "Invalid boot device (0x%x)\n", bootdev);
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500374 goto fail;
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500375 }
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500376
377 /* Do the loading, and set up vector as a far pointer to the boot
378 * address, and bootdrv as the boot drive */
Kevin O'Connor0a924122009-02-08 19:43:47 -0500379 struct ipl_entry_s *ie = &IPL.bev[bootdev];
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500380 char desc[33];
381 printf("Booting from %s...\n"
382 , strtcpy(desc, ie->description, ARRAY_SIZE(desc)));
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500383
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500384 switch(ie->type) {
Kevin O'Connorf13b0082008-08-17 11:26:42 -0400385 case IPL_TYPE_FLOPPY:
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500386 boot_disk(0x00, IPL.checkfloppysig);
387 break;
Kevin O'Connorf13b0082008-08-17 11:26:42 -0400388 case IPL_TYPE_HARDDISK:
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500389 boot_disk(0x80, 1);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500390 break;
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500391 case IPL_TYPE_CDROM:
392 boot_cdrom();
393 break;
394 case IPL_TYPE_BEV:
395 call_boot_entry(ie->vector >> 16, ie->vector & 0xffff, 0);
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500396 break;
Kevin O'Connor180a9592008-03-04 22:50:53 -0500397 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500398
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500399 // Boot failed: invoke the boot recovery function
Kevin O'Connor7af49bf2008-03-09 13:46:13 -0400400 struct bregs br;
Kevin O'Connor71f036d2009-02-08 16:57:22 -0500401fail:
Kevin O'Connor7af49bf2008-03-09 13:46:13 -0400402 memset(&br, 0, sizeof(br));
403 call16_int(0x18, &br);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500404}
405
406// Boot Failure recovery: try the next device.
Kevin O'Connor44eeaf12008-07-06 10:14:49 -0400407void VISIBLE32
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500408handle_18()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500409{
Kevin O'Connor61d6b062008-06-21 12:15:10 -0400410 debug_serial_setup();
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400411 debug_enter(NULL, DEBUG_HDL_18);
Kevin O'Connor08815372008-12-29 21:16:31 -0500412 u16 ebda_seg = get_ebda_seg();
413 u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
414 SET_EBDA2(ebda_seg, boot_sequence, seq);
Kevin O'Connor7af49bf2008-03-09 13:46:13 -0400415 do_boot(seq);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500416}
417
418// INT 19h Boot Load Service Entry Point
Kevin O'Connor44eeaf12008-07-06 10:14:49 -0400419void VISIBLE32
Kevin O'Connor38fcbfe2008-02-25 22:30:47 -0500420handle_19()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500421{
Kevin O'Connor61d6b062008-06-21 12:15:10 -0400422 debug_serial_setup();
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400423 debug_enter(NULL, DEBUG_HDL_19);
Kevin O'Connor08815372008-12-29 21:16:31 -0500424 SET_EBDA(boot_sequence, 0);
Kevin O'Connor7af49bf2008-03-09 13:46:13 -0400425 do_boot(0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500426}
Kevin O'Connorcdd3d652008-07-12 14:22:14 -0400427
428// Ughh - some older gcc compilers have a bug which causes VISIBLE32
Kevin O'Connorf13b0082008-08-17 11:26:42 -0400429// functions to not be exported as global variables.
Kevin O'Connorcdd3d652008-07-12 14:22:14 -0400430asm(".global handle_18, handle_19");