summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhpa <hpa>2004-12-03 23:15:46 +0000
committerhpa <hpa>2004-12-03 23:15:46 +0000
commitb435dd053955d42dd2ee31828061406e041935e7 (patch)
tree045e57cdcdfbb1f48729fa257d4b8e96d9961cba
parent3437b6918aca1110881506863a5f476d5c0abc24 (diff)
downloadsyslinux-b435dd053955d42dd2ee31828061406e041935e7.tar.gz
libcom32-ize the chain loader; add LICENCE file
-rw-r--r--com32/LICENCE31
-rw-r--r--com32/modules/Makefile5
-rw-r--r--com32/modules/chain.c359
3 files changed, 394 insertions, 1 deletions
diff --git a/com32/LICENCE b/com32/LICENCE
new file mode 100644
index 00000000..feb8a12c
--- /dev/null
+++ b/com32/LICENCE
@@ -0,0 +1,31 @@
+libcom32 and libutil are licensed under the MIT license:
+
+## -----------------------------------------------------------------------
+##
+## Copyright 2004 H. Peter Anvin - All Rights Reserved
+##
+## Permission is hereby granted, free of charge, to any person
+## obtaining a copy of this software and associated documentation
+## files (the "Software"), to deal in the Software without
+## restriction, including without limitation the rights to use,
+## copy, modify, merge, publish, distribute, sublicense, and/or
+## sell copies of the Software, and to permit persons to whom
+## the Software is furnished to do so, subject to the following
+## conditions:
+##
+## The above copyright notice and this permission notice shall
+## be included in all copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+## OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+## HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+## FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+## OTHER DEALINGS IN THE SOFTWARE.
+##
+## -----------------------------------------------------------------------
+
+The files in the modules directory are mostly under the GNU GPL (see
+the file COPYING in the directory above.)
diff --git a/com32/modules/Makefile b/com32/modules/Makefile
index 7f24159d..971dc95c 100644
--- a/com32/modules/Makefile
+++ b/com32/modules/Makefile
@@ -39,7 +39,10 @@ LNXLIBS = ../libutil/libutil_lnx.a
.SUFFIXES: .lss .c .o .elf .c32 .lnx
-all: hello.c32 fancyhello.c32 fancyhello.lnx keytest.c32 keytest.lnx
+all: hello.c32 \
+ fancyhello.c32 fancyhello.lnx \
+ keytest.c32 keytest.lnx \
+ chain.c32
.PRECIOUS: %.o
%.o: %.S
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
new file mode 100644
index 00000000..9b7f15b0
--- /dev/null
+++ b/com32/modules/chain.c
@@ -0,0 +1,359 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2003-2004 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Bostom MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * chain.c
+ *
+ * Chainload a hard disk (currently rather braindead.)
+ *
+ * Usage: chain hd<disk#> [<partition>]
+ * chain fd<disk#>
+ *
+ * ... e.g. "chain hd0 1" will boot the first partition on the first hard disk.
+ *
+ * Only partitions 1-4 supported at this time; 0 = boot MBR (default.)
+ */
+
+#include <com32.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#define SECTOR 512 /* bytes/sector */
+
+static inline void error(const char *msg)
+{
+ puts(msg);
+}
+
+/*
+ * Call int 13h, but with retry on failure. Especially floppies need this.
+ */
+int int13_retry(const com32sys_t *inreg, com32sys_t *outreg)
+{
+ int retry = 6; /* Number of retries */
+ com32sys_t tmpregs;
+
+ if ( !outreg ) outreg = &tmpregs;
+
+ while ( retry ) {
+ __intcall(0x13, inreg, outreg);
+ if ( (outreg->eflags.l & 1) == 0 )
+ return 0; /* CF=0, OK */
+ }
+
+ return -1; /* Error */
+}
+
+/*
+ * Query disk parameters and EBIOS availability for a particular disk.
+ */
+struct diskinfo {
+ int disk;
+ int head;
+ int sect;
+ int ebios;
+} disk_info;
+
+int get_disk_params(int disk)
+{
+ com32sys_t getparm, parm, getebios, ebios;
+
+ memset(&getparm, 0, sizeof getparm);
+ memset(&getebios, 0, sizeof getebios);
+ memset(&disk_info, 0, sizeof disk_info);
+
+ disk_info.disk = disk;
+
+ if ( disk & 0x80 ) {
+ /* Get disk parameters -- hard drives only */
+
+ getparm.eax.b[1] = 0x08;
+ getparm.edx.b[0] = disk;
+ if ( int13_retry(&getparm, &parm) )
+ return -1;
+
+ disk_info.head = parm.edx.b[1]+1;
+ disk_info.sect = parm.ecx.b[0] & 0x3f;
+
+ /* Get EBIOS support */
+
+ getebios.eax.w[0] = 0x4100;
+ getebios.edx.b[0] = disk;
+ getebios.ebx.w[0] = 0x55aa;
+ getebios.eflags.b[0] = 0x3; /* CF set */
+ if ( !int13_retry(&getebios, &ebios) ) {
+ if ( ebios.ebx.w[0] == 0xaa55 &&
+ (ebios.ecx.b[0] & 1) )
+ disk_info.ebios = 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
+ */
+struct ebios_dapa {
+ uint16_t len;
+ uint16_t count;
+ uint16_t off;
+ uint16_t seg;
+ uint64_t lba;
+} *dapa;
+
+int read_sector(void *buf, unsigned int lba)
+{
+ com32sys_t inreg;
+
+ memset(&inreg, 0, sizeof inreg);
+
+ if ( disk_info.ebios ) {
+ dapa->len = sizeof(*dapa);
+ dapa->count = 1; /* 1 sector */
+ dapa->off = OFFS(buf);
+ dapa->seg = SEG(buf);
+ dapa->lba = lba;
+
+ inreg.esi.w[0] = OFFS(dapa);
+ inreg.ds = SEG(dapa);
+ inreg.edx.b[0] = disk_info.disk;
+ inreg.eax.b[1] = 0x42; /* Extended read */
+ } else {
+ unsigned int c, h, s, t;
+
+ s = (lba % disk_info.sect) + 1;
+ t = lba / disk_info.sect; /* Track = head*cyl */
+ h = t % disk_info.head;
+ c = t / disk_info.head;
+
+ if ( s > 63 || h > 256 || c > 1023 )
+ return -1;
+
+ inreg.eax.w[0] = 0x0201; /* Read one sector */
+ inreg.ecx.b[1] = c & 0xff;
+ inreg.ecx.b[0] = s + (c >> 6);
+ inreg.edx.b[1] = h;
+ inreg.edx.b[0] = disk_info.disk;
+ inreg.ebx.w[0] = OFFS(buf);
+ inreg.es = SEG(buf);
+ }
+
+ return int13_retry(&inreg, NULL);
+}
+
+/* A DOS partition table entry */
+struct part_entry {
+ uint8_t active_flag; /* 0x80 if "active" */
+ uint8_t start_head;
+ uint8_t start_sect;
+ uint8_t start_cyl;
+ uint8_t ostype;
+ uint8_t end_head;
+ uint8_t end_sect;
+ uint8_t end_cyl;
+ uint32_t start_lba;
+ uint32_t length;
+} __attribute__((packed));
+
+
+/* Search for a logical partition. Logical partitions are actually implemented
+ as recursive partition tables; theoretically they're supposed to form a linked
+ list, but other structures have been seen.
+
+ To make things extra confusing: data partition offsets are relative to where
+ the data partition record is stored, whereas extended partition offsets
+ are relative to the beginning of the extended partition all the way back
+ at the MBR... but still not absolute! */
+
+int nextpart; /* Number of the next logical partition */
+
+struct part_entry *find_logical_partition(int whichpart, char *table, struct part_entry *self, struct part_entry *root)
+{
+ struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
+ struct part_entry *found;
+ int i;
+
+ if ( *(uint16_t *)(ptab + 0x1fe) != 0xaa55 )
+ return NULL; /* Signature missing */
+
+ /* We are assumed to already having enumerated all the data partitions
+ in this table if this is the MBR. For MBR, self == NULL. */
+
+ if ( self ) {
+ /* Scan the data partitions. */
+
+ for ( i = 0 ; i < 4 ; i++ ) {
+ if ( ptab[i].ostype == 0x00 || ptab[i].ostype == 0x05 ||
+ ptab[i].ostype == 0x0f || ptab[i].ostype == 0x85 )
+ continue; /* Skip empty or extended partitions */
+
+ if ( !ptab[i].length )
+ continue;
+
+ /* Adjust the offset to account for the extended partition itself */
+ ptab[i].start_lba += self->start_lba;
+
+ /* Sanity check entry: must not extend outside the extended partition.
+ This is necessary since some OSes put crap in some entries. */
+ if ( ptab[i].start_lba + ptab[i].length <= self->start_lba ||
+ ptab[i].start_lba >= self->start_lba + self->length )
+ continue;
+
+ /* OK, it's a data partition. Is it the one we're looking for? */
+ if ( nextpart++ == whichpart )
+ return &ptab[i];
+ }
+ }
+
+ /* Scan the extended partitions. */
+ for ( i = 0 ; i < 4 ; i++ ) {
+ if ( ptab[i].ostype != 0x05 &&
+ ptab[i].ostype != 0x0f && ptab[i].ostype != 0x85 )
+ continue; /* Skip empty or data partitions */
+
+ if ( !ptab[i].length )
+ continue;
+
+ /* Adjust the offset to account for the extended partition itself */
+ if ( root )
+ ptab[i].start_lba += root->start_lba;
+
+ /* Sanity check entry: must not extend outside the extended partition.
+ This is necessary since some OSes put crap in some entries. */
+ if ( root )
+ if ( ptab[i].start_lba + ptab[i].length <= root->start_lba ||
+ ptab[i].start_lba >= root->start_lba + root->length )
+ continue;
+
+ /* Process this partition */
+ if ( read_sector(table+SECTOR, ptab[i].start_lba) )
+ continue; /* Read error, must be invalid */
+
+ if ( (found = find_logical_partition(whichpart, table+SECTOR, &ptab[i],
+ root ? root : &ptab[i])) )
+ return found;
+ }
+
+ /* If we get here, there ain't nothing... */
+ return NULL;
+}
+
+
+int main(void)
+{
+ char *mbr, *boot_sector = NULL;
+ struct part_entry *partinfo;
+ char *cmdline = __com32.cs_cmdline;
+ int hd, drive, whichpart;
+ static com32sys_t inreg; /* In bss, so zeroed automatically */
+
+ /* Parse command line */
+ while ( isspace(*cmdline) )
+ cmdline++;
+
+ hd = 0;
+ if ( (cmdline[0] == 'h' || cmdline[0] == 'f') &&
+ cmdline[1] == 'd' ) {
+ hd = cmdline[0] == 'h';
+ cmdline += 2;
+ }
+ drive = (hd ? 0x80 : 0) | strtoul(cmdline, &cmdline, 0);
+ whichpart = 0; /* Default */
+
+ if ( isspace(*cmdline) ) {
+ while ( isspace(*cmdline) )
+ cmdline++;
+
+ whichpart = strtoul(cmdline, NULL, 0);
+ }
+
+ if ( !(drive & 0x80) && whichpart != 0 ) {
+ error("Partitions not supported on floppy drives\n");
+ goto bail;
+ }
+
+ /* Divvy up the bounce buffer. To keep things sector-
+ aligned, give the EBIOS DAPA the first sector, then
+ the MBR next, and the rest is used for the partition-
+ chasing stack. */
+ dapa = (struct ebios_dapa *)__com32.cs_bounce;
+ mbr = (char *)__com32.cs_bounce + SECTOR;
+
+ /* Get the MBR */
+ if ( get_disk_params(drive) ) {
+ error("Cannot get disk parameters\n");
+ goto bail;
+ }
+
+ if ( read_sector(mbr, 0) ) {
+ error("Cannot read MBR\n");
+ goto bail;
+ }
+
+ if ( whichpart == 0 ) {
+ /* Boot the MBR */
+ partinfo = NULL;
+ boot_sector = mbr;
+ } else if ( whichpart <= 4 ) {
+ /* Boot a primary partition */
+ partinfo = &((struct part_entry *)(mbr + 0x1be))[whichpart-1];
+ if ( partinfo->ostype == 0 ) {
+ error("Invalid primary partition\n");
+ goto bail;
+ }
+ } else {
+ /* Boot a logical partition */
+
+ nextpart = 5;
+ partinfo = find_logical_partition(whichpart, mbr, NULL, NULL);
+
+ if ( !partinfo || partinfo->ostype == 0 ) {
+ error("Requested logical partition not found\n");
+ goto bail;
+ }
+ }
+
+ /* Do the actual chainloading */
+ if ( partinfo ) {
+ /* Actually read the boot sector */
+ /* Pick the first buffer that isn't already in use */
+ boot_sector = (char *)(((unsigned long)partinfo + 511) & ~511);
+ if ( read_sector(boot_sector, partinfo->start_lba) ) {
+ error("Cannot read boot sector\n");
+ goto bail;
+ }
+
+ /* 0x7BE is the canonical place for the first partition entry. */
+ inreg.esi.w[0] = 0x7be;
+ memcpy((char *)0x7be, partinfo, sizeof(*partinfo));
+ }
+ inreg.eax.w[0] = 0x000d; /* Clean up and chain boot */
+ inreg.edx.w[0] = 0; /* Should be 3 for "keeppxe" */
+ inreg.edi.l = (uint32_t)boot_sector;
+ inreg.ecx.l = SECTOR; /* One sector */
+ inreg.ebx.b[0] = drive; /* DL = drive no */
+
+ __intcall(0x22, &inreg, NULL);
+
+ /* If we get here, badness happened */
+ error("Chainboot failed!\n");
+
+bail:
+ return 255;
+}
+
+
+