From b435dd053955d42dd2ee31828061406e041935e7 Mon Sep 17 00:00:00 2001 From: hpa Date: Fri, 3 Dec 2004 23:15:46 +0000 Subject: [PATCH] libcom32-ize the chain loader; add LICENCE file --- com32/LICENCE | 31 +++++ com32/modules/Makefile | 5 +- com32/modules/chain.c | 359 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 com32/LICENCE create mode 100644 com32/modules/chain.c diff --git a/com32/LICENCE b/com32/LICENCE new file mode 100644 index 0000000..feb8a12 --- /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 7f24159..971dc95 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 0000000..9b7f15b --- /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 [] + * chain fd + * + * ... 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 +#include +#include +#include +#include + +#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; +} + + + -- 2.7.4