aboutsummaryrefslogtreecommitdiffstats
path: root/com32/elflink/ldlinux
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@intel.com>2012-05-01 08:55:45 +0100
committerMatt Fleming <matt.fleming@intel.com>2012-06-07 14:19:20 +0100
commitc0d18deeee22f3466fd863d64b432660965ec66e (patch)
tree8fce613ef56efd6d112ede4e394051a99490a66f /com32/elflink/ldlinux
parent4fc3fd1e14f4c1b9208ef262e5b6aef853e9fce4 (diff)
downloadsyslinux-c0d18deeee22f3466fd863d64b432660965ec66e.tar.gz
syslinux-c0d18deeee22f3466fd863d64b432660965ec66e.tar.xz
syslinux-c0d18deeee22f3466fd863d64b432660965ec66e.zip
elflink: Fix boot sector booting
This adds missing support for booting from a boot sector file such as .bs, .bss or .0, by re-implementing the old asm bootsec code from core/bootsect.inc in C. This has resulted in some external changes. We've had to make StackBuf a global symbol because we access it directly from execute.c. Also, we need to move dsinfo.c into MINLIBOBJS because ldlinux now needs to reference __syslinux_derivative_info. Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'com32/elflink/ldlinux')
-rw-r--r--com32/elflink/ldlinux/Makefile2
-rw-r--r--com32/elflink/ldlinux/config.h2
-rw-r--r--com32/elflink/ldlinux/execute.c143
-rw-r--r--com32/elflink/ldlinux/loadhigh.c112
4 files changed, 258 insertions, 1 deletions
diff --git a/com32/elflink/ldlinux/Makefile b/com32/elflink/ldlinux/Makefile
index ca4c7e25..75c618f6 100644
--- a/com32/elflink/ldlinux/Makefile
+++ b/com32/elflink/ldlinux/Makefile
@@ -21,7 +21,7 @@ all: ldlinux.c32 ldlinux_lnx.a
ldlinux.c32 : ldlinux.o cli.o readconfig.o refstr.o colors.o getadv.o \
adv.o execute.o kernel.o get_key.o \
- advwrite.o setadv.o eprintf.o
+ advwrite.o setadv.o eprintf.o loadhigh.o
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
LNXLIBOBJS = get_key.lo
diff --git a/com32/elflink/ldlinux/config.h b/com32/elflink/ldlinux/config.h
index b15a0828..45832022 100644
--- a/com32/elflink/ldlinux/config.h
+++ b/com32/elflink/ldlinux/config.h
@@ -45,4 +45,6 @@ extern void eprintf(const char *filename, ...);
extern int new_linux_kernel(char *okernel, char *ocmdline);
+extern void pm_load_high(com32sys_t *regs);
+
#endif /* __CONFIG_H__ */
diff --git a/com32/elflink/ldlinux/execute.c b/com32/elflink/ldlinux/execute.c
index afe999e2..dd9854e1 100644
--- a/com32/elflink/ldlinux/execute.c
+++ b/com32/elflink/ldlinux/execute.c
@@ -17,10 +17,16 @@
#include <com32.h>
#include <sys/exec.h>
+#include <sys/io.h>
#include "core.h"
#include "menu.h"
#include "fs.h"
#include "config.h"
+#include "bios.h"
+
+#include <syslinux/bootrm.h>
+#include <syslinux/movebits.h>
+#include <syslinux/config.h>
/* Must match enum kernel_type */
const char *const kernel_types[] = {
@@ -46,6 +52,7 @@ void execute(const char *cmdline, enum kernel_type type)
const char *kernel, *args;
com32sys_t ireg;
char *q;
+ uint8_t keeppxe = 0;
memset(&ireg, 0, sizeof ireg);
@@ -103,12 +110,148 @@ void execute(const char *cmdline, enum kernel_type type)
ireg.eax.w[0] = 0x0014; /* Local boot */
ireg.edx.w[0] = strtoul(kernel, NULL, 0);
__intcall(0x22, &ireg, NULL);
+ } else if (type == KT_PXE || type == KT_BSS || type == KT_BOOT) {
+ const union syslinux_derivative_info *sdi;
+ struct syslinux_rm_regs regs;
+ struct syslinux_movelist *fraglist = NULL;
+ struct syslinux_memmap *mmap = NULL;
+ struct com32_filedata fd;
+ unsigned int free_mem, new_free_mem;
+ unsigned int edx, esi, bx;
+ com32sys_t reg;
+ char *stack;
+ void *buf;
+ int rv, max, size;
+
+ max = 0xA0000; /* Maximum load */
+ buf = malloc(max);
+ if (!buf)
+ goto bail;
+
+ rv = open_file(kernel, &fd);
+ if (rv == -1) {
+ free(buf);
+ goto bail;
+ }
+
+ reg.eax.l = max;
+ reg.ebx.l = 0;
+ reg.edx.w[0] = 0;
+ reg.edi.l = (uint32_t)buf;
+ reg.ebp.l = -1; /* XXX: limit? */
+ reg.esi.w[0] = rv;
+
+ pm_load_high(&reg);
+
+ size = reg.edi.l - (unsigned long)buf;
+ if (size > 0xA0000 - 0x7C00) {
+ printf("Too large for a boostrap (need LINUX instead of KERNEL?)\n");
+ goto boot_bail;
+ }
+
+ esi = 0;
+ bx = 0;
+
+ sdi = syslinux_derivative_info();
+ edx = sdi->rr.r.edx.b[0];
+
+ memset(&regs, 0, sizeof(regs));
+
+ if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX ||
+ sdi->c.filesystem == SYSLINUX_FS_EXTLINUX) {
+ memcpy((void *)0x800 - 18, sdi->r.esbx, 16);
+
+ /* DS:SI points to partition info */
+ esi = 0x800 - 18;
+ }
+
+ /*
+ * For a BSS boot sector we have to transfer the
+ * superblock.
+ */
+ if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX &&
+ type == KT_BSS && vfat_copy_superblock(buf))
+ goto boot_bail;
+
+ /*
+ * Set up initial stack frame (not used by PXE if
+ * keeppxe is set - we use the PXE stack then.)
+ */
+ if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
+ keeppxe = 0x03; /* Chainloading + keep PXE */
+ stack = (char *)sdi->r.fssi;
+
+ /*
+ * Restore DS, EDX and ESI to the true initial
+ * values.
+ */
+ bx = *(uint16_t *)&stack[6];
+ edx = *(uint32_t *)&stack[28];
+ esi = *(uint32_t *)&stack[12];
+
+ /* Reset stack to PXE original */
+ regs.es = regs.ss = sdi->rr.r.fs;
+ regs.esp.w[0] = sdi->rr.r.esi.w[0] + 44;
+ } else {
+ char *esdi = (char *)sdi->disk.esdi_ptr;
+
+ /*
+ * StackBuf is guaranteed to have 44 bytes
+ * free immediately above it, and will not
+ * interfere with our existing stack.
+ */
+ stack = StackBuf;
+ memset(stack, 0, 44);
+
+ regs.esp.w[0] = (uint16_t)(unsigned long)stack + 44;
+
+ /*
+ * DON'T DO THIS FOR PXELINUX...
+ * For PXE, ES:BX -> PXENV+, and this would
+ * corrupt that use.
+ *
+ * Restore ES:DI -> $PnP (if we were ourselves
+ * called that way...)
+ */
+
+ /* New DI */
+ *(uint16_t *)&stack[8] = *(uint16_t *)&esdi[0];
+
+ /* New ES */
+ *(uint16_t *)&stack[4] = *(uint16_t *)&esdi[2];
+
+ }
+
+ *(uint32_t *)&stack[28] = edx; /* New EDX */
+ *(uint32_t *)&stack[12] = esi; /* New ESI */
+ *(uint16_t *)&stack[6] = bx; /* New DS */
+
+ regs.ip = 0x7c00;
+ regs.esi.l = esi;
+ regs.edx.l = edx;
+
+ free_mem = *(volatile unsigned int *)BIOS_fbm;
+ free_mem <<= 10;
+ new_free_mem = free_mem - (0x7c00 + size);
+
+ mmap = syslinux_memory_map();
+ if (!mmap)
+ goto boot_bail;
+
+ if (!syslinux_add_movelist(&fraglist, 0x7c00,
+ (addr_t)buf, size))
+ syslinux_shuffle_boot_rm(fraglist, mmap,
+ keeppxe, &regs);
+ free(mmap);
+boot_bail:
+ free(buf);
} else {
/* Need add one item for kernel load, as we don't use
* the assembly runkernel.inc any more */
new_linux_kernel((char *)kernel, (char *)cmdline);
}
+bail:
lfree((void *)kernel);
/* If this returns, something went bad; return to menu */
diff --git a/com32/elflink/ldlinux/loadhigh.c b/com32/elflink/ldlinux/loadhigh.c
new file mode 100644
index 00000000..0f2f8428
--- /dev/null
+++ b/com32/elflink/ldlinux/loadhigh.c
@@ -0,0 +1,112 @@
+/*
+ * -----------------------------------------------------------------------
+ *
+ * Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
+ *
+ * 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,
+ * Boston MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * loadhigh.c
+ *
+ * An alternate interface to getfssec.
+ *
+ * Inputs: SI = file handle/cluster pointer
+ * EDI = target address in high memory
+ * EAX = maximum number of bytes to load
+ * DX = zero-padding mask (e.g. 0003h for pad to dword)
+ * BX = 16-bit subroutine to call at the top of each loop
+ * (to print status and check for abort)
+ * EBP = maximum load address
+ *
+ * Outputs: SI = file handle/cluster pointer
+ * EBX = first untouched address (not including padding)
+ * EDI = first untouched address (including padding)
+ * CF = reached EOF
+ * OF = ran out of high memory
+ */
+
+#include <com32.h>
+#include <minmax.h>
+#include "core.h"
+#include "fs.h"
+
+#define MAX_CHUNK (1UL << 20) /* 1 MB */
+
+void pm_load_high(com32sys_t *regs)
+{
+ struct fs_info *fs;
+ uint32_t bytes;
+ uint32_t zero_mask;
+ bool have_more;
+ uint32_t bytes_read;
+ char *buf, *limit;
+ struct file *file;
+ uint32_t sector_mask;
+ size_t pad;
+ uint32_t retflags = 0;
+
+ bytes = regs->eax.l;
+ zero_mask = regs->edx.w[0];
+ buf = (char *)regs->edi.l;
+ limit = (char *)(regs->ebp.l & ~zero_mask);
+ file = handle_to_file(regs->esi.w[0]);
+ fs = file->fs;
+
+ sector_mask = SECTOR_SIZE(fs) - 1;
+
+ while (bytes) {
+ uint32_t sectors;
+ uint32_t chunk;
+
+ if (buf + SECTOR_SIZE(fs) > limit) {
+ /* Can't fit even one more sector in... */
+ retflags = EFLAGS_OF;
+ break;
+ }
+
+ chunk = bytes;
+
+ if (regs->ebx.w[0]) {
+ call16((void (*)(void))(size_t)regs->ebx.w[0], &zero_regs, NULL);
+ chunk = min(chunk, MAX_CHUNK);
+ }
+
+ if (chunk > (((char *)limit - buf) & ~sector_mask))
+ chunk = ((char *)limit - buf) & ~sector_mask;
+
+ sectors = (chunk + sector_mask) >> SECTOR_SHIFT(fs);
+ bytes_read = fs->fs_ops->getfssec(file, buf, sectors, &have_more);
+
+ if (bytes_read > chunk)
+ bytes_read = chunk;
+
+ buf += bytes_read;
+ bytes -= bytes_read;
+
+ if (!have_more) {
+ /*
+ * If we reach EOF, the filesystem driver will have already closed
+ * the underlying file... this really should be cleaner.
+ */
+ _close_file(file);
+ regs->esi.w[0] = 0;
+ retflags = EFLAGS_CF;
+ break;
+ }
+ }
+
+ pad = (size_t)buf & zero_mask;
+ if (pad)
+ memset(buf, 0, pad);
+
+ regs->ebx.l = (size_t)buf;
+ regs->edi.l = (size_t)buf + pad;
+ set_flags(regs, retflags);
+}