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>] [options]
19 * chain fd<disk#> [options]
20 * chain mbr:<id> [<partition>] [options]
22 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
26 * The mbr: syntax means search all the hard disks until one with a
27 * specific MBR serial number (bytes 440-443) is found.
29 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
34 * loads the file <loader> **from the SYSLINUX filesystem**
35 * instead of loading the boot sector.
38 * jumps to 07C0:0000 instead of 0000:7C00, not sure if this is
42 * if the disk is not fd0/hd0, install a BIOS stub which swaps
53 #include <syslinux/loadfile.h>
54 #include <syslinux/bootrm.h>
56 #define SECTOR 512 /* bytes/sector */
58 static struct options
{
65 static inline void error(const char *msg
)
71 * Call int 13h, but with retry on failure. Especially floppies need this.
73 static int int13_retry(const com32sys_t
*inreg
, com32sys_t
*outreg
)
75 int retry
= 6; /* Number of retries */
78 if ( !outreg
) outreg
= &tmpregs
;
81 __intcall(0x13, inreg
, outreg
);
82 if ( !(outreg
->eflags
.l
& EFLAGS_CF
) )
83 return 0; /* CF=0, OK */
86 return -1; /* Error */
90 * Query disk parameters and EBIOS availability for a particular disk.
94 int ebios
; /* EBIOS supported on this disk */
95 int cbios
; /* CHS geometry is valid */
100 static int get_disk_params(int disk
)
102 static com32sys_t getparm
, parm
, getebios
, ebios
;
104 disk_info
.disk
= disk
;
105 disk_info
.ebios
= disk_info
.cbios
= 0;
107 /* Get EBIOS support */
108 getebios
.eax
.w
[0] = 0x4100;
109 getebios
.ebx
.w
[0] = 0x55aa;
110 getebios
.edx
.b
[0] = disk
;
111 getebios
.eflags
.b
[0] = 0x3; /* CF set */
113 __intcall(0x13, &getebios
, &ebios
);
115 if ( !(ebios
.eflags
.l
& EFLAGS_CF
) &&
116 ebios
.ebx
.w
[0] == 0xaa55 &&
117 (ebios
.ecx
.b
[0] & 1) ) {
121 /* Get disk parameters -- really only useful for
122 hard disks, but if we have a partitioned floppy
123 it's actually our best chance... */
124 getparm
.eax
.b
[1] = 0x08;
125 getparm
.edx
.b
[0] = disk
;
127 __intcall(0x13, &getparm
, &parm
);
129 if ( parm
.eflags
.l
& EFLAGS_CF
)
130 return disk_info
.ebios
? 0 : -1;
132 disk_info
.head
= parm
.edx
.b
[1]+1;
133 disk_info
.sect
= parm
.ecx
.b
[0] & 0x3f;
134 if ( disk_info
.sect
== 0 ) {
137 disk_info
.cbios
= 1; /* Valid geometry */
144 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
154 static int read_sector(void *buf
, unsigned int lba
)
158 memset(&inreg
, 0, sizeof inreg
);
160 if ( disk_info
.ebios
) {
161 dapa
->len
= sizeof(*dapa
);
162 dapa
->count
= 1; /* 1 sector */
163 dapa
->off
= OFFS(buf
);
164 dapa
->seg
= SEG(buf
);
167 inreg
.esi
.w
[0] = OFFS(dapa
);
168 inreg
.ds
= SEG(dapa
);
169 inreg
.edx
.b
[0] = disk_info
.disk
;
170 inreg
.eax
.b
[1] = 0x42; /* Extended read */
172 unsigned int c
, h
, s
, t
;
174 if ( !disk_info
.cbios
) {
175 /* We failed to get the geometry */
178 return -1; /* Can only read MBR */
182 s
= (lba
% disk_info
.sect
) + 1;
183 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
184 h
= t
% disk_info
.head
;
185 c
= t
/ disk_info
.head
;
188 if ( s
> 63 || h
> 256 || c
> 1023 )
191 inreg
.eax
.w
[0] = 0x0201; /* Read one sector */
192 inreg
.ecx
.b
[1] = c
& 0xff;
193 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
195 inreg
.edx
.b
[0] = disk_info
.disk
;
196 inreg
.ebx
.w
[0] = OFFS(buf
);
200 return int13_retry(&inreg
, NULL
);
203 /* Search for a specific drive, based on the MBR signature; bytes
205 static int find_disk(uint32_t mbr_sig
, void *buf
)
209 for (drive
= 0x80; drive
<= 0xff; drive
++) {
210 if (get_disk_params(drive
))
211 continue; /* Drive doesn't exist */
212 if (read_sector(buf
, 0))
213 continue; /* Cannot read sector */
215 if (*(uint32_t *)((char *)buf
+ 440) == mbr_sig
)
222 /* A DOS partition table entry */
224 uint8_t active_flag
; /* 0x80 if "active" */
234 } __attribute__((packed
));
237 /* Search for a logical partition. Logical partitions are actually implemented
238 as recursive partition tables; theoretically they're supposed to form a
239 linked list, but other structures have been seen.
241 To make things extra confusing: data partition offsets are relative to where
242 the data partition record is stored, whereas extended partition offsets
243 are relative to the beginning of the extended partition all the way back
244 at the MBR... but still not absolute! */
246 int nextpart
; /* Number of the next logical partition */
248 static struct part_entry
*
249 find_logical_partition(int whichpart
, char *table
, struct part_entry
*self
,
250 struct part_entry
*root
)
252 struct part_entry
*ptab
= (struct part_entry
*)(table
+ 0x1be);
253 struct part_entry
*found
;
256 if ( *(uint16_t *)(table
+ 0x1fe) != 0xaa55 )
257 return NULL
; /* Signature missing */
259 /* We are assumed to already having enumerated all the data partitions
260 in this table if this is the MBR. For MBR, self == NULL. */
263 /* Scan the data partitions. */
265 for ( i
= 0 ; i
< 4 ; i
++ ) {
266 if ( ptab
[i
].ostype
== 0x00 || ptab
[i
].ostype
== 0x05 ||
267 ptab
[i
].ostype
== 0x0f || ptab
[i
].ostype
== 0x85 )
268 continue; /* Skip empty or extended partitions */
270 if ( !ptab
[i
].length
)
273 /* Adjust the offset to account for the extended partition itself */
274 ptab
[i
].start_lba
+= self
->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. */
278 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= self
->start_lba
||
279 ptab
[i
].start_lba
>= self
->start_lba
+ self
->length
)
282 /* OK, it's a data partition. Is it the one we're looking for? */
283 if ( nextpart
++ == whichpart
)
288 /* Scan the extended partitions. */
289 for ( i
= 0 ; i
< 4 ; i
++ ) {
290 if ( ptab
[i
].ostype
!= 0x05 &&
291 ptab
[i
].ostype
!= 0x0f && ptab
[i
].ostype
!= 0x85 )
292 continue; /* Skip empty or data partitions */
294 if ( !ptab
[i
].length
)
297 /* Adjust the offset to account for the extended partition itself */
299 ptab
[i
].start_lba
+= root
->start_lba
;
301 /* Sanity check entry: must not extend outside the extended partition.
302 This is necessary since some OSes put crap in some entries. */
304 if ( ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
305 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
308 /* Process this partition */
309 if ( read_sector(table
+SECTOR
, ptab
[i
].start_lba
) )
310 continue; /* Read error, must be invalid */
312 if ( (found
= find_logical_partition(whichpart
, table
+SECTOR
, &ptab
[i
],
313 root
? root
: &ptab
[i
])) )
317 /* If we get here, there ain't nothing... */
321 static void do_boot(void *boot_sector
, size_t boot_size
,
322 struct syslinux_rm_regs
*regs
)
324 uint16_t * const bios_fbm
= (uint16_t *)0x413;
325 uint32_t * const int13_vec
= (uint32_t *)(0x13*4);
326 uint16_t old_bios_fbm
= *bios_fbm
;
327 uint32_t old_int13_vec
= *int13_vec
;
328 struct syslinux_memmap
*mmap
;
329 struct syslinux_movelist
*mlist
= NULL
;
330 addr_t dosmem
= old_bios_fbm
<< 10;
332 mmap
= syslinux_memory_map();
335 error("Cannot read system memory map");
341 uint8_t driveno
= regs
->edx
.b
[0];
342 uint8_t swapdrive
= driveno
& 0x80;
344 regs
->edx
.b
[0] = swapdrive
;
347 p
= (uint8_t *)dosmem
;
349 /* Install swapper stub */
350 *p
++ = 0x80; /* cmp dl,swapdrive */
353 *p
++ = 0x74; /* je swap1 */
355 *p
++ = 0x80; /* cmp dl,driveno */
358 *p
++ = 0x75; /* je noswap */
360 *p
++ = 0xb2; /* mov dl,swapdrive */
362 *p
++ = 0xeb; /* jmp noswap */
364 *p
++ = 0xb2; /* swap1: mov dl,driveno */
366 *p
++ = 0xea; /* noswap: jmp far <oldint13> */
367 *(uint32_t *)p
= old_int13_vec
;
370 syslinux_add_memmap(&mmap
, dosmem
, 0xa0000-dosmem
, SMT_RESERVED
);
372 if (syslinux_memmap_type(mmap
, 0x7c00, boot_size
) != SMT_FREE
) {
373 error("Loader file too large");
377 if (syslinux_add_movelist(&mlist
, 0x7c00, (addr_t
)boot_sector
, boot_size
)) {
378 error("Out of memory");
382 fputs("Booting...\n", stdout
);
385 /* Do this as late as possible */
386 *bios_fbm
= dosmem
>> 10;
387 *int13_vec
= dosmem
<< 12;
390 syslinux_shuffle_boot_rm(mlist
, mmap
, opt
.keeppxe
, regs
);
392 /* If we get here, badness happened */
394 *bios_fbm
= old_bios_fbm
;
395 *int13_vec
= old_int13_vec
;
397 error("Chainboot failed!\n");
400 int main(int argc
, char *argv
[])
403 void *boot_sector
= NULL
;
404 struct part_entry
*partinfo
;
405 struct syslinux_rm_regs regs
;
406 char *drivename
, *partition
;
407 int hd
, drive
, whichpart
;
409 size_t boot_size
= SECTOR
;
411 openconsole(&dev_null_r
, &dev_stdcon_w
);
416 /* Prepare the register set */
417 memset(®s
, 0, sizeof regs
);
419 for (i
= 1; i
< argc
; i
++) {
420 if (!strcmp(argv
[i
], "-file") && argv
[i
+1]) {
421 opt
.loadfile
= argv
[++i
];
422 } else if (!strcmp(argv
[i
], "-ntldr")) {
424 } else if (!strcmp(argv
[i
], "-swap")) {
426 } else if (!strcmp(argv
[i
], "keeppxe")) {
437 error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition] [-swap][-ntldr] "
443 regs
.es
= regs
.cs
= regs
.ss
= regs
.ds
= regs
.fs
= regs
.gs
= 0x07c0;
445 regs
.ip
= regs
.esp
.l
= 0x7c00;
448 /* Divvy up the bounce buffer. To keep things sector-
449 aligned, give the EBIOS DAPA the first sector, then
450 the MBR next, and the rest is used for the partition-
452 dapa
= (struct ebios_dapa
*)__com32
.cs_bounce
;
453 mbr
= (char *)__com32
.cs_bounce
+ SECTOR
;
456 partition
= argv
[2]; /* Possibly null */
459 if ( !memcmp(drivename
, "mbr:", 4) ) {
460 drive
= find_disk(strtoul(drivename
+4, NULL
, 0), mbr
);
462 error("Unable to find requested MBR signature\n");
466 if ( (drivename
[0] == 'h' || drivename
[0] == 'f') &&
467 drivename
[1] == 'd' ) {
468 hd
= drivename
[0] == 'h';
471 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
474 regs
.edx
.b
[0] = drive
;
476 whichpart
= 0; /* Default */
479 whichpart
= strtoul(partition
, NULL
, 0);
481 if ( !(drive
& 0x80) && whichpart
) {
482 error("Warning: Partitions of floppy devices may not work\n");
485 /* Get the disk geometry and disk access setup */
486 if ( get_disk_params(drive
) ) {
487 error("Cannot get disk parameters\n");
492 if ( read_sector(mbr
, 0) ) {
493 error("Cannot read Master Boot Record\n");
497 if ( whichpart
== 0 ) {
501 } else if ( whichpart
<= 4 ) {
502 /* Boot a primary partition */
503 partinfo
= &((struct part_entry
*)(mbr
+ 0x1be))[whichpart
-1];
504 if ( partinfo
->ostype
== 0 ) {
505 error("Invalid primary partition\n");
509 /* Boot a logical partition */
512 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
514 if ( !partinfo
|| partinfo
->ostype
== 0 ) {
515 error("Requested logical partition not found\n");
520 /* Do the actual chainloading */
522 if ( loadfile(opt
.loadfile
, &boot_sector
, &boot_size
) ) {
523 error("Failed to load the boot file\n");
526 } else if (partinfo
) {
527 /* Actually read the boot sector */
528 /* Pick the first buffer that isn't already in use */
529 boot_sector
= (void *)(((uintptr_t)partinfo
+ 511) & ~511);
530 if ( read_sector(boot_sector
, partinfo
->start_lba
) ) {
531 error("Cannot read boot sector\n");
537 /* 0x7BE is the canonical place for the first partition entry. */
538 regs
.esi
.w
[0] = 0x7be;
539 memcpy((char *)0x7be, partinfo
, sizeof(*partinfo
));
542 do_boot(boot_sector
, boot_size
, ®s
);