chain: Use CHS typedef and macros
[hdt-cyring.git] / com32 / modules / chain.c
blobf0ddc389eb43dff4876101a722b142f79511e5c8
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 * ----------------------------------------------------------------------- */
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> sethidden,
50 * used with WinNT's loaders
52 * cmldr=<loader>:
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
57 * freedos=<loader>:
58 * equivalent to seg=0x60 file=<loader> sethidden,
59 * used with FreeDOS kernel.sys.
61 * msdos=<loader>
62 * pcdos=<loader>
63 * equivalent to seg=0x70 file=<loader> sethidden,
64 * used with DOS' io.sys.
66 * grub=<loader>:
67 * same as seg=0x800 file=<loader> & jumping to seg 0x820,
68 * used with GRUB stage2 files.
70 * swap:
71 * if the disk is not fd0/hd0, install a BIOS stub which swaps
72 * the drive numbers.
74 * hide:
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.
79 * sethidden:
80 * update the "hidden sectors" (partition offset) field in a
81 * FAT/NTFS boot sector.
84 #include <com32.h>
85 #include <stdlib.h>
86 #include <stdio.h>
87 #include <ctype.h>
88 #include <string.h>
89 #include <console.h>
90 #include <minmax.h>
91 #include <stdbool.h>
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;
101 uint16_t keeppxe;
102 uint16_t seg;
103 bool isolinux;
104 bool cmldr;
105 bool swap;
106 bool hide;
107 bool sethidden;
108 bool grub;
109 } opt;
111 struct data_area {
112 void *data;
113 addr_t base;
114 addr_t size;
117 static inline void error(const char *msg)
119 fputs(msg, stderr);
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 */
128 com32sys_t tmpregs;
130 if (!outreg)
131 outreg = &tmpregs;
133 while (retry--) {
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.
145 struct diskinfo {
146 int disk;
147 int ebios; /* EBIOS supported on this disk */
148 int cbios; /* CHS geometry is valid */
149 int head;
150 int sect;
151 } disk_info;
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)) {
170 disk_info.ebios = 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) {
187 disk_info.sect = 1;
188 } else {
189 disk_info.cbios = 1; /* Valid geometry */
192 return 0;
196 * Get a disk block and return a malloc'd buffer.
197 * Uses the disk number and information from disk_info.
199 struct ebios_dapa {
200 uint16_t len;
201 uint16_t count;
202 uint16_t off;
203 uint16_t seg;
204 uint64_t lba;
207 /* Read count sectors from drive, starting at lba. Return a new buffer */
208 static void *read_sectors(uint64_t lba, uint8_t count)
210 com32sys_t inreg;
211 struct ebios_dapa *dapa = __com32.cs_bounce;
212 void *buf = (char *)__com32.cs_bounce + SECTOR;
213 void *data;
215 if (!count)
216 /* Silly */
217 return NULL;
219 memset(&inreg, 0, sizeof inreg);
221 if (disk_info.ebios) {
222 dapa->len = sizeof(*dapa);
223 dapa->count = count;
224 dapa->off = OFFS(buf);
225 dapa->seg = SEG(buf);
226 dapa->lba = lba;
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 */
232 } else {
233 unsigned int c, h, s, t;
235 if (!disk_info.cbios) {
236 /* We failed to get the geometry */
238 if (lba)
239 return NULL; /* Can only read MBR */
241 s = 1;
242 h = 0;
243 c = 0;
244 } else {
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)
252 return NULL;
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);
258 inreg.edx.b[1] = h;
259 inreg.edx.b[0] = disk_info.disk;
260 inreg.ebx.w[0] = OFFS(buf);
261 inreg.es = SEG(buf);
264 if (int13_retry(&inreg, NULL))
265 return NULL;
267 data = malloc(SECTOR);
268 if (data)
269 memcpy(data, buf, count * SECTOR);
270 return data;
273 static int write_sector(unsigned int lba, const void *data)
275 com32sys_t inreg;
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);
287 dapa->lba = lba;
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 */
293 } else {
294 unsigned int c, h, s, t;
296 if (!disk_info.cbios) {
297 /* We failed to get the geometry */
299 if (lba)
300 return -1; /* Can only write MBR */
302 s = 1;
303 h = 0;
304 c = 0;
305 } else {
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)
313 return -1;
315 inreg.eax.w[0] = 0x0301; /* Write one sector */
316 inreg.ecx.b[1] = c & 0xff;
317 inreg.ecx.b[0] = s + (c >> 6);
318 inreg.edx.b[1] = h;
319 inreg.edx.b[0] = disk_info.disk;
320 inreg.ebx.w[0] = OFFS(buf);
321 inreg.es = SEG(buf);
324 if (int13_retry(&inreg, NULL))
325 return -1;
327 return 0; /* ok */
330 static int write_verify_sector(unsigned int lba, const void *buf)
332 char *rb;
333 int rv;
335 rv = write_sector(lba, buf);
336 if (rv)
337 return rv; /* Write failure */
338 rb = read_sectors(lba, 1);
339 if (!rb)
340 return -1; /* Readback failure */
341 rv = memcmp(buf, rb, SECTOR);
342 free(rb);
343 return rv ? -1 : 0;
346 /* Search for a specific drive, based on the MBR signature; bytes
347 440-443. */
348 static int find_disk(uint32_t mbr_sig)
350 int drive;
351 bool is_me;
352 char *buf;
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);
360 free(buf);
361 if (is_me)
362 return drive;
364 return -1;
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 */
379 struct part_entry {
380 uint8_t active_flag; /* 0x80 if "active" */
381 chs start;
382 uint8_t ostype;
383 chs end;
384 uint32_t start_lba;
385 uint32_t length;
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;
406 char *sector;
408 int i;
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. */
416 if (self) {
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 */
424 if (!ptab[i].length)
425 continue;
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)
437 continue;
439 /* OK, it's a data partition. Is it the one we're looking for? */
440 if (nextpart++ == whichpart) {
441 memcpy(&ltab_entry, &ptab[i], sizeof ltab_entry);
442 return &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 */
453 if (!ptab[i].length)
454 continue;
456 /* Adjust the offset to account for the extended partition itself */
457 if (root)
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. */
462 if (root)
463 if (ptab[i].start_lba + ptab[i].length <= root->start_lba ||
464 ptab[i].start_lba >= root->start_lba + root->length)
465 continue;
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]);
473 free(sector);
474 if (found)
475 return found;
478 /* If we get here, there ain't nothing... */
479 return NULL;
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;
489 addr_t endimage;
490 uint8_t driveno = regs->edx.b[0];
491 uint8_t swapdrive = driveno & 0x80;
492 int i;
494 mmap = syslinux_memory_map();
496 if (!mmap) {
497 error("Cannot read system memory map\n");
498 return;
501 endimage = 0;
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)
507 goto too_big;
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))
512 goto enomem;
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];
550 uint8_t *p;
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++)
572 p[i] = 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,
586 sizeof swapstub))
587 goto enomem;
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");
601 return;
603 too_big:
604 error("Loader file too large\n");
605 return;
607 enomem:
608 error("Out of memory\n");
609 return;
612 static int hide_unhide(char *mbr, int part)
614 int i;
615 struct part_entry *pt;
616 const uint16_t mask =
617 (1 << 0x01) | (1 << 0x04) | (1 << 0x06) | (1 << 0x07) | (1 << 0x0b) | (1
619 0x0c)
620 | (1 << 0x0e);
621 uint8_t t;
622 bool write_back = false;
624 for (i = 1; i <= 4; i++) {
625 pt = (struct part_entry *)&mbr[0x1be + 16 * (i - 1)];
626 t = pt->ostype;
627 if ((t <= 0x1f) && ((mask >> (t & ~0x10)) & 1)) {
628 /* It's a hideable partition type */
629 if (i == part)
630 t &= ~0x10; /* unhide */
631 else
632 t |= 0x10; /* hide */
634 if (t != pt->ostype) {
635 write_back = true;
636 pt->ostype = t;
640 if (write_back)
641 return write_verify_sector(0, mbr);
643 return 0; /* ok */
646 static uint32_t get_file_lba(const char *filename)
648 com32sys_t inregs;
649 uint32_t lba;
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);
684 return lba;
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[])
709 char *mbr, *p;
710 struct part_entry *partinfo;
711 struct syslinux_rm_regs regs;
712 char *drivename, *partition;
713 int hd, drive, whichpart;
714 int i;
715 uint32_t file_lba = 0;
716 unsigned char *isolinux_bin;
717 uint32_t *checksum, *chkhead, *chktail;
718 struct data_area data[3];
719 int ndata = 0;
720 addr_t load_base;
721 static const char cmldr_signature[8] = "cmdcons";
723 openconsole(&dev_null_r, &dev_stdcon_w);
725 drivename = "boot";
726 partition = NULL;
728 /* Prepare the register set */
729 memset(&regs, 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");
738 goto bail;
740 opt.seg = segval;
741 } else if (!strncmp(argv[i], "isolinux=", 9)) {
742 opt.loadfile = argv[i] + 9;
743 opt.isolinux = true;
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;
751 opt.cmldr = true;
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;
765 opt.grub = true;
766 } else if (!strcmp(argv[i], "swap")) {
767 opt.swap = true;
768 } else if (!strcmp(argv[i], "noswap")) {
769 opt.swap = false;
770 } else if (!strcmp(argv[i], "hide")) {
771 opt.hide = true;
772 } else if (!strcmp(argv[i], "nohide")) {
773 opt.hide = false;
774 } else if (!strcmp(argv[i], "keeppxe")) {
775 opt.keeppxe = 3;
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)) {
786 drivename = argv[i];
787 p = strchr(drivename, ',');
788 if (p) {
789 *p = '\0';
790 partition = p + 1;
791 } else if (argv[i + 1] && argv[i + 1][0] >= '0'
792 && argv[i + 1][0] <= '9') {
793 partition = argv[++i];
795 } else {
796 usage();
797 goto bail;
801 if (opt.seg) {
802 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
803 } else {
804 regs.ip = regs.esp.l = 0x7c00;
807 hd = 0;
808 if (!strncmp(drivename, "mbr", 3)) {
809 drive = find_disk(strtoul(drivename + 4, NULL, 0));
810 if (drive == -1) {
811 error("Unable to find requested MBR signature\n");
812 goto bail;
814 } else if ((drivename[0] == 'h' || drivename[0] == 'f') &&
815 drivename[1] == 'd') {
816 hd = drivename[0] == 'h';
817 drivename += 2;
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 */
824 else
825 drive = sdi->disk.drive_number;
826 } else {
827 error("Unparsable drive specification\n");
828 goto bail;
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 */
835 if (partition)
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");
853 goto bail;
856 /* Get MBR */
857 if (!(mbr = read_sectors(0, 1))) {
858 error("Cannot read Master Boot Record\n");
859 goto bail;
862 if (opt.hide) {
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) {
870 /* Boot the MBR */
872 partinfo = NULL;
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");
879 goto bail;
881 } else {
882 /* Boot a logical partition */
884 nextpart = 5;
885 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
887 if (!partinfo || partinfo->ostype == 0) {
888 error("Requested logical partition not found\n");
889 goto bail;
893 /* Do the actual chainloading */
894 load_base = opt.seg ? (opt.seg << 4) : 0x7c00;
896 if (opt.loadfile) {
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");
900 goto bail;
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) */
909 if (opt.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);
935 if (file_lba == 0) {
936 error("Failed to find LBA offset of the boot file\n");
937 goto bail;
939 /* Set it */
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];
949 *checksum = 0;
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) {
958 uint32_t xword = 0;
959 memcpy(&xword, chkhead, data[ndata].size & 3);
960 *checksum += xword;
962 } else {
963 error
964 ("The isolinux= option is only valid when run from ISOLINUX\n");
965 goto bail;
969 if (opt.grub) {
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) =
975 0xffffff00ul;
978 ndata++;
981 if (!opt.loadfile || data[0].base >= 0x7c00 + SECTOR) {
982 /* Actually read the boot sector */
983 if (!partinfo) {
984 data[ndata].data = mbr;
985 } else if (!(data[ndata].data = read_sectors(partinfo->start_lba, 1))) {
986 error("Cannot read boot sector\n");
987 goto bail;
989 data[ndata].size = SECTOR;
990 data[ndata].base = load_base;
992 if (!opt.loadfile &&
993 *(uint16_t *) ((char *)data[ndata].data +
994 data[ndata].size - 2) != 0xaa55) {
995 error
996 ("Boot sector signature not found (unbootable disk/partition?)\n");
997 goto bail;
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;
1019 ndata++;
1022 if (partinfo) {
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;
1027 ndata++;
1028 regs.esi.w[0] = 0x7be;
1031 do_boot(data, ndata, &regs);
1033 bail:
1034 return 255;