summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2008-06-15 17:05:37 -0700
committerH. Peter Anvin <hpa@zytor.com>2008-06-15 17:05:37 -0700
commitaa7cc7229e6e715e81abb47994163af6479595ae (patch)
tree3ddf99b538050ab2a1598fc564157d59db6f65b2
parent99d175aa34800b281c65210ce436cf0a423906fc (diff)
downloadsyslinux-aa7cc7229e6e715e81abb47994163af6479595ae.tar.gz
chain.c32: finalize the NTLDR interfacesyslinux-3.70-pre18
Settle on a final implementation of the NTLDR support.
-rw-r--r--NEWS7
-rw-r--r--com32/modules/chain.c125
2 files changed, 85 insertions, 47 deletions
diff --git a/NEWS b/NEWS
index e48e6c72..a70bdaaa 100644
--- a/NEWS
+++ b/NEWS
@@ -36,6 +36,13 @@ Changes in 3.70:
* chain.c32: option -file to support loading a boot file from
the SYSLINUX filesystem instead of loading the boot sector
from the drive.
+ * chain.c32: option -seg to control the load location.
+ * chain.c32: option -ntldr as a shorthand for "-seg 0x2000
+ -file"; use this to load one of WinNT's loaders:
+
+ chain.c32 hd0 1 -ntldr /MiniNT/setupldr.bin
+
+ Note that the file needs to be in the SYSLINUX filesystem.
Changes in 3.64:
* SYSLINUX/EXTLINUX: support "localboot" with the same feature
diff --git a/com32/modules/chain.c b/com32/modules/chain.c
index a9ebf767..10cfb030 100644
--- a/com32/modules/chain.c
+++ b/com32/modules/chain.c
@@ -34,9 +34,11 @@
* loads the file <loader> **from the SYSLINUX filesystem**
* instead of loading the boot sector.
*
- * -ntldr:
- * jumps to 07C0:0000 instead of 0000:7C00, not sure if this is
- * really necessary.
+ * -seg <segment>:
+ * loads at and jumps to <seg>:0000 instead of 0000:7C00.
+ *
+ * -ntldr <loader>:
+ * equivalent to -seg 0x2000 -file <loader>, used with WinNT's loaders
*
* -swap:
* if the disk is not fd0/hd0, install a BIOS stub which swaps
@@ -58,7 +60,7 @@
static struct options {
const char *loadfile;
uint16_t keeppxe;
- bool ntldr;
+ uint16_t seg;
bool swap;
} opt;
@@ -141,7 +143,8 @@ static int get_disk_params(int disk)
}
/*
- * Get a disk block; buf is REQUIRED TO BE IN LOW MEMORY.
+ * Get a disk block and return a malloc'd buffer.
+ * Uses the disk number and information from disk_info.
*/
struct ebios_dapa {
uint16_t len;
@@ -151,9 +154,11 @@ struct ebios_dapa {
uint64_t lba;
} *dapa;
-static int read_sector(void *buf, unsigned int lba)
+static void *read_sector(unsigned int lba)
{
com32sys_t inreg;
+ void *buf = __com32.cs_bounce;
+ void *data;
memset(&inreg, 0, sizeof inreg);
@@ -175,7 +180,7 @@ static int read_sector(void *buf, unsigned int lba)
/* We failed to get the geometry */
if ( lba )
- return -1; /* Can only read MBR */
+ return NULL; /* Can only read MBR */
s = 1; h = 0; c = 0;
} else {
@@ -186,7 +191,7 @@ static int read_sector(void *buf, unsigned int lba)
}
if ( s > 63 || h > 256 || c > 1023 )
- return -1;
+ return NULL;
inreg.eax.w[0] = 0x0201; /* Read one sector */
inreg.ecx.b[1] = c & 0xff;
@@ -197,7 +202,13 @@ static int read_sector(void *buf, unsigned int lba)
inreg.es = SEG(buf);
}
- return int13_retry(&inreg, NULL);
+ if (int13_retry(&inreg, NULL))
+ return NULL;
+
+ data = malloc(SECTOR);
+ if (data)
+ memcpy(data, buf, SECTOR);
+ return data;
}
/* Search for a specific drive, based on the MBR signature; bytes
@@ -205,17 +216,18 @@ static int read_sector(void *buf, unsigned int lba)
static int find_disk(uint32_t mbr_sig, void *buf)
{
int drive;
+ bool is_me;
for (drive = 0x80; drive <= 0xff; drive++) {
if (get_disk_params(drive))
continue; /* Drive doesn't exist */
- if (read_sector(buf, 0))
+ if (!(buf = read_sector(0)))
continue; /* Cannot read sector */
-
- if (*(uint32_t *)((char *)buf + 440) == mbr_sig)
+ is_me = (*(uint32_t *)((char *)buf + 440) == mbr_sig);
+ free(buf);
+ if (is_me)
return drive;
}
-
return -1;
}
@@ -249,8 +261,11 @@ static struct part_entry *
find_logical_partition(int whichpart, char *table, struct part_entry *self,
struct part_entry *root)
{
+ static struct part_entry ltab_entry;
struct part_entry *ptab = (struct part_entry *)(table + 0x1be);
struct part_entry *found;
+ char *sector;
+
int i;
if ( *(uint16_t *)(table + 0x1fe) != 0xaa55 )
@@ -280,8 +295,10 @@ find_logical_partition(int whichpart, char *table, struct part_entry *self,
continue;
/* OK, it's a data partition. Is it the one we're looking for? */
- if ( nextpart++ == whichpart )
- return &ptab[i];
+ if ( nextpart++ == whichpart ) {
+ memcpy(&ltab_entry, &ptab[i], sizeof ltab_entry);
+ return &ltab_entry;
+ }
}
}
@@ -306,11 +323,13 @@ find_logical_partition(int whichpart, char *table, struct part_entry *self,
continue;
/* Process this partition */
- if ( read_sector(table+SECTOR, ptab[i].start_lba) )
+ if ( !(sector = read_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])) )
+ found = find_logical_partition(whichpart, sector, &ptab[i],
+ root ? root : &ptab[i]);
+ free(sector);
+ if (found)
return found;
}
@@ -321,6 +340,14 @@ find_logical_partition(int whichpart, char *table, struct part_entry *self,
static void do_boot(void *boot_sector, size_t boot_size,
struct syslinux_rm_regs *regs)
{
+ static const uint8_t swapstub[] = {
+ 0x53, /* 00: push bx */
+ 0x0f,0xb6,0xda, /* 01: movzx bx,dl */
+ 0x2e,0x8a,0x57,0x10, /* 04: mov dl,[cs:bx+16] */
+ 0x5b, /* 08: pop bx */
+ 0xea,0,0,0,0, /* 09: jmp far 0:0 */
+ 0x90,0x90, /* 0E: nop; nop */
+ };
uint16_t * const bios_fbm = (uint16_t *)0x413;
uint32_t * const int13_vec = (uint32_t *)(0x13*4);
uint16_t old_bios_fbm = *bios_fbm;
@@ -330,6 +357,8 @@ static void do_boot(void *boot_sector, size_t boot_size,
addr_t dosmem = old_bios_fbm << 10;
uint8_t driveno = regs->edx.b[0];
uint8_t swapdrive = driveno & 0x80;
+ int i;
+ addr_t loadbase = opt.seg ? (opt.seg << 4) : 0x7c00;
mmap = syslinux_memory_map();
@@ -347,34 +376,29 @@ static void do_boot(void *boot_sector, size_t boot_size,
p = (uint8_t *)dosmem;
/* Install swapper stub */
- *p++ = 0x80; /* cmp dl,swapdrive */
- *p++ = 0xfa;
- *p++ = swapdrive;
- *p++ = 0x74; /* je swap1 */
- *p++ = 0x09;
- *p++ = 0x80; /* cmp dl,driveno */
- *p++ = 0xfa;
- *p++ = driveno;
- *p++ = 0x75; /* je noswap */
- *p++ = 0x06;
- *p++ = 0xb2; /* mov dl,swapdrive */
- *p++ = swapdrive;
- *p++ = 0xeb; /* jmp noswap */
- *p++ = 0x02;
- *p++ = 0xb2; /* swap1: mov dl,driveno */
- *p++ = driveno;
- *p++ = 0xea; /* noswap: jmp far <oldint13> */
- *(uint32_t *)p = old_int13_vec;
+ memset(p, 0, 1024); /* For debugging... */
+ memcpy(p, swapstub, sizeof swapstub);
+ *(uint32_t *)&p[0x0a] = old_int13_vec;
+ p += sizeof swapstub;
+
+ /* Mapping table; start out with identity mapping everything */
+ for (i = 0; i < 256; i++)
+ p[i] = i;
+
+ /* And the actual swap */
+ p[driveno] = swapdrive;
+ p[swapdrive] = driveno;
}
syslinux_add_memmap(&mmap, dosmem, 0xa0000-dosmem, SMT_RESERVED);
- if (syslinux_memmap_type(mmap, 0x7c00, boot_size) != SMT_FREE) {
+ if (syslinux_memmap_type(mmap, loadbase, boot_size) != SMT_FREE) {
error("Loader file too large");
return;
}
- if (syslinux_add_movelist(&mlist, 0x7c00, (addr_t)boot_sector, boot_size)) {
+ if (syslinux_add_movelist(&mlist, loadbase, (addr_t)boot_sector,
+ boot_size)) {
error("Out of memory");
return;
}
@@ -419,8 +443,16 @@ int main(int argc, char *argv[])
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-file") && argv[i+1]) {
opt.loadfile = argv[++i];
- } else if (!strcmp(argv[i], "-ntldr")) {
- opt.ntldr = true;
+ } else if (!strcmp(argv[i], "-seg") && argv[i+1]) {
+ uint32_t segval = strtoul(argv[++i], NULL, 0);
+ if (segval < 0x7c0 || segval > 0x9f000) {
+ error("Invalid segment");
+ goto bail;
+ }
+ opt.seg = segval;
+ } else if (!strcmp(argv[i], "-ntldr") && argv[i+1]) {
+ opt.seg = 0x2000; /* NTLDR wants this address */
+ opt.loadfile = argv[++i];
} else if (!strcmp(argv[i], "-swap")) {
opt.swap = true;
} else if (!strcmp(argv[i], "keeppxe")) {
@@ -434,13 +466,12 @@ int main(int argc, char *argv[])
}
if ( !drivename ) {
- error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition] [-swap][-ntldr] "
- "[-file loader]\n");
+ error("Usage: chain.c32 (hd#|fd#|mbr:#) [partition] [options]\n");
goto bail;
}
- if (opt.ntldr) {
- regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = 0x07c0;
+ if (opt.seg) {
+ regs.es = regs.cs = regs.ss = regs.ds = regs.fs = regs.gs = opt.seg;
} else {
regs.ip = regs.esp.l = 0x7c00;
}
@@ -489,7 +520,7 @@ int main(int argc, char *argv[])
}
/* Get MBR */
- if ( read_sector(mbr, 0) ) {
+ if ( !(mbr = read_sector(0)) ) {
error("Cannot read Master Boot Record\n");
goto bail;
}
@@ -519,6 +550,7 @@ int main(int argc, char *argv[])
/* Do the actual chainloading */
if (opt.loadfile) {
+ fputs("Loading the boot file...\n", stdout);
if ( loadfile(opt.loadfile, &boot_sector, &boot_size) ) {
error("Failed to load the boot file\n");
goto bail;
@@ -526,8 +558,7 @@ int main(int argc, char *argv[])
} else if (partinfo) {
/* Actually read the boot sector */
/* Pick the first buffer that isn't already in use */
- boot_sector = (void *)(((uintptr_t)partinfo + 511) & ~511);
- if ( read_sector(boot_sector, partinfo->start_lba) ) {
+ if ( !(boot_sector = read_sector(partinfo->start_lba)) ) {
error("Cannot read boot sector\n");
goto bail;
}