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 * ----------------------------------------------------------------------- */
16 * Chainload a hard disk (currently rather braindead.)
18 * Usage: chain hd<disk#> [<partition>]
20 * chain mbr:<id> [<partition>]
22 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
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.)
38 #define SECTOR 512 /* bytes/sector */
40 static inline void error(const char *msg
)
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 */
53 if ( !outreg
) outreg
= &tmpregs
;
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.
69 int ebios
; /* EBIOS supported on this disk */
70 int cbios
; /* CHS geometry is valid */
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) ) {
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 ) {
112 disk_info
.cbios
= 1; /* Valid geometry */
119 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
129 int read_sector(void *buf
, unsigned int lba
)
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
);
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 */
147 unsigned int c
, h
, s
, t
;
149 if ( !disk_info
.cbios
) {
150 /* We failed to get the geometry */
153 return -1; /* Can only read MBR */
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 )
166 inreg
.eax
.w
[0] = 0x0201; /* Read one sector */
167 inreg
.ecx
.b
[1] = c
& 0xff;
168 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
170 inreg
.edx
.b
[0] = disk_info
.disk
;
171 inreg
.ebx
.w
[0] = OFFS(buf
);
175 return int13_retry(&inreg
, NULL
);
178 /* Search for a specific drive, based on the MBR signature; bytes
180 int find_disk(uint32_t mbr_sig
, void *buf
)
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
)
197 /* A DOS partition table entry */
199 uint8_t active_flag
; /* 0x80 if "active" */
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 */
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
;
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. */
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
)
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
)
257 /* OK, it's a data partition. Is it the one we're looking for? */
258 if ( nextpart
++ == whichpart
)
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
)
272 /* Adjust the offset to account for the extended partition itself */
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. */
279 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
280 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
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
])) )
292 /* If we get here, there ain't nothing... */
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
);
308 error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition]\n");
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-
316 dapa
= (struct ebios_dapa
*)__com32
.cs_bounce
;
317 mbr
= (char *)__com32
.cs_bounce
+ SECTOR
;
320 partition
= argv
[2]; /* Possibly null */
323 if ( !memcmp(drivename
, "mbr:", 4) ) {
324 drive
= find_disk(strtoul(drivename
+4, NULL
, 0), mbr
);
326 error("Unable to find requested MBR signature\n");
330 if ( (drivename
[0] == 'h' || drivename
[0] == 'f') &&
331 drivename
[1] == 'd' ) {
332 hd
= drivename
[0] == 'h';
335 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
338 whichpart
= 0; /* Default */
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");
354 if ( read_sector(mbr
, 0) ) {
355 error("Cannot read Master Boot Record\n");
359 if ( whichpart
== 0 ) {
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");
371 /* Boot a logical partition */
374 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
376 if ( !partinfo
|| partinfo
->ostype
== 0 ) {
377 error("Requested logical partition not found\n");
382 /* Do the actual chainloading */
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");
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");