Adding upstream version 3.50~pre5.
[syslinux-debian/hramrach.git] / com32 / modules / chain.c
bloba9aebdef905d73942ceafe2ada6f466a2fdc6d50
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2005 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#>
21 * ... e.g. "chain hd0 1" will boot the first partition on the first hard disk.
23 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
26 #include <com32.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <console.h>
33 #define SECTOR 512 /* bytes/sector */
35 static inline void error(const char *msg)
37 fputs(msg, stderr);
41 * Call int 13h, but with retry on failure. Especially floppies need this.
43 int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
45 int retry = 6; /* Number of retries */
46 com32sys_t tmpregs;
48 if ( !outreg ) outreg = &tmpregs;
50 while ( retry-- ) {
51 __intcall(0x13, inreg, outreg);
52 if ( !(outreg->eflags.l & EFLAGS_CF) )
53 return 0; /* CF=0, OK */
56 return -1; /* Error */
60 * Query disk parameters and EBIOS availability for a particular disk.
62 struct diskinfo {
63 int disk;
64 int ebios; /* EBIOS supported on this disk */
65 int cbios; /* CHS geometry is valid */
66 int head;
67 int sect;
68 } disk_info;
70 int get_disk_params(int disk)
72 static com32sys_t getparm, parm, getebios, ebios;
74 disk_info.disk = disk;
76 /* Get EBIOS support */
77 getebios.eax.w[0] = 0x4100;
78 getebios.ebx.w[0] = 0x55aa;
79 getebios.edx.b[0] = disk;
80 getebios.eflags.b[0] = 0x3; /* CF set */
82 __intcall(0x13, &getebios, &ebios);
84 if ( !(ebios.eflags.l & EFLAGS_CF) &&
85 ebios.ebx.w[0] == 0xaa55 &&
86 (ebios.ecx.b[0] & 1) ) {
87 disk_info.ebios = 1;
90 /* Get disk parameters -- really only useful for
91 hard disks, but if we have a partitioned floppy
92 it's actually our best chance... */
93 getparm.eax.b[1] = 0x08;
94 getparm.edx.b[0] = disk;
96 __intcall(0x13, &getparm, &parm);
98 if ( parm.eflags.l & EFLAGS_CF )
99 return disk_info.ebios ? 0 : -1;
101 disk_info.head = parm.edx.b[1]+1;
102 disk_info.sect = parm.ecx.b[0] & 0x3f;
103 if ( disk_info.sect == 0 ) {
104 disk_info.sect = 1;
105 } else {
106 disk_info.cbios = 1; /* Valid geometry */
109 return 0;
113 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
115 struct ebios_dapa {
116 uint16_t len;
117 uint16_t count;
118 uint16_t off;
119 uint16_t seg;
120 uint64_t lba;
121 } *dapa;
123 int read_sector(void *buf, unsigned int lba)
125 com32sys_t inreg;
127 memset(&inreg, 0, sizeof inreg);
129 if ( disk_info.ebios ) {
130 dapa->len = sizeof(*dapa);
131 dapa->count = 1; /* 1 sector */
132 dapa->off = OFFS(buf);
133 dapa->seg = SEG(buf);
134 dapa->lba = lba;
136 inreg.esi.w[0] = OFFS(dapa);
137 inreg.ds = SEG(dapa);
138 inreg.edx.b[0] = disk_info.disk;
139 inreg.eax.b[1] = 0x42; /* Extended read */
140 } else {
141 unsigned int c, h, s, t;
143 if ( !disk_info.cbios ) {
144 /* We failed to get the geometry */
146 if ( lba )
147 return -1; /* Can only read MBR */
149 s = 1; h = 0; c = 0;
150 } else {
151 s = (lba % disk_info.sect) + 1;
152 t = lba / disk_info.sect; /* Track = head*cyl */
153 h = t % disk_info.head;
154 c = t / disk_info.head;
157 if ( s > 63 || h > 256 || c > 1023 )
158 return -1;
160 inreg.eax.w[0] = 0x0201; /* Read one sector */
161 inreg.ecx.b[1] = c & 0xff;
162 inreg.ecx.b[0] = s + (c >> 6);
163 inreg.edx.b[1] = h;
164 inreg.edx.b[0] = disk_info.disk;
165 inreg.ebx.w[0] = OFFS(buf);
166 inreg.es = SEG(buf);
169 return int13_retry(&inreg, NULL);
172 /* A DOS partition table entry */
173 struct part_entry {
174 uint8_t active_flag; /* 0x80 if "active" */
175 uint8_t start_head;
176 uint8_t start_sect;
177 uint8_t start_cyl;
178 uint8_t ostype;
179 uint8_t end_head;
180 uint8_t end_sect;
181 uint8_t end_cyl;
182 uint32_t start_lba;
183 uint32_t length;
184 } __attribute__((packed));
187 /* Search for a logical partition. Logical partitions are actually implemented
188 as recursive partition tables; theoretically they're supposed to form a linked
189 list, but other structures have been seen.
191 To make things extra confusing: data partition offsets are relative to where
192 the data partition record is stored, whereas extended partition offsets
193 are relative to the beginning of the extended partition all the way back
194 at the MBR... but still not absolute! */
196 int nextpart; /* Number of the next logical partition */
198 struct part_entry *find_logical_partition(int whichpart, char *table, struct part_entry *self, struct part_entry *root)
200 struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
201 struct part_entry *found;
202 int i;
204 if ( *(uint16_t *)(ptab + 0x1fe) != 0xaa55 )
205 return NULL; /* Signature missing */
207 /* We are assumed to already having enumerated all the data partitions
208 in this table if this is the MBR. For MBR, self == NULL. */
210 if ( self ) {
211 /* Scan the data partitions. */
213 for ( i = 0 ; i < 4 ; i++ ) {
214 if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
215 ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
216 continue; /* Skip empty or extended partitions */
218 if ( !ptab[i].length )
219 continue;
221 /* Adjust the offset to account for the extended partition itself */
222 ptab[i].start_lba += self->start_lba;
224 /* Sanity check entry: must not extend outside the extended partition.
225 This is necessary since some OSes put crap in some entries. */
226 if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
227 ptab[i].start_lba >= self->start_lba + self->length )
228 continue;
230 /* OK, it's a data partition. Is it the one we're looking for? */
231 if ( nextpart++ == whichpart )
232 return &ptab[i];
236 /* Scan the extended partitions. */
237 for ( i = 0 ; i < 4 ; i++ ) {
238 if ( ptab[i].ostype != 0x05 &&
239 ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
240 continue; /* Skip empty or data partitions */
242 if ( !ptab[i].length )
243 continue;
245 /* Adjust the offset to account for the extended partition itself */
246 if ( root )
247 ptab[i].start_lba += root->start_lba;
249 /* Sanity check entry: must not extend outside the extended partition.
250 This is necessary since some OSes put crap in some entries. */
251 if ( root )
252 if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
253 ptab[i].start_lba >= root->start_lba + root->length )
254 continue;
256 /* Process this partition */
257 if ( read_sector(table+SECTOR, ptab[i].start_lba) )
258 continue; /* Read error, must be invalid */
260 if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
261 root ? root : &ptab[i])) )
262 return found;
265 /* If we get here, there ain't nothing... */
266 return NULL;
270 int main(int argc, char *argv[])
272 char *mbr, *boot_sector = NULL;
273 struct part_entry *partinfo;
274 char *drivename, *partition;
275 int hd, drive, whichpart;
276 static com32sys_t inreg; /* In bss, so zeroed automatically */
278 openconsole(&dev_null_r, &dev_stdcon_w);
280 if ( argc < 2 ) {
281 error("Usage: chain.c32 (hd|fd)# [partition]\n");
282 goto bail;
285 drivename = argv[1];
286 partition = argv[2]; /* Possibly null */
288 hd = 0;
289 if ( (drivename[0] == 'h' || drivename[0] == 'f') &&
290 drivename[1] == 'd' ) {
291 hd = drivename[0] == 'h';
292 drivename += 2;
294 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
295 whichpart = 0; /* Default */
297 if ( partition )
298 whichpart = strtoul(partition, NULL, 0);
300 if ( !(drive & 0x80) && whichpart ) {
301 error("Warning: Partitions of floppy devices may not work\n");
304 /* Divvy up the bounce buffer. To keep things sector-
305 aligned, give the EBIOS DAPA the first sector, then
306 the MBR next, and the rest is used for the partition-
307 chasing stack. */
308 dapa = (struct ebios_dapa *)__com32.cs_bounce;
309 mbr = (char *)__com32.cs_bounce + SECTOR;
311 /* Get the disk geometry (not needed for MBR) */
312 if ( get_disk_params(drive) && whichpart ) {
313 error("Cannot get disk parameters\n");
314 goto bail;
317 /* Get MBR */
318 if ( read_sector(mbr, 0) ) {
319 error("Cannot read Master Boot Record\n");
320 goto bail;
323 if ( whichpart == 0 ) {
324 /* Boot the MBR */
325 partinfo = NULL;
326 boot_sector = mbr;
327 } else if ( whichpart <= 4 ) {
328 /* Boot a primary partition */
329 partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
330 if ( partinfo->ostype == 0 ) {
331 error("Invalid primary partition\n");
332 goto bail;
334 } else {
335 /* Boot a logical partition */
337 nextpart = 5;
338 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
340 if ( !partinfo || partinfo->ostype == 0 ) {
341 error("Requested logical partition not found\n");
342 goto bail;
346 /* Do the actual chainloading */
347 if ( partinfo ) {
348 /* Actually read the boot sector */
349 /* Pick the first buffer that isn't already in use */
350 boot_sector = (char *)(((unsigned long)partinfo + 511) & ~511);
351 if ( read_sector(boot_sector, partinfo->start_lba) ) {
352 error("Cannot read boot sector\n");
353 goto bail;
356 /* 0x7BE is the canonical place for the first partition entry. */
357 inreg.esi.w[0] = 0x7be;
358 memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
361 fputs("Booting...\n", stdout);
363 inreg.eax.w[0] = 0x000d; /* Clean up and chain boot */
364 inreg.edx.w[0] = 0; /* Should be 3 for "keeppxe" */
365 inreg.edi.l = (uint32_t)boot_sector;
366 inreg.ecx.l = SECTOR; /* One sector */
367 inreg.ebx.b[0] = drive; /* DL = drive no */
369 __intcall(0x22, &inreg, NULL);
371 /* If we get here, badness happened */
372 error("Chainboot failed!\n");
374 bail:
375 return 255;