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>] [-ntldr] [-file <loader>]
19 * chain fd<disk#> [-ntldr] [-file <loader>]
20 * chain mbr:<id> [<partition>] [-ntldr] [-file <loader>]
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.)
30 * -file <loader> loads the file <loader> **from the SYSLINUX filesystem**
31 * instead of loading the boot sector.
33 * -ntldr jumps to 07C0:0000 instead of 0000:7C00, not sure if this is
45 #include <syslinux/loadfile.h>
46 #include <syslinux/bootrm.h>
48 #define SECTOR 512 /* bytes/sector */
50 static inline void error(const char *msg
)
56 * Call int 13h, but with retry on failure. Especially floppies need this.
58 static int int13_retry(const com32sys_t
*inreg
, com32sys_t
*outreg
)
60 int retry
= 6; /* Number of retries */
63 if ( !outreg
) outreg
= &tmpregs
;
66 __intcall(0x13, inreg
, outreg
);
67 if ( !(outreg
->eflags
.l
& EFLAGS_CF
) )
68 return 0; /* CF=0, OK */
71 return -1; /* Error */
75 * Query disk parameters and EBIOS availability for a particular disk.
79 int ebios
; /* EBIOS supported on this disk */
80 int cbios
; /* CHS geometry is valid */
85 static int get_disk_params(int disk
)
87 static com32sys_t getparm
, parm
, getebios
, ebios
;
89 disk_info
.disk
= disk
;
90 disk_info
.ebios
= disk_info
.cbios
= 0;
92 /* Get EBIOS support */
93 getebios
.eax
.w
[0] = 0x4100;
94 getebios
.ebx
.w
[0] = 0x55aa;
95 getebios
.edx
.b
[0] = disk
;
96 getebios
.eflags
.b
[0] = 0x3; /* CF set */
98 __intcall(0x13, &getebios
, &ebios
);
100 if ( !(ebios
.eflags
.l
& EFLAGS_CF
) &&
101 ebios
.ebx
.w
[0] == 0xaa55 &&
102 (ebios
.ecx
.b
[0] & 1) ) {
106 /* Get disk parameters -- really only useful for
107 hard disks, but if we have a partitioned floppy
108 it's actually our best chance... */
109 getparm
.eax
.b
[1] = 0x08;
110 getparm
.edx
.b
[0] = disk
;
112 __intcall(0x13, &getparm
, &parm
);
114 if ( parm
.eflags
.l
& EFLAGS_CF
)
115 return disk_info
.ebios
? 0 : -1;
117 disk_info
.head
= parm
.edx
.b
[1]+1;
118 disk_info
.sect
= parm
.ecx
.b
[0] & 0x3f;
119 if ( disk_info
.sect
== 0 ) {
122 disk_info
.cbios
= 1; /* Valid geometry */
129 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
139 static int read_sector(void *buf
, unsigned int lba
)
143 memset(&inreg
, 0, sizeof inreg
);
145 if ( disk_info
.ebios
) {
146 dapa
->len
= sizeof(*dapa
);
147 dapa
->count
= 1; /* 1 sector */
148 dapa
->off
= OFFS(buf
);
149 dapa
->seg
= SEG(buf
);
152 inreg
.esi
.w
[0] = OFFS(dapa
);
153 inreg
.ds
= SEG(dapa
);
154 inreg
.edx
.b
[0] = disk_info
.disk
;
155 inreg
.eax
.b
[1] = 0x42; /* Extended read */
157 unsigned int c
, h
, s
, t
;
159 if ( !disk_info
.cbios
) {
160 /* We failed to get the geometry */
163 return -1; /* Can only read MBR */
167 s
= (lba
% disk_info
.sect
) + 1;
168 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
169 h
= t
% disk_info
.head
;
170 c
= t
/ disk_info
.head
;
173 if ( s
> 63 || h
> 256 || c
> 1023 )
176 inreg
.eax
.w
[0] = 0x0201; /* Read one sector */
177 inreg
.ecx
.b
[1] = c
& 0xff;
178 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
180 inreg
.edx
.b
[0] = disk_info
.disk
;
181 inreg
.ebx
.w
[0] = OFFS(buf
);
185 return int13_retry(&inreg
, NULL
);
188 /* Search for a specific drive, based on the MBR signature; bytes
190 static int find_disk(uint32_t mbr_sig
, void *buf
)
194 for (drive
= 0x80; drive
<= 0xff; drive
++) {
195 if (get_disk_params(drive
))
196 continue; /* Drive doesn't exist */
197 if (read_sector(buf
, 0))
198 continue; /* Cannot read sector */
200 if (*(uint32_t *)((char *)buf
+ 440) == mbr_sig
)
207 /* A DOS partition table entry */
209 uint8_t active_flag
; /* 0x80 if "active" */
219 } __attribute__((packed
));
222 /* Search for a logical partition. Logical partitions are actually implemented
223 as recursive partition tables; theoretically they're supposed to form a
224 linked list, but other structures have been seen.
226 To make things extra confusing: data partition offsets are relative to where
227 the data partition record is stored, whereas extended partition offsets
228 are relative to the beginning of the extended partition all the way back
229 at the MBR... but still not absolute! */
231 int nextpart
; /* Number of the next logical partition */
233 static struct part_entry
*
234 find_logical_partition(int whichpart
, char *table
, struct part_entry
*self
,
235 struct part_entry
*root
)
237 struct part_entry
*ptab
= (struct part_entry
*)(table
+ 0x1be);
238 struct part_entry
*found
;
241 if ( *(uint16_t *)(table
+ 0x1fe) != 0xaa55 )
242 return NULL
; /* Signature missing */
244 /* We are assumed to already having enumerated all the data partitions
245 in this table if this is the MBR. For MBR, self == NULL. */
248 /* Scan the data partitions. */
250 for ( i
= 0 ; i
< 4 ; i
++ ) {
251 if ( ptab
[i
].ostype
== 0x00 || ptab
[i
].ostype
== 0x05 ||
252 ptab
[i
].ostype
== 0x0f || ptab
[i
].ostype
== 0x85 )
253 continue; /* Skip empty or extended partitions */
255 if ( !ptab
[i
].length
)
258 /* Adjust the offset to account for the extended partition itself */
259 ptab
[i
].start_lba
+= self
->start_lba
;
261 /* Sanity check entry: must not extend outside the extended partition.
262 This is necessary since some OSes put crap in some entries. */
263 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= self
->start_lba
||
264 ptab
[i
].start_lba
>= self
->start_lba
+ self
->length
)
267 /* OK, it's a data partition. Is it the one we're looking for? */
268 if ( nextpart
++ == whichpart
)
273 /* Scan the extended partitions. */
274 for ( i
= 0 ; i
< 4 ; i
++ ) {
275 if ( ptab
[i
].ostype
!= 0x05 &&
276 ptab
[i
].ostype
!= 0x0f && ptab
[i
].ostype
!= 0x85 )
277 continue; /* Skip empty or data partitions */
279 if ( !ptab
[i
].length
)
282 /* Adjust the offset to account for the extended partition itself */
284 ptab
[i
].start_lba
+= root
->start_lba
;
286 /* Sanity check entry: must not extend outside the extended partition.
287 This is necessary since some OSes put crap in some entries. */
289 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
290 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
293 /* Process this partition */
294 if ( read_sector(table
+SECTOR
, ptab
[i
].start_lba
) )
295 continue; /* Read error, must be invalid */
297 if ( (found
= find_logical_partition(whichpart
, table
+SECTOR
, &ptab
[i
],
298 root
? root
: &ptab
[i
])) )
302 /* If we get here, there ain't nothing... */
306 static void do_boot(uint16_t keeppxe
, void *boot_sector
,
307 size_t boot_size
, struct syslinux_rm_regs
*regs
)
309 struct syslinux_memmap
*mmap
;
310 struct syslinux_movelist
*mlist
= NULL
;
312 mmap
= syslinux_memory_map();
315 error("Cannot read system memory map");
319 if (syslinux_memmap_type(mmap
, 0x7c00, boot_size
) != SMT_FREE
) {
320 error("Loader file too large");
324 if (syslinux_add_movelist(&mlist
, 0x7c00, (addr_t
)boot_sector
, boot_size
)) {
325 error("Out of memory");
329 fputs("Booting...\n", stdout
);
331 syslinux_shuffle_boot_rm(mlist
, mmap
, keeppxe
, regs
);
333 /* If we get here, badness happened */
334 error("Chainboot failed!\n");
337 int main(int argc
, char *argv
[])
340 void *boot_sector
= NULL
;
341 struct part_entry
*partinfo
;
342 struct syslinux_rm_regs regs
;
343 char *drivename
, *partition
;
344 int hd
, drive
, whichpart
;
346 uint16_t keeppxe
= 0;
348 const char *load_file
;
349 size_t boot_size
= SECTOR
;
351 openconsole(&dev_null_r
, &dev_stdcon_w
);
357 /* Prepare the register set */
358 memset(®s
, 0, sizeof regs
);
360 for (i
= 1; i
< argc
; i
++) {
361 if (!strcmp(argv
[i
], "-file") && argv
[i
+1]) {
362 load_file
= argv
[++i
];
363 } else if (!strcmp(argv
[i
], "-ntldr")) {
365 } else if (!strcmp(argv
[i
], "keeppxe")) {
376 error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition] [-ntldr] "
382 regs
.es
= regs
.cs
= regs
.ss
= regs
.ds
= regs
.fs
= regs
.gs
= 0x07c0;
384 regs
.ip
= regs
.esp
.l
= 0x7c00;
387 /* Divvy up the bounce buffer. To keep things sector-
388 aligned, give the EBIOS DAPA the first sector, then
389 the MBR next, and the rest is used for the partition-
391 dapa
= (struct ebios_dapa
*)__com32
.cs_bounce
;
392 mbr
= (char *)__com32
.cs_bounce
+ SECTOR
;
395 partition
= argv
[2]; /* Possibly null */
398 if ( !memcmp(drivename
, "mbr:", 4) ) {
399 drive
= find_disk(strtoul(drivename
+4, NULL
, 0), mbr
);
401 error("Unable to find requested MBR signature\n");
405 if ( (drivename
[0] == 'h' || drivename
[0] == 'f') &&
406 drivename
[1] == 'd' ) {
407 hd
= drivename
[0] == 'h';
410 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
413 regs
.edx
.b
[0] = drive
;
415 whichpart
= 0; /* Default */
418 whichpart
= strtoul(partition
, NULL
, 0);
420 if ( !(drive
& 0x80) && whichpart
) {
421 error("Warning: Partitions of floppy devices may not work\n");
424 /* Get the disk geometry and disk access setup */
425 if ( get_disk_params(drive
) ) {
426 error("Cannot get disk parameters\n");
431 if ( read_sector(mbr
, 0) ) {
432 error("Cannot read Master Boot Record\n");
436 if ( whichpart
== 0 ) {
440 } else if ( whichpart
<= 4 ) {
441 /* Boot a primary partition */
442 partinfo
= &((struct part_entry
*)(mbr
+ 0x1be))[whichpart
-1];
443 if ( partinfo
->ostype
== 0 ) {
444 error("Invalid primary partition\n");
448 /* Boot a logical partition */
451 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
453 if ( !partinfo
|| partinfo
->ostype
== 0 ) {
454 error("Requested logical partition not found\n");
459 /* Do the actual chainloading */
461 if ( loadfile(load_file
, &boot_sector
, &boot_size
) ) {
462 error("Failed to load the boot file\n");
465 } else if (partinfo
) {
466 /* Actually read the boot sector */
467 /* Pick the first buffer that isn't already in use */
468 boot_sector
= (void *)(((uintptr_t)partinfo
+ 511) & ~511);
469 if ( read_sector(boot_sector
, partinfo
->start_lba
) ) {
470 error("Cannot read boot sector\n");
476 /* 0x7BE is the canonical place for the first partition entry. */
477 regs
.esi
.w
[0] = 0x7be;
478 memcpy((char *)0x7be, partinfo
, sizeof(*partinfo
));
481 do_boot(keeppxe
, boot_sector
, boot_size
, ®s
);