core: do aligned transfers in bcopy32
[syslinux.git] / com32 / modules / chain.c
blobec92cd0c183a631a640be95ac1136b46d860d8d9
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
14 * chain.c
16 * Chainload a hard disk (currently rather braindead.)
18 * Usage: chain hd<disk#> [<partition>]
19 * chain fd<disk#>
20 * chain mbr:<id> [<partition>]
22 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
23 * disk.
25 * The mbr: syntax means search all the hard disks until one with a
26 * specific MBR serial number (bytes 440-443) is found.
28 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
31 #include <com32.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <console.h>
38 #define SECTOR 512 /* bytes/sector */
40 static inline void error(const char *msg)
42 fputs(msg, stderr);
46 * Call int 13h, but with retry on failure. Especially floppies need this.
48 int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
50 int retry = 6; /* Number of retries */
51 com32sys_t tmpregs;
53 if ( !outreg ) outreg = &tmpregs;
55 while ( retry-- ) {
56 __intcall(0x13, inreg, outreg);
57 if ( !(outreg->eflags.l & EFLAGS_CF) )
58 return 0; /* CF=0, OK */
61 return -1; /* Error */
65 * Query disk parameters and EBIOS availability for a particular disk.
67 struct diskinfo {
68 int disk;
69 int ebios; /* EBIOS supported on this disk */
70 int cbios; /* CHS geometry is valid */
71 int head;
72 int sect;
73 } disk_info;
75 int get_disk_params(int disk)
77 static com32sys_t getparm, parm, getebios, ebios;
79 disk_info.disk = disk;
80 disk_info.ebios = disk_info.cbios = 0;
82 /* Get EBIOS support */
83 getebios.eax.w[0] = 0x4100;
84 getebios.ebx.w[0] = 0x55aa;
85 getebios.edx.b[0] = disk;
86 getebios.eflags.b[0] = 0x3; /* CF set */
88 __intcall(0x13, &getebios, &ebios);
90 if ( !(ebios.eflags.l & EFLAGS_CF) &&
91 ebios.ebx.w[0] == 0xaa55 &&
92 (ebios.ecx.b[0] & 1) ) {
93 disk_info.ebios = 1;
96 /* Get disk parameters -- really only useful for
97 hard disks, but if we have a partitioned floppy
98 it's actually our best chance... */
99 getparm.eax.b[1] = 0x08;
100 getparm.edx.b[0] = disk;
102 __intcall(0x13, &getparm, &parm);
104 if ( parm.eflags.l & EFLAGS_CF )
105 return disk_info.ebios ? 0 : -1;
107 disk_info.head = parm.edx.b[1]+1;
108 disk_info.sect = parm.ecx.b[0] & 0x3f;
109 if ( disk_info.sect == 0 ) {
110 disk_info.sect = 1;
111 } else {
112 disk_info.cbios = 1; /* Valid geometry */
115 return 0;
119 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
121 struct ebios_dapa {
122 uint16_t len;
123 uint16_t count;
124 uint16_t off;
125 uint16_t seg;
126 uint64_t lba;
127 } *dapa;
129 int read_sector(void *buf, unsigned int lba)
131 com32sys_t inreg;
133 memset(&inreg, 0, sizeof inreg);
135 if ( disk_info.ebios ) {
136 dapa->len = sizeof(*dapa);
137 dapa->count = 1; /* 1 sector */
138 dapa->off = OFFS(buf);
139 dapa->seg = SEG(buf);
140 dapa->lba = lba;
142 inreg.esi.w[0] = OFFS(dapa);
143 inreg.ds = SEG(dapa);
144 inreg.edx.b[0] = disk_info.disk;
145 inreg.eax.b[1] = 0x42; /* Extended read */
146 } else {
147 unsigned int c, h, s, t;
149 if ( !disk_info.cbios ) {
150 /* We failed to get the geometry */
152 if ( lba )
153 return -1; /* Can only read MBR */
155 s = 1; h = 0; c = 0;
156 } else {
157 s = (lba % disk_info.sect) + 1;
158 t = lba / disk_info.sect; /* Track = head*cyl */
159 h = t % disk_info.head;
160 c = t / disk_info.head;
163 if ( s > 63 || h > 256 || c > 1023 )
164 return -1;
166 inreg.eax.w[0] = 0x0201; /* Read one sector */
167 inreg.ecx.b[1] = c & 0xff;
168 inreg.ecx.b[0] = s + (c >> 6);
169 inreg.edx.b[1] = h;
170 inreg.edx.b[0] = disk_info.disk;
171 inreg.ebx.w[0] = OFFS(buf);
172 inreg.es = SEG(buf);
175 return int13_retry(&inreg, NULL);
178 /* Search for a specific drive, based on the MBR signature; bytes
179 440-443. */
180 int find_disk(uint32_t mbr_sig, void *buf)
182 int drive;
184 for (drive = 0x80; drive <= 0xff; drive++) {
185 if (get_disk_params(drive))
186 continue; /* Drive doesn't exist */
187 if (read_sector(buf, 0))
188 continue; /* Cannot read sector */
190 if (*(uint32_t *)((char *)buf + 440) == mbr_sig)
191 return drive;
194 return -1;
197 /* A DOS partition table entry */
198 struct part_entry {
199 uint8_t active_flag; /* 0x80 if "active" */
200 uint8_t start_head;
201 uint8_t start_sect;
202 uint8_t start_cyl;
203 uint8_t ostype;
204 uint8_t end_head;
205 uint8_t end_sect;
206 uint8_t end_cyl;
207 uint32_t start_lba;
208 uint32_t length;
209 } __attribute__((packed));
212 /* Search for a logical partition. Logical partitions are actually implemented
213 as recursive partition tables; theoretically they're supposed to form a
214 linked list, but other structures have been seen.
216 To make things extra confusing: data partition offsets are relative to where
217 the data partition record is stored, whereas extended partition offsets
218 are relative to the beginning of the extended partition all the way back
219 at the MBR... but still not absolute! */
221 int nextpart; /* Number of the next logical partition */
223 struct part_entry *
224 find_logical_partition(int whichpart, char *table, struct part_entry *self,
225 struct part_entry *root)
227 struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
228 struct part_entry *found;
229 int i;
231 if ( *(uint16_t *)(table + 0x1fe) != 0xaa55 )
232 return NULL; /* Signature missing */
234 /* We are assumed to already having enumerated all the data partitions
235 in this table if this is the MBR. For MBR, self == NULL. */
237 if ( self ) {
238 /* Scan the data partitions. */
240 for ( i = 0 ; i < 4 ; i++ ) {
241 if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
242 ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
243 continue; /* Skip empty or extended partitions */
245 if ( !ptab[i].length )
246 continue;
248 /* Adjust the offset to account for the extended partition itself */
249 ptab[i].start_lba += self->start_lba;
251 /* Sanity check entry: must not extend outside the extended partition.
252 This is necessary since some OSes put crap in some entries. */
253 if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
254 ptab[i].start_lba >= self->start_lba + self->length )
255 continue;
257 /* OK, it's a data partition. Is it the one we're looking for? */
258 if ( nextpart++ == whichpart )
259 return &ptab[i];
263 /* Scan the extended partitions. */
264 for ( i = 0 ; i < 4 ; i++ ) {
265 if ( ptab[i].ostype != 0x05 &&
266 ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
267 continue; /* Skip empty or data partitions */
269 if ( !ptab[i].length )
270 continue;
272 /* Adjust the offset to account for the extended partition itself */
273 if ( root )
274 ptab[i].start_lba += root->start_lba;
276 /* Sanity check entry: must not extend outside the extended partition.
277 This is necessary since some OSes put crap in some entries. */
278 if ( root )
279 if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
280 ptab[i].start_lba >= root->start_lba + root->length )
281 continue;
283 /* Process this partition */
284 if ( read_sector(table+SECTOR, ptab[i].start_lba) )
285 continue; /* Read error, must be invalid */
287 if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
288 root ? root : &ptab[i])) )
289 return found;
292 /* If we get here, there ain't nothing... */
293 return NULL;
297 int main(int argc, char *argv[])
299 char *mbr, *boot_sector = NULL;
300 struct part_entry *partinfo;
301 char *drivename, *partition;
302 int hd, drive, whichpart;
303 static com32sys_t inreg; /* In bss, so zeroed automatically */
305 openconsole(&dev_null_r, &dev_stdcon_w);
307 if ( argc < 2 ) {
308 error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition]\n");
309 goto bail;
312 /* Divvy up the bounce buffer. To keep things sector-
313 aligned, give the EBIOS DAPA the first sector, then
314 the MBR next, and the rest is used for the partition-
315 chasing stack. */
316 dapa = (struct ebios_dapa *)__com32.cs_bounce;
317 mbr = (char *)__com32.cs_bounce + SECTOR;
319 drivename = argv[1];
320 partition = argv[2]; /* Possibly null */
322 hd = 0;
323 if ( !memcmp(drivename, "mbr:", 4) ) {
324 drive = find_disk(strtoul(drivename+4, NULL, 0), mbr);
325 if (drive == -1) {
326 error("Unable to find requested MBR signature\n");
327 goto bail;
329 } else {
330 if ( (drivename[0] == 'h' || drivename[0] == 'f') &&
331 drivename[1] == 'd' ) {
332 hd = drivename[0] == 'h';
333 drivename += 2;
335 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
338 whichpart = 0; /* Default */
340 if ( partition )
341 whichpart = strtoul(partition, NULL, 0);
343 if ( !(drive & 0x80) && whichpart ) {
344 error("Warning: Partitions of floppy devices may not work\n");
347 /* Get the disk geometry and disk access setup */
348 if ( get_disk_params(drive) ) {
349 error("Cannot get disk parameters\n");
350 goto bail;
353 /* Get MBR */
354 if ( read_sector(mbr, 0) ) {
355 error("Cannot read Master Boot Record\n");
356 goto bail;
359 if ( whichpart == 0 ) {
360 /* Boot the MBR */
361 partinfo = NULL;
362 boot_sector = mbr;
363 } else if ( whichpart <= 4 ) {
364 /* Boot a primary partition */
365 partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
366 if ( partinfo->ostype == 0 ) {
367 error("Invalid primary partition\n");
368 goto bail;
370 } else {
371 /* Boot a logical partition */
373 nextpart = 5;
374 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
376 if ( !partinfo || partinfo->ostype == 0 ) {
377 error("Requested logical partition not found\n");
378 goto bail;
382 /* Do the actual chainloading */
383 if ( partinfo ) {
384 /* Actually read the boot sector */
385 /* Pick the first buffer that isn't already in use */
386 boot_sector = (char *)(((unsigned long)partinfo + 511) & ~511);
387 if ( read_sector(boot_sector, partinfo->start_lba) ) {
388 error("Cannot read boot sector\n");
389 goto bail;
392 /* 0x7BE is the canonical place for the first partition entry. */
393 inreg.esi.w[0] = 0x7be;
394 memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
397 fputs("Booting...\n", stdout);
399 inreg.eax.w[0] = 0x000d; /* Clean up and chain boot */
400 inreg.edx.w[0] = 0; /* Should be 3 for "keeppxe" */
401 inreg.edi.l = (uint32_t)boot_sector;
402 inreg.ecx.l = SECTOR; /* One sector */
403 inreg.ebx.b[0] = drive; /* DL = drive no */
405 __intcall(0x22, &inreg, NULL);
407 /* If we get here, badness happened */
408 error("Chainboot failed!\n");
410 bail:
411 return 255;