1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 * Copyright 2009-2010 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> sethidden,
50 * used with WinNT's loaders
53 * used with Recovery Console of Windows NT/2K/XP.
54 * same as ntldr=<loader> & "cmdcons\0" written to
55 * the system name field in the bootsector
58 * equivalent to seg=0x60 file=<loader> sethidden,
59 * used with FreeDOS kernel.sys.
63 * equivalent to seg=0x70 file=<loader> sethidden,
64 * used with DOS' io.sys.
67 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
68 * used with GRUB stage2 files.
71 * if the disk is not fd0/hd0, install a BIOS stub which swaps
75 * change type of primary partitions with IDs 01, 04, 06, 07,
76 * 0b, 0c, or 0e to 1x, except for the selected partition, which
77 * is converted the other way.
80 * update the "hidden sectors" (partition offset) field in a
81 * FAT/NTFS boot sector.
92 #include <syslinux/loadfile.h>
93 #include <syslinux/bootrm.h>
94 #include <syslinux/config.h>
95 #include <syslinux/video.h>
97 #define SECTOR 512 /* bytes/sector */
99 static struct options
{
100 const char *loadfile
;
117 static inline void error(const char *msg
)
123 * Call int 13h, but with retry on failure. Especially floppies need this.
125 static int int13_retry(const com32sys_t
* inreg
, com32sys_t
* outreg
)
127 int retry
= 6; /* Number of retries */
134 __intcall(0x13, inreg
, outreg
);
135 if (!(outreg
->eflags
.l
& EFLAGS_CF
))
136 return 0; /* CF=0, OK */
139 return -1; /* Error */
143 * Query disk parameters and EBIOS availability for a particular disk.
147 int ebios
; /* EBIOS supported on this disk */
148 int cbios
; /* CHS geometry is valid */
153 static int get_disk_params(int disk
)
155 static com32sys_t getparm
, parm
, getebios
, ebios
;
157 disk_info
.disk
= disk
;
158 disk_info
.ebios
= disk_info
.cbios
= 0;
160 /* Get EBIOS support */
161 getebios
.eax
.w
[0] = 0x4100;
162 getebios
.ebx
.w
[0] = 0x55aa;
163 getebios
.edx
.b
[0] = disk
;
164 getebios
.eflags
.b
[0] = 0x3; /* CF set */
166 __intcall(0x13, &getebios
, &ebios
);
168 if (!(ebios
.eflags
.l
& EFLAGS_CF
) &&
169 ebios
.ebx
.w
[0] == 0xaa55 && (ebios
.ecx
.b
[0] & 1)) {
173 /* Get disk parameters -- really only useful for
174 hard disks, but if we have a partitioned floppy
175 it's actually our best chance... */
176 getparm
.eax
.b
[1] = 0x08;
177 getparm
.edx
.b
[0] = disk
;
179 __intcall(0x13, &getparm
, &parm
);
181 if (parm
.eflags
.l
& EFLAGS_CF
)
182 return disk_info
.ebios
? 0 : -1;
184 disk_info
.head
= parm
.edx
.b
[1] + 1;
185 disk_info
.sect
= parm
.ecx
.b
[0] & 0x3f;
186 if (disk_info
.sect
== 0) {
189 disk_info
.cbios
= 1; /* Valid geometry */
196 * Get a disk block and return a malloc'd buffer.
197 * Uses the disk number and information from disk_info.
207 /* Read count sectors from drive, starting at lba. Return a new buffer */
208 static void *read_sectors(uint64_t lba
, uint8_t count
)
211 struct ebios_dapa
*dapa
= __com32
.cs_bounce
;
212 void *buf
= (char *)__com32
.cs_bounce
+ SECTOR
;
219 memset(&inreg
, 0, sizeof inreg
);
221 if (disk_info
.ebios
) {
222 dapa
->len
= sizeof(*dapa
);
224 dapa
->off
= OFFS(buf
);
225 dapa
->seg
= SEG(buf
);
228 inreg
.esi
.w
[0] = OFFS(dapa
);
229 inreg
.ds
= SEG(dapa
);
230 inreg
.edx
.b
[0] = disk_info
.disk
;
231 inreg
.eax
.b
[1] = 0x42; /* Extended read */
233 unsigned int c
, h
, s
, t
;
235 if (!disk_info
.cbios
) {
236 /* We failed to get the geometry */
239 return NULL
; /* Can only read MBR */
245 s
= (lba
% disk_info
.sect
) + 1;
246 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
247 h
= t
% disk_info
.head
;
248 c
= t
/ disk_info
.head
;
251 if (s
> 63 || h
> 256 || c
> 1023)
254 inreg
.eax
.b
[0] = count
;
255 inreg
.eax
.b
[1] = 0x02; /* Read */
256 inreg
.ecx
.b
[1] = c
& 0xff;
257 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
259 inreg
.edx
.b
[0] = disk_info
.disk
;
260 inreg
.ebx
.w
[0] = OFFS(buf
);
264 if (int13_retry(&inreg
, NULL
))
267 data
= malloc(SECTOR
);
269 memcpy(data
, buf
, count
* SECTOR
);
273 static int write_sector(unsigned int lba
, const void *data
)
276 struct ebios_dapa
*dapa
= __com32
.cs_bounce
;
277 void *buf
= (char *)__com32
.cs_bounce
+ SECTOR
;
279 memcpy(buf
, data
, SECTOR
);
280 memset(&inreg
, 0, sizeof inreg
);
282 if (disk_info
.ebios
) {
283 dapa
->len
= sizeof(*dapa
);
284 dapa
->count
= 1; /* 1 sector */
285 dapa
->off
= OFFS(buf
);
286 dapa
->seg
= SEG(buf
);
289 inreg
.esi
.w
[0] = OFFS(dapa
);
290 inreg
.ds
= SEG(dapa
);
291 inreg
.edx
.b
[0] = disk_info
.disk
;
292 inreg
.eax
.w
[0] = 0x4300; /* Extended write */
294 unsigned int c
, h
, s
, t
;
296 if (!disk_info
.cbios
) {
297 /* We failed to get the geometry */
300 return -1; /* Can only write MBR */
306 s
= (lba
% disk_info
.sect
) + 1;
307 t
= lba
/ disk_info
.sect
; /* Track = head*cyl */
308 h
= t
% disk_info
.head
;
309 c
= t
/ disk_info
.head
;
312 if (s
> 63 || h
> 256 || c
> 1023)
315 inreg
.eax
.w
[0] = 0x0301; /* Write one sector */
316 inreg
.ecx
.b
[1] = c
& 0xff;
317 inreg
.ecx
.b
[0] = s
+ (c
>> 6);
319 inreg
.edx
.b
[0] = disk_info
.disk
;
320 inreg
.ebx
.w
[0] = OFFS(buf
);
324 if (int13_retry(&inreg
, NULL
))
330 static int write_verify_sector(unsigned int lba
, const void *buf
)
335 rv
= write_sector(lba
, buf
);
337 return rv
; /* Write failure */
338 rb
= read_sectors(lba
, 1);
340 return -1; /* Readback failure */
341 rv
= memcmp(buf
, rb
, SECTOR
);
346 /* Search for a specific drive, based on the MBR signature; bytes
348 static int find_disk(uint32_t mbr_sig
)
354 for (drive
= 0x80; drive
<= 0xff; drive
++) {
355 if (get_disk_params(drive
))
356 continue; /* Drive doesn't exist */
357 if (!(buf
= read_sectors(0, 1)))
358 continue; /* Cannot read sector */
359 is_me
= (*(uint32_t *) ((char *)buf
+ 440) == mbr_sig
);
368 * CHS (cylinder, head, sector) value extraction macros.
369 * Taken from WinVBlock. Does not expand to an lvalue
371 #define chs_head(chs) chs[0]
372 #define chs_sector(chs) (chs[1] & 0x3F)
373 #define chs_cyl_high(chs) (((uint16_t)(chs[1] & 0xC0)) << 2)
374 #define chs_cyl_low(chs) ((uint16_t)chs[2])
375 #define chs_cylinder(chs) (chs_cyl_high(chs) | chs_cyl_low(chs))
376 typedef uint8_t chs
[3];
378 /* A DOS partition table entry */
380 uint8_t active_flag
; /* 0x80 if "active" */
386 } __attribute__ ((packed
));
388 /* Search for a logical partition. Logical partitions are actually implemented
389 as recursive partition tables; theoretically they're supposed to form a
390 linked list, but other structures have been seen.
392 To make things extra confusing: data partition offsets are relative to where
393 the data partition record is stored, whereas extended partition offsets
394 are relative to the beginning of the extended partition all the way back
395 at the MBR... but still not absolute! */
397 int nextpart
; /* Number of the next logical partition */
399 static struct part_entry
*find_logical_partition(int whichpart
, char *table
,
400 struct part_entry
*self
,
401 struct part_entry
*root
)
403 static struct part_entry ltab_entry
;
404 struct part_entry
*ptab
= (struct part_entry
*)(table
+ 0x1be);
405 struct part_entry
*found
;
410 if (*(uint16_t *) (table
+ 0x1fe) != 0xaa55)
411 return NULL
; /* Signature missing */
413 /* We are assumed to already having enumerated all the data partitions
414 in this table if this is the MBR. For MBR, self == NULL. */
417 /* Scan the data partitions. */
419 for (i
= 0; i
< 4; i
++) {
420 if (ptab
[i
].ostype
== 0x00 || ptab
[i
].ostype
== 0x05 ||
421 ptab
[i
].ostype
== 0x0f || ptab
[i
].ostype
== 0x85)
422 continue; /* Skip empty or extended partitions */
427 /* Adjust the offset to account for the extended partition itself */
428 ptab
[i
].start_lba
+= self
->start_lba
;
431 * Sanity check entry: must not extend outside the
432 * extended partition. This is necessary since some OSes
433 * put crap in some entries. Note that root is non-NULL here.
435 if (ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
436 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
439 /* OK, it's a data partition. Is it the one we're looking for? */
440 if (nextpart
++ == whichpart
) {
441 memcpy(<ab_entry
, &ptab
[i
], sizeof ltab_entry
);
447 /* Scan the extended partitions. */
448 for (i
= 0; i
< 4; i
++) {
449 if (ptab
[i
].ostype
!= 0x05 &&
450 ptab
[i
].ostype
!= 0x0f && ptab
[i
].ostype
!= 0x85)
451 continue; /* Skip empty or data partitions */
456 /* Adjust the offset to account for the extended partition itself */
458 ptab
[i
].start_lba
+= root
->start_lba
;
460 /* Sanity check entry: must not extend outside the extended partition.
461 This is necessary since some OSes put crap in some entries. */
463 if (ptab
[i
].start_lba
+ ptab
[i
].length
<= root
->start_lba
||
464 ptab
[i
].start_lba
>= root
->start_lba
+ root
->length
)
467 /* Process this partition */
468 if (!(sector
= read_sectors(ptab
[i
].start_lba
, 1)))
469 continue; /* Read error, must be invalid */
471 found
= find_logical_partition(whichpart
, sector
, &ptab
[i
],
472 root
? root
: &ptab
[i
]);
478 /* If we get here, there ain't nothing... */
482 static void do_boot(struct data_area
*data
, int ndata
,
483 struct syslinux_rm_regs
*regs
)
485 uint16_t *const bios_fbm
= (uint16_t *) 0x413;
486 addr_t dosmem
= *bios_fbm
<< 10; /* Technically a low bound */
487 struct syslinux_memmap
*mmap
;
488 struct syslinux_movelist
*mlist
= NULL
;
490 uint8_t driveno
= regs
->edx
.b
[0];
491 uint8_t swapdrive
= driveno
& 0x80;
494 mmap
= syslinux_memory_map();
497 error("Cannot read system memory map\n");
502 for (i
= 0; i
< ndata
; i
++) {
503 if (data
[i
].base
+ data
[i
].size
> endimage
)
504 endimage
= data
[i
].base
+ data
[i
].size
;
506 if (endimage
> dosmem
)
509 for (i
= 0; i
< ndata
; i
++) {
510 if (syslinux_add_movelist(&mlist
, data
[i
].base
,
511 (addr_t
) data
[i
].data
, data
[i
].size
))
515 if (opt
.swap
&& driveno
!= swapdrive
) {
516 static const uint8_t swapstub_master
[] = {
517 /* The actual swap code */
518 0x53, /* 00: push bx */
519 0x0f, 0xb6, 0xda, /* 01: movzx bx,dl */
520 0x2e, 0x8a, 0x57, 0x60, /* 04: mov dl,[cs:bx+0x60] */
521 0x5b, /* 08: pop bx */
522 0xea, 0, 0, 0, 0, /* 09: jmp far 0:0 */
523 0x90, 0x90, /* 0E: nop; nop */
524 /* Code to install this in the right location */
525 /* Entry with DS = CS; ES = SI = 0; CX = 256 */
526 0x26, 0x66, 0x8b, 0x7c, 0x4c, /* 10: mov edi,[es:si+4*0x13] */
527 0x66, 0x89, 0x3e, 0x0a, 0x00, /* 15: mov [0x0A],edi */
528 0x26, 0x8b, 0x3e, 0x13, 0x04, /* 1A: mov di,[es:0x413] */
529 0x4f, /* 1F: dec di */
530 0x26, 0x89, 0x3e, 0x13, 0x04, /* 20: mov [es:0x413],di */
531 0x66, 0xc1, 0xe7, 0x16, /* 25: shl edi,16+6 */
532 0x26, 0x66, 0x89, 0x7c, 0x4c, /* 29: mov [es:si+4*0x13],edi */
533 0x66, 0xc1, 0xef, 0x10, /* 2E: shr edi,16 */
534 0x8e, 0xc7, /* 32: mov es,di */
535 0x31, 0xff, /* 34: xor di,di */
536 0xf3, 0x66, 0xa5, /* 36: rep movsd */
537 0xbe, 0, 0, /* 39: mov si,0 */
538 0xbf, 0, 0, /* 3C: mov di,0 */
539 0x8e, 0xde, /* 3F: mov ds,si */
540 0x8e, 0xc7, /* 41: mov es,di */
541 0x66, 0xb9, 0, 0, 0, 0, /* 43: mov ecx,0 */
542 0x66, 0xbe, 0, 0, 0, 0, /* 49: mov esi,0 */
543 0x66, 0xbf, 0, 0, 0, 0, /* 4F: mov edi,0 */
544 0xea, 0, 0, 0, 0, /* 55: jmp 0:0 */
545 /* pad out to segment boundary */
546 0x90, 0x90, /* 5A: ... */
547 0x90, 0x90, 0x90, 0x90, /* 5C: ... */
549 static uint8_t swapstub
[1024];
552 /* Note: we can't rely on either INT 13h nor the dosmem
553 vector to be correct at this stage, so we have to use an
554 installer stub to put things in the right place.
555 Round the installer location to a 1K boundary so the only
556 possible overlap is the identity mapping. */
557 endimage
= (endimage
+ 1023) & ~1023;
559 /* Create swap stub */
560 memcpy(swapstub
, swapstub_master
, sizeof swapstub_master
);
561 *(uint16_t *) & swapstub
[0x3a] = regs
->ds
;
562 *(uint16_t *) & swapstub
[0x3d] = regs
->es
;
563 *(uint32_t *) & swapstub
[0x45] = regs
->ecx
.l
;
564 *(uint32_t *) & swapstub
[0x4b] = regs
->esi
.l
;
565 *(uint32_t *) & swapstub
[0x51] = regs
->edi
.l
;
566 *(uint16_t *) & swapstub
[0x56] = regs
->ip
;
567 *(uint16_t *) & swapstub
[0x58] = regs
->cs
;
568 p
= &swapstub
[sizeof swapstub_master
];
570 /* Mapping table; start out with identity mapping everything */
571 for (i
= 0; i
< 256; i
++)
574 /* And the actual swap */
575 p
[driveno
] = swapdrive
;
576 p
[swapdrive
] = driveno
;
578 /* Adjust registers */
579 regs
->ds
= regs
->cs
= endimage
>> 4;
580 regs
->es
= regs
->esi
.l
= 0;
581 regs
->ecx
.l
= sizeof swapstub
>> 2;
582 regs
->ip
= 0x10; /* Installer offset */
583 regs
->ebx
.b
[0] = regs
->edx
.b
[0] = swapdrive
;
585 if (syslinux_add_movelist(&mlist
, endimage
, (addr_t
) swapstub
,
589 endimage
+= sizeof swapstub
;
592 /* Tell the shuffler not to muck with this area... */
593 syslinux_add_memmap(&mmap
, endimage
, 0xa0000 - endimage
, SMT_RESERVED
);
595 /* Force text mode */
596 syslinux_force_text_mode();
598 fputs("Booting...\n", stdout
);
599 syslinux_shuffle_boot_rm(mlist
, mmap
, opt
.keeppxe
, regs
);
600 error("Chainboot failed!\n");
604 error("Loader file too large\n");
608 error("Out of memory\n");
612 static int hide_unhide(char *mbr
, int part
)
615 struct part_entry
*pt
;
616 const uint16_t mask
=
617 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
622 bool write_back
= false;
624 for (i
= 1; i
<= 4; i
++) {
625 pt
= (struct part_entry
*)&mbr
[0x1be + 16 * (i
- 1)];
627 if ((t
<= 0x1f) && ((mask
>> (t
& ~0x10)) & 1)) {
628 /* It's a hideable partition type */
630 t
&= ~0x10; /* unhide */
632 t
|= 0x10; /* hide */
634 if (t
!= pt
->ostype
) {
641 return write_verify_sector(0, mbr
);
646 static uint32_t get_file_lba(const char *filename
)
651 /* Start with clean registers */
652 memset(&inregs
, 0, sizeof(com32sys_t
));
654 /* Put the filename in the bounce buffer */
655 strlcpy(__com32
.cs_bounce
, filename
, __com32
.cs_bounce_size
);
657 /* Call comapi_open() which returns a structure pointer in SI
658 * to a structure whose first member happens to be the LBA.
660 inregs
.eax
.w
[0] = 0x0006;
661 inregs
.esi
.w
[0] = OFFS(__com32
.cs_bounce
);
662 inregs
.es
= SEG(__com32
.cs_bounce
);
663 __com32
.cs_intcall(0x22, &inregs
, &inregs
);
665 if ((inregs
.eflags
.l
& EFLAGS_CF
) || inregs
.esi
.w
[0] == 0) {
666 return 0; /* Filename not found */
669 /* Since the first member is the LBA, we simply cast */
670 lba
= *((uint32_t *) MK_PTR(inregs
.ds
, inregs
.esi
.w
[0]));
672 /* Clean the registers for the next call */
673 memset(&inregs
, 0, sizeof(com32sys_t
));
675 /* Put the filename in the bounce buffer */
676 strlcpy(__com32
.cs_bounce
, filename
, __com32
.cs_bounce_size
);
678 /* Call comapi_close() to free the structure */
679 inregs
.eax
.w
[0] = 0x0008;
680 inregs
.esi
.w
[0] = OFFS(__com32
.cs_bounce
);
681 inregs
.es
= SEG(__com32
.cs_bounce
);
682 __com32
.cs_intcall(0x22, &inregs
, &inregs
);
687 static void usage(void)
689 error("Usage: chain.c32 hd<disk#> [<partition>] [options]\n"
690 " chain.c32 fd<disk#> [options]\n"
691 " chain.c32 mbr:<id> [<partition>] [options]\n"
692 " chain.c32 boot [<partition>] [options]\n"
693 "Options: file=<loader> load file, instead of boot sector\n"
694 " isolinux=<loader> load another version of ISOLINUX\n"
695 " ntldr=<loader> load Windows NTLDR, SETUPLDR.BIN or BOOTMGR\n"
696 " cmldr=<loader> load Recovery Console of Windows NT/2K/XP\n"
697 " freedos=<loader> load FreeDOS kernel.sys\n"
698 " msdos=<loader> load MS-DOS io.sys\n"
699 " pcdos=<loader> load PC-DOS ibmbio.com\n"
700 " grub=<loader> load GRUB stage2\n"
701 " seg=<segment> jump to <seg>:0000 instead of 0000:7C00\n"
702 " swap swap drive numbers, if bootdisk is not fd0/hd0\n"
703 " hide hide primary partitions, except selected partition\n"
704 " sethidden set the FAT/NTFS hidden sectors field\n");
707 int main(int argc
, char *argv
[])
710 struct part_entry
*partinfo
;
711 struct syslinux_rm_regs regs
;
712 char *drivename
, *partition
;
713 int hd
, drive
, whichpart
;
715 uint32_t file_lba
= 0;
716 unsigned char *isolinux_bin
;
717 uint32_t *checksum
, *chkhead
, *chktail
;
718 struct data_area data
[3];
721 static const char cmldr_signature
[8] = "cmdcons";
723 openconsole(&dev_null_r
, &dev_stdcon_w
);
728 /* Prepare the register set */
729 memset(®s
, 0, sizeof regs
);
731 for (i
= 1; i
< argc
; i
++) {
732 if (!strncmp(argv
[i
], "file=", 5)) {
733 opt
.loadfile
= argv
[i
] + 5;
734 } else if (!strncmp(argv
[i
], "seg=", 4)) {
735 uint32_t segval
= strtoul(argv
[i
] + 4, NULL
, 0);
736 if (segval
< 0x50 || segval
> 0x9f000) {
737 error("Invalid segment\n");
741 } else if (!strncmp(argv
[i
], "isolinux=", 9)) {
742 opt
.loadfile
= argv
[i
] + 9;
744 } else if (!strncmp(argv
[i
], "ntldr=", 6)) {
745 opt
.seg
= 0x2000; /* NTLDR wants this address */
746 opt
.loadfile
= argv
[i
] + 6;
747 opt
.sethidden
= true;
748 } else if (!strncmp(argv
[i
], "cmldr=", 6)) {
749 opt
.seg
= 0x2000; /* CMLDR wants this address */
750 opt
.loadfile
= argv
[i
] + 6;
752 opt
.sethidden
= true;
753 } else if (!strncmp(argv
[i
], "freedos=", 8)) {
754 opt
.seg
= 0x60; /* FREEDOS wants this address */
755 opt
.loadfile
= argv
[i
] + 8;
756 opt
.sethidden
= true;
757 } else if (!strncmp(argv
[i
], "msdos=", 6) ||
758 !strncmp(argv
[i
], "pcdos=", 6)) {
759 opt
.seg
= 0x70; /* MS-DOS 2.0+ wants this address */
760 opt
.loadfile
= argv
[i
] + 6;
761 opt
.sethidden
= true;
762 } else if (!strncmp(argv
[i
], "grub=", 5)) {
763 opt
.seg
= 0x800; /* stage2 wants this address */
764 opt
.loadfile
= argv
[i
] + 5;
766 } else if (!strcmp(argv
[i
], "swap")) {
768 } else if (!strcmp(argv
[i
], "noswap")) {
770 } else if (!strcmp(argv
[i
], "hide")) {
772 } else if (!strcmp(argv
[i
], "nohide")) {
774 } else if (!strcmp(argv
[i
], "keeppxe")) {
776 } else if (!strcmp(argv
[i
], "sethidden")) {
777 opt
.sethidden
= true;
778 } else if (!strcmp(argv
[i
], "nosethidden")) {
779 opt
.sethidden
= false;
780 } else if (((argv
[i
][0] == 'h' || argv
[i
][0] == 'f')
781 && argv
[i
][1] == 'd')
782 || !strncmp(argv
[i
], "mbr:", 4)
783 || !strncmp(argv
[i
], "mbr=", 4)
784 || !strcmp(argv
[i
], "boot")
785 || !strncmp(argv
[i
], "boot,", 5)) {
787 p
= strchr(drivename
, ',');
791 } else if (argv
[i
+ 1] && argv
[i
+ 1][0] >= '0'
792 && argv
[i
+ 1][0] <= '9') {
793 partition
= argv
[++i
];
802 regs
.es
= regs
.cs
= regs
.ss
= regs
.ds
= regs
.fs
= regs
.gs
= opt
.seg
;
804 regs
.ip
= regs
.esp
.l
= 0x7c00;
808 if (!strncmp(drivename
, "mbr", 3)) {
809 drive
= find_disk(strtoul(drivename
+ 4, NULL
, 0));
811 error("Unable to find requested MBR signature\n");
814 } else if ((drivename
[0] == 'h' || drivename
[0] == 'f') &&
815 drivename
[1] == 'd') {
816 hd
= drivename
[0] == 'h';
818 drive
= (hd
? 0x80 : 0) | strtoul(drivename
, NULL
, 0);
819 } else if (!strcmp(drivename
, "boot")) {
820 const union syslinux_derivative_info
*sdi
;
821 sdi
= syslinux_derivative_info();
822 if (sdi
->c
.filesystem
== SYSLINUX_FS_PXELINUX
)
823 drive
= 0x80; /* Boot drive not available */
825 drive
= sdi
->disk
.drive_number
;
827 error("Unparsable drive specification\n");
831 /* DOS kernels want the drive number in BL instead of DL. Indulge them. */
832 regs
.ebx
.b
[0] = regs
.edx
.b
[0] = drive
;
834 whichpart
= 0; /* Default */
836 whichpart
= strtoul(partition
, NULL
, 0);
838 if (!(drive
& 0x80) && whichpart
) {
839 error("Warning: Partitions of floppy devices may not work\n");
843 * grldr of Grub4dos wants the partition number in DH:
844 * -1: whole drive (default)
845 * 0-3: primary partitions
846 * 4-*: logical partitions
848 regs
.edx
.b
[1] = whichpart
- 1;
850 /* Get the disk geometry and disk access setup */
851 if (get_disk_params(drive
)) {
852 error("Cannot get disk parameters\n");
857 if (!(mbr
= read_sectors(0, 1))) {
858 error("Cannot read Master Boot Record\n");
863 if (whichpart
< 1 || whichpart
> 4)
864 error("WARNING: hide specified without a non-primary partition\n");
865 if (hide_unhide(mbr
, whichpart
))
866 error("WARNING: failed to write MBR for 'hide'\n");
869 if (whichpart
== 0) {
873 } else if (whichpart
<= 4) {
874 /* Boot a primary partition */
876 partinfo
= &((struct part_entry
*)(mbr
+ 0x1be))[whichpart
- 1];
877 if (partinfo
->ostype
== 0) {
878 error("Invalid primary partition\n");
882 /* Boot a logical partition */
885 partinfo
= find_logical_partition(whichpart
, mbr
, NULL
, NULL
);
887 if (!partinfo
|| partinfo
->ostype
== 0) {
888 error("Requested logical partition not found\n");
893 /* Do the actual chainloading */
894 load_base
= opt
.seg
? (opt
.seg
<< 4) : 0x7c00;
897 fputs("Loading the boot file...\n", stdout
);
898 if (loadfile(opt
.loadfile
, &data
[ndata
].data
, &data
[ndata
].size
)) {
899 error("Failed to load the boot file\n");
902 data
[ndata
].base
= load_base
;
903 load_base
= 0x7c00; /* If we also load a boot sector */
905 /* Create boot info table: needed when you want to chainload
906 another version of ISOLINUX (or another bootlaoder that needs
907 the -boot-info-table switch of mkisofs)
908 (will only work when run from ISOLINUX) */
910 const union syslinux_derivative_info
*sdi
;
911 sdi
= syslinux_derivative_info();
913 if (sdi
->c
.filesystem
== SYSLINUX_FS_ISOLINUX
) {
914 /* Boot info table info (integers in little endian format)
916 Offset Name Size Meaning
917 8 bi_pvd 4 bytes LBA of primary volume descriptor
918 12 bi_file 4 bytes LBA of boot file
919 16 bi_length 4 bytes Boot file length in bytes
920 20 bi_csum 4 bytes 32-bit checksum
921 24 bi_reserved 40 bytes Reserved
923 The 32-bit checksum is the sum of all the 32-bit words in the
924 boot file starting at byte offset 64. All linear block
925 addresses (LBAs) are given in CD sectors (normally 2048 bytes).
927 LBA of primary volume descriptor should already be set to 16.
930 isolinux_bin
= (unsigned char *)data
[ndata
].data
;
932 /* Get LBA address of bootfile */
933 file_lba
= get_file_lba(opt
.loadfile
);
936 error("Failed to find LBA offset of the boot file\n");
940 *((uint32_t *) & isolinux_bin
[12]) = file_lba
;
942 /* Set boot file length */
943 *((uint32_t *) & isolinux_bin
[16]) = data
[ndata
].size
;
945 /* Calculate checksum */
946 checksum
= (uint32_t *) & isolinux_bin
[20];
947 chkhead
= (uint32_t *) & isolinux_bin
[64];
948 chktail
= (uint32_t *) & isolinux_bin
[data
[ndata
].size
& ~3];
950 while (chkhead
< chktail
)
951 *checksum
+= *chkhead
++;
954 * Deal with possible fractional dword at the end;
955 * this *should* never happen...
957 if (data
[ndata
].size
& 3) {
959 memcpy(&xword
, chkhead
, data
[ndata
].size
& 3);
964 ("The isolinux= option is only valid when run from ISOLINUX\n");
970 regs
.ip
= 0x200; /* jump 0x200 bytes into the loadfile */
972 /* 0xffffff00 seems to be GRUB ways to record that it's
973 "root" is the whole disk (and not a partition). */
974 *(uint32_t *) ((unsigned char *)data
[ndata
].data
+ 0x208) =
981 if (!opt
.loadfile
|| data
[0].base
>= 0x7c00 + SECTOR
) {
982 /* Actually read the boot sector */
984 data
[ndata
].data
= mbr
;
985 } else if (!(data
[ndata
].data
= read_sectors(partinfo
->start_lba
, 1))) {
986 error("Cannot read boot sector\n");
989 data
[ndata
].size
= SECTOR
;
990 data
[ndata
].base
= load_base
;
993 *(uint16_t *) ((char *)data
[ndata
].data
+
994 data
[ndata
].size
- 2) != 0xaa55) {
996 ("Boot sector signature not found (unbootable disk/partition?)\n");
1001 * To boot the Recovery Console of Windows NT/2K/XP we need to write
1002 * the string "cmdcons\0" to memory location 0000:7C03.
1003 * Memory location 0000:7C00 contains the bootsector of the partition.
1005 if (partinfo
&& opt
.cmldr
) {
1006 memcpy((char *)data
[ndata
].data
+ 3, cmldr_signature
,
1007 sizeof cmldr_signature
);
1011 * Modify the hidden sectors (partition offset) copy in memory;
1012 * this modifies the field used by FAT and NTFS filesystems, and
1013 * possibly other boot loaders which use the same format.
1015 if (partinfo
&& opt
.sethidden
) {
1016 *(uint32_t *) ((char *)data
[ndata
].data
+ 28) = partinfo
->start_lba
;
1023 /* 0x7BE is the canonical place for the first partition entry. */
1024 data
[ndata
].data
= partinfo
;
1025 data
[ndata
].base
= 0x7be;
1026 data
[ndata
].size
= sizeof *partinfo
;
1028 regs
.esi
.w
[0] = 0x7be;
1031 do_boot(data
, ndata
, ®s
);