Implement memcpy_far and checksum_far, and replace _fl variants.
[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             if (GET_FARVAR(bootseg, *(u16*)0x1fe) != 0xaa55) {
134                 print_boot_failure(type, 0);
135                 return;
136             }
137         }
138
139         /* Canonicalize bootseg:bootip */
140         bootip = (bootseg & 0x0fff) << 4;
141         bootseg &= 0xf000;
142         break;
143     case IPL_TYPE_CDROM: {
144         /* CD-ROM */
145         if (! CONFIG_CDROM_BOOT)
146             return;
147         int status = cdrom_boot();
148         if (status) {
149             printf("CDROM boot failure code : %04x\n", status);
150             print_boot_failure(type, 1);
151             return;
152         }
153
154         u16 ebda_seg = get_ebda_seg();
155         bootdrv = GET_EBDA2(ebda_seg, cdemu.emulated_drive);
156         bootseg = GET_EBDA2(ebda_seg, cdemu.load_segment);
157         /* Canonicalize bootseg:bootip */
158         bootip = (bootseg & 0x0fff) << 4;
159         bootseg &= 0xf000;
160         break;
161     }
162     case IPL_TYPE_BEV: {
163         /* Expansion ROM with a Bootstrap Entry Vector (a far
164          * pointer) */
165         u32 vector = IPL.table[bootdev].vector;
166         bootseg = vector >> 16;
167         bootip = vector & 0xffff;
168         break;
169     }
170     default:
171         return;
172     }
173
174     /* Debugging info */
175     dprintf(1, "Booting from %x:%x\n", bootseg, bootip);
176
177     memset(&cr, 0, sizeof(cr));
178     cr.ip = bootip;
179     cr.cs = bootseg;
180     // Set the magic number in ax and the boot drive in dl.
181     cr.dl = bootdrv;
182     cr.ax = 0xaa55;
183     call16(&cr);
184 }
185
186 static void
187 do_boot(u16 seq_nr)
188 {
189     try_boot(seq_nr);
190
191     // Boot failed: invoke the boot recovery function
192     struct bregs br;
193     memset(&br, 0, sizeof(br));
194     call16_int(0x18, &br);
195 }
196
197 // Boot Failure recovery: try the next device.
198 void VISIBLE32
199 handle_18()
200 {
201     debug_serial_setup();
202     debug_enter(NULL, DEBUG_HDL_18);
203     u16 ebda_seg = get_ebda_seg();
204     u16 seq = GET_EBDA2(ebda_seg, boot_sequence) + 1;
205     SET_EBDA2(ebda_seg, boot_sequence, seq);
206     do_boot(seq);
207 }
208
209 // INT 19h Boot Load Service Entry Point
210 void VISIBLE32
211 handle_19()
212 {
213     debug_serial_setup();
214     debug_enter(NULL, DEBUG_HDL_19);
215     SET_EBDA(boot_sequence, 0);
216     do_boot(0);
217 }
218
219 // Ughh - some older gcc compilers have a bug which causes VISIBLE32
220 // functions to not be exported as global variables.
221 asm(".global handle_18, handle_19");