Make disk access work.
[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 "cmos.h" // inb_cmos
12 #include "ata.h" // ata_detect
13
14 //--------------------------------------------------------------------------
15 // print_boot_device
16 //   displays the boot device
17 //--------------------------------------------------------------------------
18
19 static const char drivetypes[][10]={
20     "", "Floppy","Hard Disk","CD-Rom", "Network"
21 };
22
23 static void
24 print_boot_device(u16 type)
25 {
26     /* NIC appears as type 0x80 */
27     if (type == IPL_TYPE_BEV)
28         type = 0x4;
29     if (type == 0 || type > 0x4)
30         BX_PANIC("Bad drive type\n");
31     printf("Booting from %s...\n", drivetypes[type]);
32
33     // XXX - latest cvs has BEV description
34 }
35
36 //--------------------------------------------------------------------------
37 // print_boot_failure
38 //   displays the reason why boot failed
39 //--------------------------------------------------------------------------
40 static void
41 print_boot_failure(u16 type, u8 reason)
42 {
43     if (type == 0 || type > 0x3)
44         BX_PANIC("Bad drive type\n");
45
46     printf("Boot failed");
47     if (type < 4) {
48         /* Report the reason too */
49         if (reason==0)
50             printf(": not a bootable disk");
51         else
52             printf(": could not read the boot disk");
53     }
54     printf("\n\n");
55 }
56
57 static void
58 try_boot(u16 seq_nr)
59 {
60     SET_IPL(sequence, seq_nr);
61     u16 bootseg;
62     u8 bootdrv = 0;
63     u16 bootdev, bootip;
64
65     if (CONFIG_ELTORITO_BOOT) {
66         bootdev = inb_cmos(CMOS_BIOS_BOOTFLAG2);
67         bootdev |= ((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0xf0) << 4);
68         bootdev >>= 4 * seq_nr;
69         bootdev &= 0xf;
70         if (bootdev == 0)
71             BX_PANIC("No bootable device.\n");
72
73         /* Translate from CMOS runes to an IPL table offset by subtracting 1 */
74         bootdev -= 1;
75     } else {
76         if (seq_nr ==2)
77             BX_PANIC("No more boot devices.");
78         if (!!(inb_cmos(CMOS_BIOS_CONFIG) & 0x20) ^ (seq_nr == 1))
79             /* Boot from floppy if the bit is set or it's the second boot */
80             bootdev = 0x00;
81         else
82             bootdev = 0x01;
83     }
84
85     if (bootdev >= GET_IPL(count)) {
86         BX_INFO("Invalid boot device (0x%x)\n", bootdev);
87         return;
88     }
89     u16 type = GET_IPL(table[bootdev].type);
90
91     /* Do the loading, and set up vector as a far pointer to the boot
92      * address, and bootdrv as the boot drive */
93     print_boot_device(type);
94
95     struct bregs cr;
96     switch(type) {
97     case IPL_TYPE_FLOPPY: /* FDD */
98     case IPL_TYPE_HARDDISK: /* HDD */
99
100         bootdrv = (type == IPL_TYPE_HARDDISK) ? 0x80 : 0x00;
101         bootseg = 0x07c0;
102
103         // Read sector
104         memset(&cr, 0, sizeof(cr));
105         cr.dl = bootdrv;
106         cr.es = bootseg;
107         cr.ah = 2;
108         cr.al = 1;
109         cr.cl = 1;
110         call16_int(0x13, &cr);
111
112         if (cr.flags & F_CF) {
113             print_boot_failure(type, 1);
114             return;
115         }
116
117         /* Always check the signature on a HDD boot sector; on FDD,
118          * only do the check if the CMOS doesn't tell us to skip it */
119         if ((type != IPL_TYPE_FLOPPY)
120             || !((inb_cmos(CMOS_BIOS_BOOTFLAG1) & 0x01))) {
121             if (GET_FARVAR(bootseg, *(u16*)0x1fe) != 0xaa55) {
122                 print_boot_failure(type, 0);
123                 return;
124             }
125         }
126
127         /* Canonicalize bootseg:bootip */
128         bootip = (bootseg & 0x0fff) << 4;
129         bootseg &= 0xf000;
130         break;
131     case IPL_TYPE_CDROM: /* CD-ROM */
132         // XXX
133         return;
134         break;
135     case IPL_TYPE_BEV: {
136         /* Expansion ROM with a Bootstrap Entry Vector (a far
137          * pointer) */
138         u32 vector = GET_IPL(table[bootdev].vector);
139         bootseg = vector >> 16;
140         bootip = vector & 0xffff;
141         break;
142     }
143     default:
144         return;
145     }
146
147     memset(&cr, 0, sizeof(cr));
148     cr.ip = bootip;
149     cr.cs = bootseg;
150     // Set the magic number in ax and the boot drive in dl.
151     cr.dl = bootdrv;
152     cr.ax = 0xaa55;
153     call16(&cr);
154
155     // Boot failed: invoke the boot recovery function
156     memset(&cr, 0, sizeof(cr));
157     call16_int(0x18, &cr);
158 }
159
160 // Boot Failure recovery: try the next device.
161 void VISIBLE
162 handle_18()
163 {
164     debug_enter(NULL);
165     u16 seq = GET_IPL(sequence) + 1;
166     try_boot(seq);
167 }
168
169 // INT 19h Boot Load Service Entry Point
170 void VISIBLE
171 handle_19()
172 {
173     debug_enter(NULL);
174     try_boot(0);
175 }
176
177 // Called from 32bit code - start boot process
178 void VISIBLE
179 begin_boot()
180 {
181     ata_detect();
182     irq_enable();
183     struct bregs br;
184     memset(&br, 0, sizeof(br));
185     call16_int(0x19, &br);
186 }