1 #ident "$Id: chain.c,v 1.4 2005/01/12 00:34:54 hpa Exp $"
2 /* ----------------------------------------------------------------------- *
4 * Copyright 2003-2005 H. Peter Anvin - All Rights Reserved
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Chainload a hard disk (currently rather braindead.)
19 * Usage: chain hd<disk#> [<partition>]
22 * ... e.g. "chain hd0 1" will boot the first partition on the first hard disk.
24 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
34 #define SECTOR 512 /* bytes/sector */
36 static inline void error(const char *msg)
42 * Call int 13h, but with retry on failure. Especially floppies need this.
44 int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
46 int retry = 6; /* Number of retries */
49 if ( !outreg ) outreg = &tmpregs;
52 __intcall(0x13, inreg, outreg);
53 if ( !(outreg->eflags.l & EFLAGS_CF) )
54 return 0; /* CF=0, OK */
57 return -1; /* Error */
61 * Query disk parameters and EBIOS availability for a particular disk.
65 int ebios; /* EBIOS supported on this disk */
66 int cbios; /* CHS geometry is valid */
71 int get_disk_params(int disk)
73 static com32sys_t getparm, parm, getebios, ebios;
75 disk_info.disk = disk;
77 /* Get EBIOS support */
78 getebios.eax.w[0] = 0x4100;
79 getebios.ebx.w[0] = 0x55aa;
80 getebios.edx.b[0] = disk;
81 getebios.eflags.b[0] = 0x3; /* CF set */
83 __intcall(0x13, &getebios, &ebios);
85 if ( !(ebios.eflags.l & EFLAGS_CF) &&
86 ebios.ebx.w[0] == 0xaa55 &&
87 (ebios.ecx.b[0] & 1) ) {
91 /* Get disk parameters -- really only useful for
92 hard disks, but if we have a partitioned floppy
93 it's actually our best chance... */
94 getparm.eax.b[1] = 0x08;
95 getparm.edx.b[0] = disk;
97 __intcall(0x13, &getparm, &parm);
99 if ( parm.eflags.l & EFLAGS_CF )
100 return disk_info.ebios ? 0 : -1;
102 disk_info.head = parm.edx.b[1]+1;
103 disk_info.sect = parm.ecx.b[0] & 0x3f;
104 if ( disk_info.sect == 0 ) {
107 disk_info.cbios = 1; /* Valid geometry */
114 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
124 int read_sector(void *buf, unsigned int lba)
128 memset(&inreg, 0, sizeof inreg);
130 if ( disk_info.ebios ) {
131 dapa->len = sizeof(*dapa);
132 dapa->count = 1; /* 1 sector */
133 dapa->off = OFFS(buf);
134 dapa->seg = SEG(buf);
137 inreg.esi.w[0] = OFFS(dapa);
138 inreg.ds = SEG(dapa);
139 inreg.edx.b[0] = disk_info.disk;
140 inreg.eax.b[1] = 0x42; /* Extended read */
142 unsigned int c, h, s, t;
144 if ( !disk_info.cbios ) {
145 /* We failed to get the geometry */
148 return -1; /* Can only read MBR */
152 s = (lba % disk_info.sect) + 1;
153 t = lba / disk_info.sect; /* Track = head*cyl */
154 h = t % disk_info.head;
155 c = t / disk_info.head;
158 if ( s > 63 || h > 256 || c > 1023 )
161 inreg.eax.w[0] = 0x0201; /* Read one sector */
162 inreg.ecx.b[1] = c & 0xff;
163 inreg.ecx.b[0] = s + (c >> 6);
165 inreg.edx.b[0] = disk_info.disk;
166 inreg.ebx.w[0] = OFFS(buf);
170 return int13_retry(&inreg, NULL);
173 /* A DOS partition table entry */
175 uint8_t active_flag; /* 0x80 if "active" */
185 } __attribute__((packed));
188 /* Search for a logical partition. Logical partitions are actually implemented
189 as recursive partition tables; theoretically they're supposed to form a linked
190 list, but other structures have been seen.
192 To make things extra confusing: data partition offsets are relative to where
193 the data partition record is stored, whereas extended partition offsets
194 are relative to the beginning of the extended partition all the way back
195 at the MBR... but still not absolute! */
197 int nextpart; /* Number of the next logical partition */
199 struct part_entry *find_logical_partition(int whichpart, char *table, struct part_entry *self, struct part_entry *root)
201 struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
202 struct part_entry *found;
205 if ( *(uint16_t *)(ptab + 0x1fe) != 0xaa55 )
206 return NULL; /* Signature missing */
208 /* We are assumed to already having enumerated all the data partitions
209 in this table if this is the MBR. For MBR, self == NULL. */
212 /* Scan the data partitions. */
214 for ( i = 0 ; i < 4 ; i++ ) {
215 if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
216 ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
217 continue; /* Skip empty or extended partitions */
219 if ( !ptab[i].length )
222 /* Adjust the offset to account for the extended partition itself */
223 ptab[i].start_lba += self->start_lba;
225 /* Sanity check entry: must not extend outside the extended partition.
226 This is necessary since some OSes put crap in some entries. */
227 if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
228 ptab[i].start_lba >= self->start_lba + self->length )
231 /* OK, it's a data partition. Is it the one we're looking for? */
232 if ( nextpart++ == whichpart )
237 /* Scan the extended partitions. */
238 for ( i = 0 ; i < 4 ; i++ ) {
239 if ( ptab[i].ostype != 0x05 &&
240 ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
241 continue; /* Skip empty or data partitions */
243 if ( !ptab[i].length )
246 /* Adjust the offset to account for the extended partition itself */
248 ptab[i].start_lba += root->start_lba;
250 /* Sanity check entry: must not extend outside the extended partition.
251 This is necessary since some OSes put crap in some entries. */
253 if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
254 ptab[i].start_lba >= root->start_lba + root->length )
257 /* Process this partition */
258 if ( read_sector(table+SECTOR, ptab[i].start_lba) )
259 continue; /* Read error, must be invalid */
261 if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
262 root ? root : &ptab[i])) )
266 /* If we get here, there ain't nothing... */
271 int main(int argc, char *argv[])
273 char *mbr, *boot_sector = NULL;
274 struct part_entry *partinfo;
275 char *drivename, *partition;
276 int hd, drive, whichpart;
277 static com32sys_t inreg; /* In bss, so zeroed automatically */
279 openconsole(&dev_null_r, &dev_stdcon_w);
282 error("Usage: chain.c32 (hd|fd)# [partition]\n");
287 partition = argv[2]; /* Possibly null */
290 if ( (drivename[0] == 'h' || drivename[0] == 'f') &&
291 drivename[1] == 'd' ) {
292 hd = drivename[0] == 'h';
295 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
296 whichpart = 0; /* Default */
299 whichpart = strtoul(partition, NULL, 0);
301 if ( !(drive & 0x80) && whichpart ) {
302 error("Warning: Partitions of floppy devices may not work\n");
305 /* Divvy up the bounce buffer. To keep things sector-
306 aligned, give the EBIOS DAPA the first sector, then
307 the MBR next, and the rest is used for the partition-
309 dapa = (struct ebios_dapa *)__com32.cs_bounce;
310 mbr = (char *)__com32.cs_bounce + SECTOR;
312 /* Get the disk geometry (not needed for MBR) */
313 if ( get_disk_params(drive) && whichpart ) {
314 error("Cannot get disk parameters\n");
319 if ( read_sector(mbr, 0) ) {
320 error("Cannot read Master Boot Record\n");
324 if ( whichpart == 0 ) {
328 } else if ( whichpart <= 4 ) {
329 /* Boot a primary partition */
330 partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
331 if ( partinfo->ostype == 0 ) {
332 error("Invalid primary partition\n");
336 /* Boot a logical partition */
339 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
341 if ( !partinfo || partinfo->ostype == 0 ) {
342 error("Requested logical partition not found\n");
347 /* Do the actual chainloading */
349 /* Actually read the boot sector */
350 /* Pick the first buffer that isn't already in use */
351 boot_sector = (char *)(((unsigned long)partinfo + 511) & ~511);
352 if ( read_sector(boot_sector, partinfo->start_lba) ) {
353 error("Cannot read boot sector\n");
357 /* 0x7BE is the canonical place for the first partition entry. */
358 inreg.esi.w[0] = 0x7be;
359 memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
362 fputs("Booting...\n", stdout);
364 inreg.eax.w[0] = 0x000d; /* Clean up and chain boot */
365 inreg.edx.w[0] = 0; /* Should be 3 for "keeppxe" */
366 inreg.edi.l = (uint32_t)boot_sector;
367 inreg.ecx.l = SECTOR; /* One sector */
368 inreg.ebx.b[0] = drive; /* DL = drive no */
370 __intcall(0x22, &inreg, NULL);
372 /* If we get here, badness happened */
373 error("Chainboot failed!\n");