1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009 Intel Corporation; author: H. Peter Anvin
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9 * Boston MA 02111-1307, USA; either version 2 of the License, or
10 * (at your option) any later version; incorporated herein by reference.
12 * ----------------------------------------------------------------------- */
17 * Chainload a hard disk (currently rather braindead.)
19 * Usage: chain hd<disk#> [<partition>] [options]
20 * chain fd<disk#> [options]
21 * chain mbr:<id> [<partition>] [options]
22 * chain boot [<partition>] [options]
24 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
28 * The mbr: syntax means search all the hard disks until one with a
29 * specific MBR serial number (bytes 440-443) is found.
31 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
36 * loads the file <loader> **from the SYSLINUX filesystem**
37 * instead of loading the boot sector.
40 * loads at and jumps to <seg>:0000 instead of 0000:7C00.
43 * chainload another version/build of the ISOLINUX bootloader and patch
44 * the loader with appropriate parameters in memory.
45 * This avoids the need for the -eltorito-alt-boot parameter of mkisofs,
46 * when you want more than one ISOLINUX per CD/DVD.
49 * equivalent to -seg 0x2000 -file <loader>, used with WinNT's loaders
52 * equivalent to -seg 0x60 -file <loader>, used with FreeDOS kernel.sys.
56 * equivalent to -seg 0x70 -file <loader>, used with DOS' io.sys.
59 * if the disk is not fd0/hd0, install a BIOS stub which swaps
63 * change type of primary partitions with IDs 01, 04, 06, 07,
64 * 0b, 0c, or 0e to 1x, except for the selected partition, which
65 * is converted the other way.
76 #include <syslinux/loadfile.h>
77 #include <syslinux/bootrm.h>
78 #include <syslinux/config.h>
80 #define SECTOR 512 /* bytes/sector */
82 static struct options
{
91 static inline void error(const char *msg
)
97 * Call int 13h, but with retry on failure. Especially floppies need this.
99 static int int13_retry(const com32sys_t
* inreg
, com32sys_t
* outreg
)
101 int retry
= 6; /* Number of retries */
108 __intcall(0x13, inreg
, outreg
);
109 if (!(outreg
->eflags
.l
& EFLAGS_CF
))
110 return 0; /* CF=0, OK */
113 return -1; /* Error */
117 * Query disk parameters and EBIOS availability for a particular disk.
121 int ebios
; /* EBIOS supported on this disk */
122 int cbios
; /* CHS geometry is valid */
127 static int get_disk_params(int disk
)
129 static com32sys_t getparm
, parm
, getebios
, ebios
;
131 disk_info
.disk
= disk
;
132 disk_info
.ebios
= disk_info
.cbios
= 0;
134 /* Get EBIOS support */
135 getebios
.eax
.w
[0] = 0x4100;
136 getebios
.ebx
.w
[0] = 0x55aa;
137 getebios
.edx
.b
[0] = disk
;
138 getebios
.eflags
.b
[0] = 0x3; /* CF set */
140 __intcall(0x13, &getebios
, &ebios
);
142 if (!(ebios
.eflags
.l
& EFLAGS_CF
) &&
143 ebios
.ebx
.w
[0] == 0xaa55 && (ebios
.ecx
.b
[0] & 1)) {
147 /* Get disk parameters -- really only useful for
148 hard disks, but if we have a partitioned floppy
149 it's actually our best chance... */
150 getparm
.eax
.b
[1] = 0x08;
151 getparm
.edx
.b
[0] = disk
;
153 __intcall(0x13, &getparm
, &parm
);
155 if (parm
.eflags
.l
& EFLAGS_CF
)
156 return disk_info
.ebios
? 0 : -1;
158 disk_info
.head
= parm
.edx
.b
[1] + 1;
159 disk_info
.sect
= parm
.ecx
.b
[0] & 0x3f;
160 if (disk_info
.sect
== 0) {
163 disk_info
.cbios
= 1; /* Valid geometry */
170 * Get a disk block and return a malloc'd buffer.
171 * Uses the disk number and information from disk_info.
181 static void *read_sector(unsigned int lba
)
184 struct ebios_dapa
*dapa
= __com32
.cs_bounce
;
185 void *buf
= (char *)__com32
.cs_bounce
+ SECTOR
;
188 memset(&inreg
, 0, sizeof inreg
);
190 if (disk_info
.ebios
) {
191 dapa
->len
= sizeof(*dapa
);
192 dapa
->count
= 1; /* 1 sector */
193 dapa
->off
= OFFS(buf
);
194 dapa
->seg
= SEG(buf
);
197 inreg
.esi
.w
[0] = OFFS(dapa
);
198 inreg
.ds
= SEG(dapa
);
199 inreg
.edx
.b
[0] = disk_info
.disk
;
200 inreg
.eax
.b
[1] = 0x42; /* Extended read */
202 unsigned int c
, h
, s
, t
;
204 if (!disk_info
.cbios
) {
205 /* We failed to get the geometry */
208 return NULL
; /* Can only read MBR */
214 s
= (lba
% disk_info
.sect
) + 1;
215 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
216 h
= t
% disk_info
.head
;
217 c
= t
/ disk_info
.head
;
220 if (s
> 63 || h
> 256 || c
> 1023)
223 inreg
.eax
.w
[0] = 0x0201; /* Read one sector */
224 inreg
.ecx
.b
[1] = c
& 0xff;
225 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
227 inreg
.edx
.b
[0] = disk_info
.disk
;
228 inreg
.ebx
.w
[0] = OFFS(buf
);
232 if (int13_retry(&inreg
, NULL
))
235 data
= malloc(SECTOR
);
237 memcpy(data
, buf
, SECTOR
);
241 static int write_sector(unsigned int lba
, const void *data
)
244 struct ebios_dapa
*dapa
= __com32
.cs_bounce
;
245 void *buf
= (char *)__com32
.cs_bounce
+ SECTOR
;
247 memcpy(buf
, data
, SECTOR
);
248 memset(&inreg
, 0, sizeof inreg
);
250 if (disk_info
.ebios
) {
251 dapa
->len
= sizeof(*dapa
);
252 dapa
->count
= 1; /* 1 sector */
253 dapa
->off
= OFFS(buf
);
254 dapa
->seg
= SEG(buf
);
257 inreg
.esi
.w
[0] = OFFS(dapa
);
258 inreg
.ds
= SEG(dapa
);
259 inreg
.edx
.b
[0] = disk_info
.disk
;
260 inreg
.eax
.w
[0] = 0x4300; /* Extended write */
262 unsigned int c
, h
, s
, t
;
264 if (!disk_info
.cbios
) {
265 /* We failed to get the geometry */
268 return -1; /* Can only write MBR */
274 s
= (lba
% disk_info
.sect
) + 1;
275 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
276 h
= t
% disk_info
.head
;
277 c
= t
/ disk_info
.head
;
280 if (s
> 63 || h
> 256 || c
> 1023)
283 inreg
.eax
.w
[0] = 0x0301; /* Write one sector */
284 inreg
.ecx
.b
[1] = c
& 0xff;
285 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
287 inreg
.edx
.b
[0] = disk_info
.disk
;
288 inreg
.ebx
.w
[0] = OFFS(buf
);
292 if (int13_retry(&inreg
, NULL
))
298 static int write_verify_sector(unsigned int lba
, const void *buf
)
303 rv
= write_sector(lba
, buf
);
305 return rv
; /* Write failure */
306 rb
= read_sector(lba
);
308 return -1; /* Readback failure */
309 rv
= memcmp(buf
, rb
, SECTOR
);
314 /* Search for a specific drive, based on the MBR signature; bytes
316 static int find_disk(uint32_t mbr_sig
)
322 for (drive
= 0x80; drive
<= 0xff; drive
++) {
323 if (get_disk_params(drive
))
324 continue; /* Drive doesn't exist */
325 if (!(buf
= read_sector(0)))
326 continue; /* Cannot read sector */
327 is_me
= (*(uint32_t *) ((char *)buf
+ 440) == mbr_sig
);
335 /* A DOS partition table entry */
337 uint8_t active_flag
; /* 0x80 if "active" */
347 } __attribute__ ((packed
));
349 /* Search for a logical partition. Logical partitions are actually implemented
350 as recursive partition tables; theoretically they're supposed to form a
351 linked list, but other structures have been seen.
353 To make things extra confusing: data partition offsets are relative to where
354 the data partition record is stored, whereas extended partition offsets
355 are relative to the beginning of the extended partition all the way back
356 at the MBR... but still not absolute! */
358 int nextpart
; /* Number of the next logical partition */
360 static struct part_entry
*find_logical_partition(int whichpart
, char *table
,
361 struct part_entry
*self
,
362 struct part_entry
*root
)
364 static struct part_entry ltab_entry
;
365 struct part_entry
*ptab
= (struct part_entry
*)(table
+ 0x1be);
366 struct part_entry
*found
;
371 if (*(uint16_t *) (table
+ 0x1fe) != 0xaa55)
372 return NULL
; /* Signature missing */
374 /* We are assumed to already having enumerated all the data partitions
375 in this table if this is the MBR. For MBR, self == NULL. */
378 /* Scan the data partitions. */
380 for (i
= 0; i
< 4; i
++) {
381 if (ptab
[i
].ostype
== 0x00 || ptab
[i
].ostype
== 0x05 ||
382 ptab
[i
].ostype
== 0x0f || ptab
[i
].ostype
== 0x85)
383 continue; /* Skip empty or extended partitions */
388 /* Adjust the offset to account for the extended partition itself */
389 ptab
[i
].start_lba
+= self
->start_lba
;
391 /* Sanity check entry: must not extend outside the extended partition.
392 This is necessary since some OSes put crap in some entries. */
393 if (ptab
[i
].start_lba
+ ptab
[i
].length
<= self
->start_lba
||
394 ptab
[i
].start_lba
>= self
->start_lba
+ self
->length
)
397 /* OK, it's a data partition. Is it the one we're looking for? */
398 if (nextpart
++ == whichpart
) {
399 memcpy(<ab_entry
, &ptab
[i
], sizeof ltab_entry
);
405 /* Scan the extended partitions. */
406 for (i
= 0; i
< 4; i
++) {
407 if (ptab
[i
].ostype
!= 0x05 &&
408 ptab
[i
].ostype
!= 0x0f && ptab
[i
].ostype
!= 0x85)
409 continue; /* Skip empty or data partitions */
414 /* Adjust the offset to account for the extended partition itself */
416 ptab
[i
].start_lba
+= root
->start_lba
;
418 /* Sanity check entry: must not extend outside the extended partition.
419 This is necessary since some OSes put crap in some entries. */
421 if (ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
422 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
425 /* Process this partition */
426 if (!(sector
= read_sector(ptab
[i
].start_lba
)))
427 continue; /* Read error, must be invalid */
429 found
= find_logical_partition(whichpart
, sector
, &ptab
[i
],
430 root
? root
: &ptab
[i
]);
436 /* If we get here, there ain't nothing... */
440 static void do_boot(void *boot_sector
, size_t boot_size
,
441 struct syslinux_rm_regs
*regs
)
443 uint16_t *const bios_fbm
= (uint16_t *) 0x413;
444 addr_t dosmem
= *bios_fbm
<< 10; /* Technically a low bound */
445 struct syslinux_memmap
*mmap
;
446 struct syslinux_movelist
*mlist
= NULL
;
448 uint8_t driveno
= regs
->edx
.b
[0];
449 uint8_t swapdrive
= driveno
& 0x80;
451 addr_t loadbase
= opt
.seg
? (opt
.seg
<< 4) : 0x7c00;
453 mmap
= syslinux_memory_map();
456 error("Cannot read system memory map\n");
460 /* Nothing below 0x7c00, much simpler... */
462 if (boot_size
>= dosmem
- loadbase
)
465 endimage
= loadbase
+ boot_size
;
467 if (syslinux_add_movelist
468 (&mlist
, loadbase
, (addr_t
) boot_sector
, boot_size
))
471 if (opt
.swap
&& driveno
!= swapdrive
) {
472 static const uint8_t swapstub_master
[] = {
473 /* The actual swap code */
474 0x53, /* 00: push bx */
475 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
476 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
477 0x5b, /* 08: pop bx */
478 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
479 0x90, 0x90, /* 0E: nop; nop */
480 /* Code to install this in the right location */
481 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
482 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
483 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
484 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
485 0x4f, /* 1F: dec di */
486 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
487 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
488 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
489 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
490 0x8e, 0xc7, /* 32: mov es,di */
491 0x31, 0xff, /* 34: xor di,di */
492 0xf3, 0x66, 0xa5, /* 36: rep movsd */
493 0xbe, 0, 0, /* 39: mov si,0 */
494 0xbf, 0, 0, /* 3C: mov di,0 */
495 0x8e, 0xde, /* 3F: mov ds,si */
496 0x8e, 0xc7, /* 41: mov es,di */
497 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
498 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
499 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
500 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
501 /* pad out to segment boundary */
502 0x90, 0x90, /* 5A: ... */
503 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
505 static uint8_t swapstub
[1024];
508 /* Note: we can't rely on either INT 13h nor the dosmem
509 vector to be correct at this stage, so we have to use an
510 installer stub to put things in the right place.
511 Round the installer location to a 1K boundary so the only
512 possible overlap is the identity mapping. */
513 endimage
= (endimage
+ 1023) & ~1023;
515 /* Create swap stub */
516 memcpy(swapstub
, swapstub_master
, sizeof swapstub_master
);
517 *(uint16_t *) & swapstub
[0x3a] = regs
->ds
;
518 *(uint16_t *) & swapstub
[0x3d] = regs
->es
;
519 *(uint32_t *) & swapstub
[0x45] = regs
->ecx
.l
;
520 *(uint32_t *) & swapstub
[0x4b] = regs
->esi
.l
;
521 *(uint32_t *) & swapstub
[0x51] = regs
->edi
.l
;
522 *(uint16_t *) & swapstub
[0x56] = regs
->ip
;
523 *(uint16_t *) & swapstub
[0x58] = regs
->cs
;
524 p
= &swapstub
[sizeof swapstub_master
];
526 /* Mapping table; start out with identity mapping everything */
527 for (i
= 0; i
< 256; i
++)
530 /* And the actual swap */
531 p
[driveno
] = swapdrive
;
532 p
[swapdrive
] = driveno
;
534 /* Adjust registers */
535 regs
->ds
= regs
->cs
= endimage
>> 4;
536 regs
->es
= regs
->esi
.l
= 0;
537 regs
->ecx
.l
= sizeof swapstub
>> 2;
538 regs
->ip
= 0x10; /* Installer offset */
539 regs
->ebx
.b
[0] = regs
->edx
.b
[0] = swapdrive
;
541 if (syslinux_add_movelist(&mlist
, endimage
, (addr_t
) swapstub
,
545 endimage
+= sizeof swapstub
;
548 /* Tell the shuffler not to muck with this area... */
549 syslinux_add_memmap(&mmap
, endimage
, 0xa0000 - endimage
, SMT_RESERVED
);
551 fputs("Booting...\n", stdout
);
552 syslinux_shuffle_boot_rm(mlist
, mmap
, opt
.keeppxe
, regs
);
553 error("Chainboot failed!\n");
557 error("Loader file too large\n");
561 error("Out of memory\n");
565 static int hide_unhide(char *mbr
, int part
)
568 struct part_entry
*pt
;
569 const uint16_t mask
=
570 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
575 bool write_back
= false;
577 for (i
= 1; i
<= 4; i
++) {
578 pt
= (struct part_entry
*)&mbr
[0x1be + 16 * (i
- 1)];
580 if ((t
<= 0x1f) && ((mask
>> (t
& ~0x10)) & 1)) {
581 /* It's a hideable partition type */
583 t
&= ~0x10; /* unhide */
585 t
|= 0x10; /* hide */
587 if (t
!= pt
->ostype
) {
594 return write_verify_sector(0, mbr
);
601 static uint32_t get_file_lba(const char *filename
)
606 /* Start with clean registers */
607 memset(&inregs
, 0, sizeof(com32sys_t
));
609 /* Put the filename in the bounce buffer */
610 strlcpy(__com32
.cs_bounce
, filename
, __com32
.cs_bounce_size
);
612 /* Call comapi_open() which returns a structure pointer in SI
613 * to a structure whose first member happens to be the LBA.
615 inregs
.eax
.w
[0] = 0x0006;
616 inregs
.esi
.w
[0] = OFFS(__com32
.cs_bounce
);
617 inregs
.es
= SEG(__com32
.cs_bounce
);
618 __com32
.cs_intcall(0x22, &inregs
, &inregs
);
620 if ((inregs
.eflags
.l
& EFLAGS_CF
) || inregs
.esi
.w
[0] == 0) {
621 return 0; /* Filename not found */
624 /* Since the first member is the LBA, we simply cast */
625 lba
= *((uint32_t*)MK_PTR(inregs
.ds
, inregs
.esi
.w
[0]));
627 /* Clean the registers for the next call*/
628 memset(&inregs
, 0, sizeof(com32sys_t
));
630 /* Put the filename in the bounce buffer */
631 strlcpy(__com32
.cs_bounce
, filename
, __com32
.cs_bounce_size
);
633 /* Call comapi_close() to free the structure */
634 inregs
.eax
.w
[0] = 0x0008;
635 inregs
.esi
.w
[0] = OFFS(__com32
.cs_bounce
);
636 inregs
.es
= SEG(__com32
.cs_bounce
);
637 __com32
.cs_intcall(0x22, &inregs
, &inregs
);
642 int main(int argc
, char *argv
[])
645 void *boot_sector
= NULL
;
646 struct part_entry
*partinfo
;
647 struct syslinux_rm_regs regs
;
648 char *drivename
, *partition
;
649 int hd
, drive
, whichpart
;
651 uint32_t file_lba
= 0;
652 unsigned char *isolinux_bin
;
653 uint32_t *checksum
, *chkhead
, *chktail
;
654 size_t boot_size
= SECTOR
;
657 openconsole(&dev_null_r
, &dev_stdcon_w
);
662 /* Prepare the register set */
663 memset(®s
, 0, sizeof regs
);
665 for (i
= 1; i
< argc
; i
++) {
666 if (!strncmp(argv
[i
], "file=", 5)) {
667 opt
.loadfile
= argv
[i
] + 5;
668 } else if (!strncmp(argv
[i
], "seg=", 4)) {
669 uint32_t segval
= strtoul(argv
[i
] + 4, NULL
, 0);
670 if (segval
< 0x50 || segval
> 0x9f000) {
671 error("Invalid segment\n");
675 } else if (!strncmp(argv
[i
], "isolinux=", 9)) {
676 opt
.loadfile
= argv
[i
] + 9;
678 } else if (!strncmp(argv
[i
], "ntldr=", 6)) {
679 opt
.seg
= 0x2000; /* NTLDR wants this address */
680 opt
.loadfile
= argv
[i
] + 6;
681 } else if (!strncmp(argv
[i
], "freedos=", 8)) {
682 opt
.seg
= 0x60; /* FREEDOS wants this address */
683 opt
.loadfile
= argv
[i
] + 8;
684 } else if (!strncmp(argv
[i
], "msdos=", 6) ||
685 !strncmp(argv
[i
], "pcdos=", 6)) {
686 opt
.seg
= 0x70; /* MS-DOS 2.0+ wants this address */
687 opt
.loadfile
= argv
[i
] + 6;
688 } else if (!strcmp(argv
[i
], "swap")) {
690 } else if (!strcmp(argv
[i
], "hide")) {
692 } else if (!strcmp(argv
[i
], "keeppxe")) {
695 if (((argv
[i
][0] == 'h' || argv
[i
][0] == 'f') && argv
[i
][1] == 'd')
696 || !strncmp(argv
[i
], "mbr:", 4)
697 || !strncmp(argv
[i
], "mbr=", 4)
698 || !strcmp(argv
[i
], "boot") || !strncmp(argv
[i
], "boot,", 5)) {
700 p
= strchr(drivename
, ',');
704 } else if (argv
[i
+ 1] && argv
[i
+ 1][0] >= '0'
705 && argv
[i
+ 1][0] <= '9') {
706 partition
= argv
[++i
];
710 ("Usage: chain.c32 hd<disk#> [<partition>] [options]\n"
711 " chain.c32 fd<disk#> [options]\n"
712 " chain.c32 mbr:<id> [<partition>] [options]\n"
713 " chain.c32 boot [<partition>] [options]\n"
714 "Options: file=<loader> load file, instead of boot sector\n"
715 " isolinux=<loader> load another version of ISOLINUX\n"
716 " ntldr=<loader> load Windows bootloaders: NTLDR, SETUPLDR, BOOTMGR\n"
717 " freedos=<loader> load FreeDOS kernel.sys\n"
718 " msdos=<loader> load MS-DOS io.sys\n"
719 " pcdos=<loader> load PC-DOS ibmbio.com\n"
720 " seg=<segment> jump to <seg>:0000 instead of 0000:7C00\n"
721 " swap swap drive numbers, if bootdisk is not fd0/hd0\n"
722 " hide hide primary partitions, except selected partition\n");
728 regs
.es
= regs
.cs
= regs
.ss
= regs
.ds
= regs
.fs
= regs
.gs
= opt
.seg
;
730 regs
.ip
= regs
.esp
.l
= 0x7c00;
734 if (!strncmp(drivename
, "mbr", 3)) {
735 drive
= find_disk(strtoul(drivename
+ 4, NULL
, 0));
737 error("Unable to find requested MBR signature\n");
740 } else if ((drivename
[0] == 'h' || drivename
[0] == 'f') &&
741 drivename
[1] == 'd') {
742 hd
= drivename
[0] == 'h';
744 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
745 } else if (!strcmp(drivename
, "boot")) {
746 const union syslinux_derivative_info
*sdi
;
747 sdi
= syslinux_derivative_info();
748 if (sdi
->c
.filesystem
== SYSLINUX_FS_PXELINUX
)
749 drive
= 0x80; /* Boot drive not available */
751 drive
= sdi
->disk
.drive_number
;
753 error("Unparsable drive specification\n");
757 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
758 regs
.ebx
.b
[0] = regs
.edx
.b
[0] = drive
;
760 whichpart
= 0; /* Default */
763 whichpart
= strtoul(partition
, NULL
, 0);
765 if (!(drive
& 0x80) && whichpart
) {
766 error("Warning: Partitions of floppy devices may not work\n");
769 /* Get the disk geometry and disk access setup */
770 if (get_disk_params(drive
)) {
771 error("Cannot get disk parameters\n");
776 if (!(mbr
= read_sector(0))) {
777 error("Cannot read Master Boot Record\n");
782 if (whichpart
< 1 || whichpart
> 4)
783 error("WARNING: hide specified without a non-primary partition\n");
784 if (hide_unhide(mbr
, whichpart
))
785 error("WARNING: failed to write MBR for 'hide'\n");
788 if (whichpart
== 0) {
793 } else if (whichpart
<= 4) {
794 /* Boot a primary partition */
796 partinfo
= &((struct part_entry
*)(mbr
+ 0x1be))[whichpart
- 1];
797 if (partinfo
->ostype
== 0) {
798 error("Invalid primary partition\n");
802 /* Boot a logical partition */
805 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
807 if (!partinfo
|| partinfo
->ostype
== 0) {
808 error("Requested logical partition not found\n");
813 /* Do the actual chainloading */
815 fputs("Loading the boot file...\n", stdout
);
816 if (loadfile(opt
.loadfile
, &boot_sector
, &boot_size
)) {
817 error("Failed to load the boot file\n");
821 /* Create boot info table: needed when you want to chainload
822 another version of ISOLINUX (or another bootlaoder that needs
823 the -boot-info-table switch of mkisofs)
824 (will only work when run from ISOLINUX) */
826 const union syslinux_derivative_info
*sdi
;
827 sdi
= syslinux_derivative_info();
829 if (sdi
->c
.filesystem
== SYSLINUX_FS_ISOLINUX
) {
830 /* Boot info table info (integers in little endian format)
832 Offset Name Size Meaning
833 8 bi_pvd 4 bytes LBA of primary volume descriptor
834 12 bi_file 4 bytes LBA of boot file
835 16 bi_length 4 bytes Boot file length in bytes
836 20 bi_csum 4 bytes 32-bit checksum
837 24 bi_reserved 40 bytes Reserved
839 The 32-bit checksum is the sum of all the 32-bit words in the
840 boot file starting at byte offset 64. All linear block
841 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
843 LBA of primary volume descriptor should already be set to 16.
846 isolinux_bin
= (unsigned char*)boot_sector
;
848 /* Get LBA address of bootfile */
849 file_lba
= get_file_lba(opt
.loadfile
);
852 error("Failed to find LBA offset of the boot file\n");
856 *((uint32_t*)&isolinux_bin
[12]) = file_lba
;
858 /* Set boot file length */
859 *((uint32_t*)&isolinux_bin
[16]) = boot_size
;
861 /* Calculate checksum */
862 checksum
= (uint32_t*)&isolinux_bin
[20];
863 chkhead
= (uint32_t*)&isolinux_bin
[64];
864 chktail
= (uint32_t*)&isolinux_bin
[boot_size
-1];
865 /* Fresh checksum and clear possibly fractional uint32_t at the end */
866 *checksum
= *((uint32_t*)&isolinux_bin
[boot_size
]) = 0;
868 while (chkhead
<= chktail
)
870 *checksum
+= *chkhead
++;
874 error("The isolinux= option is only valid when run from ISOLINUX\n");
879 } else if (partinfo
) {
880 /* Actually read the boot sector */
881 /* Pick the first buffer that isn't already in use */
882 if (!(boot_sector
= read_sector(partinfo
->start_lba
))) {
883 error("Cannot read boot sector\n");
889 if (*(uint16_t *) ((char *)boot_sector
+ boot_size
- 2) != 0xaa55) {
891 ("Boot sector signature not found (unbootable disk/partition?)\n");
897 /* 0x7BE is the canonical place for the first partition entry. */
898 regs
.esi
.w
[0] = 0x7be;
899 memcpy((char *)0x7be, partinfo
, sizeof(*partinfo
));
902 do_boot(boot_sector
, boot_size
, ®s
);