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 * ----------------------------------------------------------------------- */
16 * Chainload a hard disk (currently rather braindead.)
18 * Usage: chain hd<disk#> [<partition>]
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.)
33 #define SECTOR 512 /* bytes/sector */
35 static inline void error(const char *msg
)
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 */
48 if ( !outreg
) outreg
= &tmpregs
;
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.
64 int ebios
; /* EBIOS supported on this disk */
65 int cbios
; /* CHS geometry is valid */
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) ) {
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 ) {
106 disk_info
.cbios
= 1; /* Valid geometry */
113 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
123 int read_sector(void *buf
, unsigned int lba
)
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
);
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 */
141 unsigned int c
, h
, s
, t
;
143 if ( !disk_info
.cbios
) {
144 /* We failed to get the geometry */
147 return -1; /* Can only read MBR */
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 )
160 inreg
.eax
.w
[0] = 0x0201; /* Read one sector */
161 inreg
.ecx
.b
[1] = c
& 0xff;
162 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
164 inreg
.edx
.b
[0] = disk_info
.disk
;
165 inreg
.ebx
.w
[0] = OFFS(buf
);
169 return int13_retry(&inreg
, NULL
);
172 /* A DOS partition table entry */
174 uint8_t active_flag
; /* 0x80 if "active" */
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
;
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. */
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
)
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
)
230 /* OK, it's a data partition. Is it the one we're looking for? */
231 if ( nextpart
++ == whichpart
)
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
)
245 /* Adjust the offset to account for the extended partition itself */
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. */
252 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
253 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
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
])) )
265 /* If we get here, there ain't nothing... */
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
);
281 error("Usage: chain.c32 (hd|fd)# [partition]\n");
286 partition
= argv
[2]; /* Possibly null */
289 if ( (drivename
[0] == 'h' || drivename
[0] == 'f') &&
290 drivename
[1] == 'd' ) {
291 hd
= drivename
[0] == 'h';
294 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
295 whichpart
= 0; /* Default */
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-
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");
318 if ( read_sector(mbr
, 0) ) {
319 error("Cannot read Master Boot Record\n");
323 if ( whichpart
== 0 ) {
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");
335 /* Boot a logical partition */
338 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
340 if ( !partinfo
|| partinfo
->ostype
== 0 ) {
341 error("Requested logical partition not found\n");
346 /* Do the actual chainloading */
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");
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");