Introduce MBR struct; simplify cdrom emulation 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
15 struct ipl_s IPL;
16
17 //--------------------------------------------------------------------------
18 // print_boot_device
19 //   displays the boot device
20 //--------------------------------------------------------------------------
21
22 static const char drivetypes[][10]={
23     "", "Floppy", "Hard Disk", "CD-Rom", "Network"
24 };
25
26 void
27 printf_bootdev(u16 bootdev)
28 {
29     u16 type = IPL.table[bootdev].type;
30
31     /* NIC appears as type 0x80 */
32     if (type == IPL_TYPE_BEV)
33         type = 0x4;
34     if (type == 0 || type > 0x4)
35         BX_PANIC("Bad drive type\n");
36     printf("%s", drivetypes[type]);
37
38     /* print product string if BEV */
39     char *description_fl = IPL.table[bootdev].description;
40     if (type == 4 && description_fl != 0) {
41         char description[33];
42         /* first 32 bytes are significant */
43         memcpy(description, description_fl, 32);
44         /* terminate string */
45         description[32] = 0;
46         printf(" [%.s]", description);
47     }
48 }
49
50 static void
51 print_boot_device(u16 bootdev)
52 {
53     printf("Booting from ");
54     printf_bootdev(bootdev);
55     printf("...\n");
56 }
57
58 //--------------------------------------------------------------------------
59 // print_boot_failure
60 //   displays the reason why boot failed
61 //--------------------------------------------------------------------------
62 static void
63 print_boot_failure(u16 type, u8 reason)
64 {
65     if (type == 0 || type > 0x3)
66         BX_PANIC("Bad drive type\n");
67
68     printf("Boot failed");
69     if (type < 4) {
70         /* Report the reason too */
71         if (reason==0)
72             printf(": not a bootable disk");
73         else
74             printf(": could not read the boot disk");
75     }
76     printf("\n\n");
77 }
78
79 static void
80 try_boot(u16 seq_nr)
81 {
82     if (! CONFIG_BOOT)
83         BX_PANIC("Boot support not compiled in.\n");
84
85     u32 bootdev = IPL.bootorder;
86     bootdev >>= 4 * seq_nr;
87     bootdev &= 0xf;
88
89     if (bootdev == 0)
90         BX_PANIC("No bootable device.\n");
91
92     /* Translate bootdev to an IPL table offset by subtracting 1 */
93     bootdev -= 1;
94
95     if (bootdev >= IPL.count) {
96         dprintf(1, "Invalid boot device (0x%x)\n", bootdev);
97         return;
98     }
99
100     /* Do the loading, and set up vector as a far pointer to the boot
101      * address, and bootdrv as the boot drive */
102     print_boot_device(bootdev);
103
104     u16 type = IPL.table[bootdev].type;
105
106     u16 bootseg, bootip;
107     u8 bootdrv = 0;
108     struct bregs cr;
109     switch(type) {
110     case IPL_TYPE_FLOPPY:
111     case IPL_TYPE_HARDDISK:
112
113         bootdrv = (type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
114         bootseg = 0x07c0;
115
116         // Read sector
117         memset(&cr, 0, sizeof(cr));
118         cr.dl = bootdrv;
119         cr.es = bootseg;
120         cr.ah = 2;
121         cr.al = 1;
122         cr.cl = 1;
123         call16_int(0x13, &cr);
124
125         if (cr.flags & F_CF) {
126             print_boot_failure(type, 1);
127             return;
128         }
129
130         /* Always check the signature on a HDD boot sector; on FDD,
131          * only do the check if configured for it */
132         if (type != IPL_TYPE_FLOPPY || IPL.checkfloppysig) {
133             struct mbr_s *mbr = (void*)0;
134             if (GET_FARVAR(bootseg, mbr->signature) != MBR_SIGNATURE) {
135                 print_boot_failure(type, 0);
136                 return;
137             }
138         }
139
140         /* Canonicalize bootseg:bootip */
141         bootip = (bootseg & 0x0fff) << 4;
142         bootseg &= 0xf000;
143         break;
144     case IPL_TYPE_CDROM: {
145         /* CD-ROM */
146         if (! CONFIG_CDROM_BOOT)
147             return;
148         int status = cdrom_boot();
149         if (status) {
150             printf("CDROM boot failure code : %04x\n", status);
151             print_boot_failure(type, 1);
152             return;
153         }
154
155         u16 ebda_seg = get_ebda_seg();
156         bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
157         bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
158         /* Canonicalize bootseg:bootip */
159         bootip = (bootseg & 0x0fff) << 4;
160         bootseg &= 0xf000;
161         break;
162     }
163     case IPL_TYPE_BEV: {
164         /* Expansion ROM with a Bootstrap Entry Vector (a far
165          * pointer) */
166         u32 vector = IPL.table[bootdev].vector;
167         bootseg = vector >> 16;
168         bootip = vector & 0xffff;
169         break;
170     }
171     default:
172         return;
173     }
174
175     /* Debugging info */
176     dprintf(1, "Booting from %x:%x\n", bootseg, bootip);
177
178     memset(&cr, 0, sizeof(cr));
179     cr.ip = bootip;
180     cr.cs = bootseg;
181     // Set the magic number in ax and the boot drive in dl.
182     cr.dl = bootdrv;
183     cr.ax = 0xaa55;
184     call16(&cr);
185 }
186
187 static void
188 do_boot(u16 seq_nr)
189 {
190     try_boot(seq_nr);
191
192     // Boot failed: invoke the boot recovery function
193     struct bregs br;
194     memset(&br, 0, sizeof(br));
195     call16_int(0x18, &br);
196 }
197
198 // Boot Failure recovery: try the next device.
199 void VISIBLE32
200 handle_18()
201 {
202     debug_serial_setup();
203     debug_enter(NULL, DEBUG_HDL_18);
204     u16 ebda_seg = get_ebda_seg();
205     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
206     SET_EBDA2(ebda_seg, boot_sequence, seq);
207     do_boot(seq);
208 }
209
210 // INT 19h Boot Load Service Entry Point
211 void VISIBLE32
212 handle_19()
213 {
214     debug_serial_setup();
215     debug_enter(NULL, DEBUG_HDL_19);
216     SET_EBDA(boot_sequence, 0);
217     do_boot(0);
218 }
219
220 // Ughh - some older gcc compilers have a bug which causes VISIBLE32
221 // functions to not be exported as global variables.
222 asm(".global handle_18, handle_19");