[chain] Add support for chain-loading another isolinux.bin
[hdt-cyring.git] / com32 / modules / chain.c
blob2e5b6e471a4e02e5eb0e7286a141d3802b034601
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 * ----------------------------------------------------------------------- */
15 * chain.c
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
25 * disk.
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.)
33 * Options:
35 * file=<loader>:
36 * loads the file <loader> **from the SYSLINUX filesystem**
37 * instead of loading the boot sector.
39 * seg=<segment>:
40 * loads at and jumps to <seg>:0000 instead of 0000:7C00.
42 * isolinux=<loader>:
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.
48 * ntldr=<loader>:
49 * equivalent to -seg 0x2000 -file <loader>, used with WinNT's loaders
51 * freedos=<loader>:
52 * equivalent to -seg 0x60 -file <loader>, used with FreeDOS kernel.sys.
54 * msdos=<loader>
55 * pcdos=<loader>
56 * equivalent to -seg 0x70 -file <loader>, used with DOS' io.sys.
58 * swap:
59 * if the disk is not fd0/hd0, install a BIOS stub which swaps
60 * the drive numbers.
62 * hide:
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.
68 #include <com32.h>
69 #include <stdlib.h>
70 #include <stdio.h>
71 #include <ctype.h>
72 #include <string.h>
73 #include <console.h>
74 #include <minmax.h>
75 #include <stdbool.h>
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 {
83 const char *loadfile;
84 uint16_t keeppxe;
85 uint16_t seg;
86 bool isolinux;
87 bool swap;
88 bool hide;
89 } opt;
91 static inline void error(const char *msg)
93 fputs(msg, stderr);
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 */
102 com32sys_t tmpregs;
104 if (!outreg)
105 outreg = &tmpregs;
107 while (retry--) {
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.
119 struct diskinfo {
120 int disk;
121 int ebios; /* EBIOS supported on this disk */
122 int cbios; /* CHS geometry is valid */
123 int head;
124 int sect;
125 } disk_info;
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)) {
144 disk_info.ebios = 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) {
161 disk_info.sect = 1;
162 } else {
163 disk_info.cbios = 1; /* Valid geometry */
166 return 0;
170 * Get a disk block and return a malloc'd buffer.
171 * Uses the disk number and information from disk_info.
173 struct ebios_dapa {
174 uint16_t len;
175 uint16_t count;
176 uint16_t off;
177 uint16_t seg;
178 uint64_t lba;
181 static void *read_sector(unsigned int lba)
183 com32sys_t inreg;
184 struct ebios_dapa *dapa = __com32.cs_bounce;
185 void *buf = (char *)__com32.cs_bounce + SECTOR;
186 void *data;
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);
195 dapa->lba = lba;
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 */
201 } else {
202 unsigned int c, h, s, t;
204 if (!disk_info.cbios) {
205 /* We failed to get the geometry */
207 if (lba)
208 return NULL; /* Can only read MBR */
210 s = 1;
211 h = 0;
212 c = 0;
213 } else {
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)
221 return NULL;
223 inreg.eax.w[0] = 0x0201; /* Read one sector */
224 inreg.ecx.b[1] = c & 0xff;
225 inreg.ecx.b[0] = s + (c >> 6);
226 inreg.edx.b[1] = h;
227 inreg.edx.b[0] = disk_info.disk;
228 inreg.ebx.w[0] = OFFS(buf);
229 inreg.es = SEG(buf);
232 if (int13_retry(&inreg, NULL))
233 return NULL;
235 data = malloc(SECTOR);
236 if (data)
237 memcpy(data, buf, SECTOR);
238 return data;
241 static int write_sector(unsigned int lba, const void *data)
243 com32sys_t inreg;
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);
255 dapa->lba = lba;
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 */
261 } else {
262 unsigned int c, h, s, t;
264 if (!disk_info.cbios) {
265 /* We failed to get the geometry */
267 if (lba)
268 return -1; /* Can only write MBR */
270 s = 1;
271 h = 0;
272 c = 0;
273 } else {
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)
281 return -1;
283 inreg.eax.w[0] = 0x0301; /* Write one sector */
284 inreg.ecx.b[1] = c & 0xff;
285 inreg.ecx.b[0] = s + (c >> 6);
286 inreg.edx.b[1] = h;
287 inreg.edx.b[0] = disk_info.disk;
288 inreg.ebx.w[0] = OFFS(buf);
289 inreg.es = SEG(buf);
292 if (int13_retry(&inreg, NULL))
293 return -1;
295 return 0; /* ok */
298 static int write_verify_sector(unsigned int lba, const void *buf)
300 char *rb;
301 int rv;
303 rv = write_sector(lba, buf);
304 if (rv)
305 return rv; /* Write failure */
306 rb = read_sector(lba);
307 if (!rb)
308 return -1; /* Readback failure */
309 rv = memcmp(buf, rb, SECTOR);
310 free(rb);
311 return rv ? -1 : 0;
314 /* Search for a specific drive, based on the MBR signature; bytes
315 440-443. */
316 static int find_disk(uint32_t mbr_sig)
318 int drive;
319 bool is_me;
320 char *buf;
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);
328 free(buf);
329 if (is_me)
330 return drive;
332 return -1;
335 /* A DOS partition table entry */
336 struct part_entry {
337 uint8_t active_flag; /* 0x80 if "active" */
338 uint8_t start_head;
339 uint8_t start_sect;
340 uint8_t start_cyl;
341 uint8_t ostype;
342 uint8_t end_head;
343 uint8_t end_sect;
344 uint8_t end_cyl;
345 uint32_t start_lba;
346 uint32_t length;
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;
367 char *sector;
369 int i;
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. */
377 if (self) {
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 */
385 if (!ptab[i].length)
386 continue;
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)
395 continue;
397 /* OK, it's a data partition. Is it the one we're looking for? */
398 if (nextpart++ == whichpart) {
399 memcpy(&ltab_entry, &ptab[i], sizeof ltab_entry);
400 return &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 */
411 if (!ptab[i].length)
412 continue;
414 /* Adjust the offset to account for the extended partition itself */
415 if (root)
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. */
420 if (root)
421 if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
422 ptab[i].start_lba >= root->start_lba + root->length)
423 continue;
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]);
431 free(sector);
432 if (found)
433 return found;
436 /* If we get here, there ain't nothing... */
437 return NULL;
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;
447 addr_t endimage;
448 uint8_t driveno = regs->edx.b[0];
449 uint8_t swapdrive = driveno & 0x80;
450 int i;
451 addr_t loadbase = opt.seg ? (opt.seg << 4) : 0x7c00;
453 mmap = syslinux_memory_map();
455 if (!mmap) {
456 error("Cannot read system memory map\n");
457 return;
460 /* Nothing below 0x7c00, much simpler... */
462 if (boot_size >= dosmem - loadbase)
463 goto too_big;
465 endimage = loadbase + boot_size;
467 if (syslinux_add_movelist
468 (&mlist, loadbase, (addr_t) boot_sector, boot_size))
469 goto enomem;
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];
506 uint8_t *p;
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++)
528 p[i] = 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,
542 sizeof swapstub))
543 goto enomem;
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");
554 return;
556 too_big:
557 error("Loader file too large\n");
558 return;
560 enomem:
561 error("Out of memory\n");
562 return;
565 static int hide_unhide(char *mbr, int part)
567 int i;
568 struct part_entry *pt;
569 const uint16_t mask =
570 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
572 0x0c)
573 | (1 << 0x0e);
574 uint8_t t;
575 bool write_back = false;
577 for (i = 1; i <= 4; i++) {
578 pt = (struct part_entry *)&mbr[0x1be + 16 * (i - 1)];
579 t = pt->ostype;
580 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
581 /* It's a hideable partition type */
582 if (i == part)
583 t &= ~0x10; /* unhide */
584 else
585 t |= 0x10; /* hide */
587 if (t != pt->ostype) {
588 write_back = true;
589 pt->ostype = t;
593 if (write_back)
594 return write_verify_sector(0, mbr);
596 return 0; /* ok */
601 static uint32_t get_file_lba(const char *filename)
603 com32sys_t inregs;
604 uint32_t lba;
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);
639 return lba;
642 int main(int argc, char *argv[])
644 char *mbr, *p;
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;
650 int i;
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);
659 drivename = "boot";
660 partition = NULL;
662 /* Prepare the register set */
663 memset(&regs, 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");
672 goto bail;
674 opt.seg = segval;
675 } else if (!strncmp(argv[i], "isolinux=", 9)) {
676 opt.loadfile = argv[i] + 9;
677 opt.isolinux = true;
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")) {
689 opt.swap = true;
690 } else if (!strcmp(argv[i], "hide")) {
691 opt.hide = true;
692 } else if (!strcmp(argv[i], "keeppxe")) {
693 opt.keeppxe = 3;
694 } else
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)) {
699 drivename = argv[i];
700 p = strchr(drivename, ',');
701 if (p) {
702 *p = '\0';
703 partition = p + 1;
704 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
705 && argv[i + 1][0] <= '9') {
706 partition = argv[++i];
708 } else {
709 error
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");
723 goto bail;
727 if (opt.seg) {
728 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
729 } else {
730 regs.ip = regs.esp.l = 0x7c00;
733 hd = 0;
734 if (!strncmp(drivename, "mbr", 3)) {
735 drive = find_disk(strtoul(drivename + 4, NULL, 0));
736 if (drive == -1) {
737 error("Unable to find requested MBR signature\n");
738 goto bail;
740 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
741 drivename[1] == 'd') {
742 hd = drivename[0] == 'h';
743 drivename += 2;
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 */
750 else
751 drive = sdi->disk.drive_number;
752 } else {
753 error("Unparsable drive specification\n");
754 goto bail;
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 */
762 if (partition)
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");
772 goto bail;
775 /* Get MBR */
776 if (!(mbr = read_sector(0))) {
777 error("Cannot read Master Boot Record\n");
778 goto bail;
781 if (opt.hide) {
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) {
789 /* Boot the MBR */
791 partinfo = NULL;
792 boot_sector = mbr;
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");
799 goto bail;
801 } else {
802 /* Boot a logical partition */
804 nextpart = 5;
805 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
807 if (!partinfo || partinfo->ostype == 0) {
808 error("Requested logical partition not found\n");
809 goto bail;
813 /* Do the actual chainloading */
814 if (opt.loadfile) {
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");
818 goto bail;
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) */
825 if (opt.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);
851 if (file_lba == 0) {
852 error("Failed to find LBA offset of the boot file\n");
853 goto bail;
855 /* Set it */
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++;
873 else {
874 error("The isolinux= option is only valid when run from ISOLINUX\n");
875 goto bail;
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");
884 goto bail;
888 if (!opt.loadfile) {
889 if (*(uint16_t *) ((char *)boot_sector + boot_size - 2) != 0xaa55) {
890 error
891 ("Boot sector signature not found (unbootable disk/partition?)\n");
892 goto bail;
896 if (partinfo) {
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, &regs);
904 bail:
905 return 255;