chain: use shuffle API; support loading a boot file
[syslinux.git] / com32 / modules / chain.c
blob3a614e1b6cafa845658378729c516a5982f7353c
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2003-2008 H. Peter Anvin - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
14 * chain.c
16 * Chainload a hard disk (currently rather braindead.)
18 * Usage: chain hd<disk#> [<partition>] [-ntldr] [-file <loader>]
19 * chain fd<disk#> [-ntldr] [-file <loader>]
20 * chain mbr:<id> [<partition>] [-ntldr] [-file <loader>]
22 * ... e.g. "chain hd0 1" will boot the first partition on the first hard
23 * disk.
25 * The mbr: syntax means search all the hard disks until one with a
26 * specific MBR serial number (bytes 440-443) is found.
28 * Partitions 1-4 are primary, 5+ logical, 0 = boot MBR (default.)
30 * -file <loader> loads the file <loader> **from the SYSLINUX filesystem**
31 * instead of loading the boot sector.
33 * -ntldr jumps to 07C0:0000 instead of 0000:7C00, not sure if this is
34 * really necessary.
38 #include <com32.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <ctype.h>
42 #include <string.h>
43 #include <console.h>
44 #include <stdbool.h>
45 #include <syslinux/loadfile.h>
46 #include <syslinux/bootrm.h>
48 #define SECTOR 512 /* bytes/sector */
50 static inline void error(const char *msg)
52 fputs(msg, stderr);
56 * Call int 13h, but with retry on failure. Especially floppies need this.
58 static int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
60 int retry = 6; /* Number of retries */
61 com32sys_t tmpregs;
63 if ( !outreg ) outreg = &tmpregs;
65 while ( retry-- ) {
66 __intcall(0x13, inreg, outreg);
67 if ( !(outreg->eflags.l & EFLAGS_CF) )
68 return 0; /* CF=0, OK */
71 return -1; /* Error */
75 * Query disk parameters and EBIOS availability for a particular disk.
77 struct diskinfo {
78 int disk;
79 int ebios; /* EBIOS supported on this disk */
80 int cbios; /* CHS geometry is valid */
81 int head;
82 int sect;
83 } disk_info;
85 static int get_disk_params(int disk)
87 static com32sys_t getparm, parm, getebios, ebios;
89 disk_info.disk = disk;
90 disk_info.ebios = disk_info.cbios = 0;
92 /* Get EBIOS support */
93 getebios.eax.w[0] = 0x4100;
94 getebios.ebx.w[0] = 0x55aa;
95 getebios.edx.b[0] = disk;
96 getebios.eflags.b[0] = 0x3; /* CF set */
98 __intcall(0x13, &getebios, &ebios);
100 if ( !(ebios.eflags.l & EFLAGS_CF) &&
101 ebios.ebx.w[0] == 0xaa55 &&
102 (ebios.ecx.b[0] & 1) ) {
103 disk_info.ebios = 1;
106 /* Get disk parameters -- really only useful for
107 hard disks, but if we have a partitioned floppy
108 it's actually our best chance... */
109 getparm.eax.b[1] = 0x08;
110 getparm.edx.b[0] = disk;
112 __intcall(0x13, &getparm, &parm);
114 if ( parm.eflags.l & EFLAGS_CF )
115 return disk_info.ebios ? 0 : -1;
117 disk_info.head = parm.edx.b[1]+1;
118 disk_info.sect = parm.ecx.b[0] & 0x3f;
119 if ( disk_info.sect == 0 ) {
120 disk_info.sect = 1;
121 } else {
122 disk_info.cbios = 1; /* Valid geometry */
125 return 0;
129 * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
131 struct ebios_dapa {
132 uint16_t len;
133 uint16_t count;
134 uint16_t off;
135 uint16_t seg;
136 uint64_t lba;
137 } *dapa;
139 static int read_sector(void *buf, unsigned int lba)
141 com32sys_t inreg;
143 memset(&inreg, 0, sizeof inreg);
145 if ( disk_info.ebios ) {
146 dapa->len = sizeof(*dapa);
147 dapa->count = 1; /* 1 sector */
148 dapa->off = OFFS(buf);
149 dapa->seg = SEG(buf);
150 dapa->lba = lba;
152 inreg.esi.w[0] = OFFS(dapa);
153 inreg.ds = SEG(dapa);
154 inreg.edx.b[0] = disk_info.disk;
155 inreg.eax.b[1] = 0x42; /* Extended read */
156 } else {
157 unsigned int c, h, s, t;
159 if ( !disk_info.cbios ) {
160 /* We failed to get the geometry */
162 if ( lba )
163 return -1; /* Can only read MBR */
165 s = 1; h = 0; c = 0;
166 } else {
167 s = (lba % disk_info.sect) + 1;
168 t = lba / disk_info.sect; /* Track = head*cyl */
169 h = t % disk_info.head;
170 c = t / disk_info.head;
173 if ( s > 63 || h > 256 || c > 1023 )
174 return -1;
176 inreg.eax.w[0] = 0x0201; /* Read one sector */
177 inreg.ecx.b[1] = c & 0xff;
178 inreg.ecx.b[0] = s + (c >> 6);
179 inreg.edx.b[1] = h;
180 inreg.edx.b[0] = disk_info.disk;
181 inreg.ebx.w[0] = OFFS(buf);
182 inreg.es = SEG(buf);
185 return int13_retry(&inreg, NULL);
188 /* Search for a specific drive, based on the MBR signature; bytes
189 440-443. */
190 static int find_disk(uint32_t mbr_sig, void *buf)
192 int drive;
194 for (drive = 0x80; drive <= 0xff; drive++) {
195 if (get_disk_params(drive))
196 continue; /* Drive doesn't exist */
197 if (read_sector(buf, 0))
198 continue; /* Cannot read sector */
200 if (*(uint32_t *)((char *)buf + 440) == mbr_sig)
201 return drive;
204 return -1;
207 /* A DOS partition table entry */
208 struct part_entry {
209 uint8_t active_flag; /* 0x80 if "active" */
210 uint8_t start_head;
211 uint8_t start_sect;
212 uint8_t start_cyl;
213 uint8_t ostype;
214 uint8_t end_head;
215 uint8_t end_sect;
216 uint8_t end_cyl;
217 uint32_t start_lba;
218 uint32_t length;
219 } __attribute__((packed));
222 /* Search for a logical partition. Logical partitions are actually implemented
223 as recursive partition tables; theoretically they're supposed to form a
224 linked list, but other structures have been seen.
226 To make things extra confusing: data partition offsets are relative to where
227 the data partition record is stored, whereas extended partition offsets
228 are relative to the beginning of the extended partition all the way back
229 at the MBR... but still not absolute! */
231 int nextpart; /* Number of the next logical partition */
233 static struct part_entry *
234 find_logical_partition(int whichpart, char *table, struct part_entry *self,
235 struct part_entry *root)
237 struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
238 struct part_entry *found;
239 int i;
241 if ( *(uint16_t *)(table + 0x1fe) != 0xaa55 )
242 return NULL; /* Signature missing */
244 /* We are assumed to already having enumerated all the data partitions
245 in this table if this is the MBR. For MBR, self == NULL. */
247 if ( self ) {
248 /* Scan the data partitions. */
250 for ( i = 0 ; i < 4 ; i++ ) {
251 if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
252 ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
253 continue; /* Skip empty or extended partitions */
255 if ( !ptab[i].length )
256 continue;
258 /* Adjust the offset to account for the extended partition itself */
259 ptab[i].start_lba += self->start_lba;
261 /* Sanity check entry: must not extend outside the extended partition.
262 This is necessary since some OSes put crap in some entries. */
263 if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
264 ptab[i].start_lba >= self->start_lba + self->length )
265 continue;
267 /* OK, it's a data partition. Is it the one we're looking for? */
268 if ( nextpart++ == whichpart )
269 return &ptab[i];
273 /* Scan the extended partitions. */
274 for ( i = 0 ; i < 4 ; i++ ) {
275 if ( ptab[i].ostype != 0x05 &&
276 ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
277 continue; /* Skip empty or data partitions */
279 if ( !ptab[i].length )
280 continue;
282 /* Adjust the offset to account for the extended partition itself */
283 if ( root )
284 ptab[i].start_lba += root->start_lba;
286 /* Sanity check entry: must not extend outside the extended partition.
287 This is necessary since some OSes put crap in some entries. */
288 if ( root )
289 if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
290 ptab[i].start_lba >= root->start_lba + root->length )
291 continue;
293 /* Process this partition */
294 if ( read_sector(table+SECTOR, ptab[i].start_lba) )
295 continue; /* Read error, must be invalid */
297 if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
298 root ? root : &ptab[i])) )
299 return found;
302 /* If we get here, there ain't nothing... */
303 return NULL;
306 static void do_boot(uint16_t keeppxe, void *boot_sector,
307 size_t boot_size, struct syslinux_rm_regs *regs)
309 struct syslinux_memmap *mmap;
310 struct syslinux_movelist *mlist = NULL;
312 mmap = syslinux_memory_map();
314 if (!mmap) {
315 error("Cannot read system memory map");
316 return;
319 if (syslinux_memmap_type(mmap, 0x7c00, boot_size) != SMT_FREE) {
320 error("Loader file too large");
321 return;
324 if (syslinux_add_movelist(&mlist, 0x7c00, (addr_t)boot_sector, boot_size)) {
325 error("Out of memory");
326 return;
329 fputs("Booting...\n", stdout);
331 syslinux_shuffle_boot_rm(mlist, mmap, keeppxe, regs);
333 /* If we get here, badness happened */
334 error("Chainboot failed!\n");
337 int main(int argc, char *argv[])
339 char *mbr;
340 void *boot_sector = NULL;
341 struct part_entry *partinfo;
342 struct syslinux_rm_regs regs;
343 char *drivename, *partition;
344 int hd, drive, whichpart;
345 int i;
346 uint16_t keeppxe = 0;
347 bool ntldr = false;
348 const char *load_file;
349 size_t boot_size = SECTOR;
351 openconsole(&dev_null_r, &dev_stdcon_w);
353 drivename = NULL;
354 partition = NULL;
355 load_file = NULL;
357 /* Prepare the register set */
358 memset(&regs, 0, sizeof regs);
360 for (i = 1; i < argc; i++) {
361 if (!strcmp(argv[i], "-file") && argv[i+1]) {
362 load_file = argv[++i];
363 } else if (!strcmp(argv[i], "-ntldr")) {
364 ntldr = true;
365 } else if (!strcmp(argv[i], "keeppxe")) {
366 keeppxe = 3;
367 } else {
368 if (!drivename)
369 drivename = argv[i];
370 else if (!partition)
371 partition = argv[i];
375 if ( !drivename ) {
376 error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition] [-ntldr] "
377 "[-file loader]\n");
378 goto bail;
381 if (ntldr) {
382 regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = 0x07c0;
383 } else {
384 regs.ip = regs.esp.l = 0x7c00;
387 /* Divvy up the bounce buffer. To keep things sector-
388 aligned, give the EBIOS DAPA the first sector, then
389 the MBR next, and the rest is used for the partition-
390 chasing stack. */
391 dapa = (struct ebios_dapa *)__com32.cs_bounce;
392 mbr = (char *)__com32.cs_bounce + SECTOR;
394 drivename = argv[1];
395 partition = argv[2]; /* Possibly null */
397 hd = 0;
398 if ( !memcmp(drivename, "mbr:", 4) ) {
399 drive = find_disk(strtoul(drivename+4, NULL, 0), mbr);
400 if (drive == -1) {
401 error("Unable to find requested MBR signature\n");
402 goto bail;
404 } else {
405 if ( (drivename[0] == 'h' || drivename[0] == 'f') &&
406 drivename[1] == 'd' ) {
407 hd = drivename[0] == 'h';
408 drivename += 2;
410 drive = (hd ? 0x80 : 0) | strtoul(drivename, NULL, 0);
413 regs.edx.b[0] = drive;
415 whichpart = 0; /* Default */
417 if ( partition )
418 whichpart = strtoul(partition, NULL, 0);
420 if ( !(drive & 0x80) && whichpart ) {
421 error("Warning: Partitions of floppy devices may not work\n");
424 /* Get the disk geometry and disk access setup */
425 if ( get_disk_params(drive) ) {
426 error("Cannot get disk parameters\n");
427 goto bail;
430 /* Get MBR */
431 if ( read_sector(mbr, 0) ) {
432 error("Cannot read Master Boot Record\n");
433 goto bail;
436 if ( whichpart == 0 ) {
437 /* Boot the MBR */
438 partinfo = NULL;
439 boot_sector = mbr;
440 } else if ( whichpart <= 4 ) {
441 /* Boot a primary partition */
442 partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
443 if ( partinfo->ostype == 0 ) {
444 error("Invalid primary partition\n");
445 goto bail;
447 } else {
448 /* Boot a logical partition */
450 nextpart = 5;
451 partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
453 if ( !partinfo || partinfo->ostype == 0 ) {
454 error("Requested logical partition not found\n");
455 goto bail;
459 /* Do the actual chainloading */
460 if (load_file) {
461 if ( loadfile(load_file, &boot_sector, &boot_size) ) {
462 error("Failed to load the boot file\n");
463 goto bail;
465 } else if (partinfo) {
466 /* Actually read the boot sector */
467 /* Pick the first buffer that isn't already in use */
468 boot_sector = (void *)(((uintptr_t)partinfo + 511) & ~511);
469 if ( read_sector(boot_sector, partinfo->start_lba) ) {
470 error("Cannot read boot sector\n");
471 goto bail;
475 if (partinfo) {
476 /* 0x7BE is the canonical place for the first partition entry. */
477 regs.esi.w[0] = 0x7be;
478 memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
481 do_boot(keeppxe, boot_sector, boot_size, &regs);
483 bail:
484 return 255;