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