Improve some debugging output.
[seabios.git] / src / boot.c
1 // 16bit 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" // struct bregs
10 #include "config.h" // CONFIG_*
11 #include "ata.h" // ata_detect
12 #include "disk.h" // cdrom_boot
13
14 // We need a copy of this string, but we are not actually a PnP BIOS,
15 // so make sure it is *not* aligned, so OSes will not see it if they
16 // scan.
17 char pnp_string[] VISIBLE16 __attribute__((aligned (2))) = " $PnP";
18
19 //--------------------------------------------------------------------------
20 // print_boot_device
21 //   displays the boot device
22 //--------------------------------------------------------------------------
23
24 static const char drivetypes[][10]={
25     "", "Floppy", "Hard Disk", "CD-Rom", "Network"
26 };
27
28 void
29 printf_bootdev(u16 bootdev)
30 {
31     u16 type = GET_EBDA(ipl.table[bootdev].type);
32
33     /* NIC appears as type 0x80 */
34     if (type == IPL_TYPE_BEV)
35         type = 0x4;
36     if (type == 0 || type > 0x4)
37         BX_PANIC("Bad drive type\n");
38     printf("%s", drivetypes[type]);
39
40     /* print product string if BEV */
41     void *far_description = (void*)GET_EBDA(ipl.table[bootdev].description);
42     if (type == 4 && far_description != 0) {
43         char description[33];
44         /* first 32 bytes are significant */
45         memcpy(MAKE_FARPTR(GET_SEG(SS), &description), far_description, 32);
46         /* terminate string */
47         description[32] = 0;
48         printf(" [%.s]", description);
49     }
50 }
51
52 static void
53 print_boot_device(u16 bootdev)
54 {
55     printf("Booting from ");
56     printf_bootdev(bootdev);
57     printf("...\n");
58 }
59
60 //--------------------------------------------------------------------------
61 // print_boot_failure
62 //   displays the reason why boot failed
63 //--------------------------------------------------------------------------
64 static void
65 print_boot_failure(u16 type, u8 reason)
66 {
67     if (type == 0 || type > 0x3)
68         BX_PANIC("Bad drive type\n");
69
70     printf("Boot failed");
71     if (type < 4) {
72         /* Report the reason too */
73         if (reason==0)
74             printf(": not a bootable disk");
75         else
76             printf(": could not read the boot disk");
77     }
78     printf("\n\n");
79 }
80
81 static void
82 try_boot(u16 seq_nr)
83 {
84     irq_enable();
85
86     SET_EBDA(ipl.sequence, seq_nr);
87
88     u32 bootdev = GET_EBDA(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 >= GET_EBDA(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 = GET_EBDA(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: /* FDD */
114     case IPL_TYPE_HARDDISK: /* HDD */
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 || GET_EBDA(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             break;
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_EBDA(cdemu.emulated_drive);
158         bootseg = GET_EBDA(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 = GET_EBDA(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 VISIBLE16
201 handle_18()
202 {
203     debug_serial_setup();
204     debug_enter(NULL, DEBUG_HDL_18);
205     u16 seq = GET_EBDA(ipl.sequence) + 1;
206     do_boot(seq);
207 }
208
209 // INT 19h Boot Load Service Entry Point
210 void VISIBLE16
211 handle_19()
212 {
213     debug_serial_setup();
214     debug_enter(NULL, DEBUG_HDL_19);
215     do_boot(0);
216 }