Return CDEMU parameters to ebda segment (instead of using globals).
[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     u32 bootdev = IPL.bootorder;
87     bootdev >>= 4 * seq_nr;
88     bootdev &= 0xf;
89
90     if (bootdev == 0)
91         BX_PANIC("No bootable device.\n");
92
93     /* Translate bootdev to an IPL table offset by subtracting 1 */
94     bootdev -= 1;
95
96     if (bootdev >= IPL.count) {
97         dprintf(1, "Invalid boot device (0x%x)\n", bootdev);
98         return;
99     }
100
101     /* Do the loading, and set up vector as a far pointer to the boot
102      * address, and bootdrv as the boot drive */
103     print_boot_device(bootdev);
104
105     u16 type = IPL.table[bootdev].type;
106
107     u16 bootseg, bootip;
108     u8 bootdrv = 0;
109     struct bregs cr;
110     switch(type) {
111     case IPL_TYPE_FLOPPY:
112     case IPL_TYPE_HARDDISK:
113
114         bootdrv = (type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
115         bootseg = 0x07c0;
116
117         // Read sector
118         memset(&cr, 0, sizeof(cr));
119         cr.dl = bootdrv;
120         cr.es = bootseg;
121         cr.ah = 2;
122         cr.al = 1;
123         cr.cl = 1;
124         call16_int(0x13, &cr);
125
126         if (cr.flags & F_CF) {
127             print_boot_failure(type, 1);
128             return;
129         }
130
131         /* Always check the signature on a HDD boot sector; on FDD,
132          * only do the check if configured for it */
133         if (type != IPL_TYPE_FLOPPY || IPL.checkfloppysig) {
134             if (GET_FARVAR(bootseg, *(u16*)0x1fe) != 0xaa55) {
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");