aboutsummaryrefslogtreecommitdiffstats
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
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>
-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.c (renamed from core/fs/loadhigh.c)2
-rw-r--r--com32/lib/Makefile3
-rw-r--r--core/diskboot.inc1
-rw-r--r--core/fs/fat/fat.c28
-rw-r--r--core/include/core.h1
-rw-r--r--core/include/fs.h3
-rw-r--r--core/isolinux.asm1
-rw-r--r--core/pxelinux.asm1
11 files changed, 184 insertions, 3 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/core/fs/loadhigh.c b/com32/elflink/ldlinux/loadhigh.c
index bd9d3535..0f2f8428 100644
--- a/core/fs/loadhigh.c
+++ b/com32/elflink/ldlinux/loadhigh.c
@@ -37,7 +37,7 @@
#include "core.h"
#include "fs.h"
-#define MAX_CHUNK (1 << 20) /* 1 MB */
+#define MAX_CHUNK (1UL << 20) /* 1 MB */
void pm_load_high(com32sys_t *regs)
{
diff --git a/com32/lib/Makefile b/com32/lib/Makefile
index a0ddb9d6..4edf0a41 100644
--- a/com32/lib/Makefile
+++ b/com32/lib/Makefile
@@ -47,7 +47,7 @@ LIBPCI_OBJS = \
LIBSYSLINUX_OBJS = \
syslinux/reboot.o syslinux/keyboard.o \
syslinux/features.o syslinux/config.o \
- syslinux/dsinfo.o syslinux/version.o \
+ syslinux/version.o \
syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o \
syslinux/video/fontquery.o syslinux/video/reportmode.o
@@ -178,6 +178,7 @@ CORELIBOBJS = \
MINLIBOBJS = \
syslinux/ipappend.o \
+ syslinux/dsinfo.o \
$(LIBOTHER_OBJS) \
$(LIBGCC_OBJS) \
$(LIBCONSOLE_OBJS) \
diff --git a/core/diskboot.inc b/core/diskboot.inc
index 141986e8..3e42044a 100644
--- a/core/diskboot.inc
+++ b/core/diskboot.inc
@@ -28,6 +28,7 @@
; reduce the code size...
;
+ global StackBuf
StackBuf equ STACK_TOP-44-92 ; Start the stack here (grow down - 4K)
PartInfo equ StackBuf
.mbr equ PartInfo
diff --git a/core/fs/fat/fat.c b/core/fs/fat/fat.c
index fbc4386b..2c8dc315 100644
--- a/core/fs/fat/fat.c
+++ b/core/fs/fat/fat.c
@@ -779,6 +779,34 @@ static int vfat_fs_init(struct fs_info *fs)
return fs->block_shift;
}
+int vfat_copy_superblock(void *buf)
+{
+ struct fat_bpb fat;
+ struct disk *disk;
+ size_t sb_off;
+ void *dst;
+ int sb_len;
+
+ disk = this_fs->fs_dev->disk;
+ disk->rdwr_sectors(disk, &fat, 0, 1, 0);
+
+ /* XXX: Find better sanity checks... */
+ if (!fat.bxResSectors || !fat.bxFATs)
+ return -1;
+
+ sb_off = offsetof(struct fat_bpb, sector_size);
+ sb_len = offsetof(struct fat_bpb, fat12_16) - sb_off \
+ + sizeof(fat.fat12_16);
+
+ /*
+ * Only copy fields of the superblock we actually care about.
+ */
+ dst = buf + sb_off;
+ memcpy(dst, (void *)&fat + sb_off, sb_len);
+
+ return 0;
+}
+
const struct fs_ops vfat_fs_ops = {
.fs_name = "vfat",
.fs_flags = FS_USEMEM | FS_THISIND,
diff --git a/core/include/core.h b/core/include/core.h
index 7d36e982..e19f2f1e 100644
--- a/core/include/core.h
+++ b/core/include/core.h
@@ -24,6 +24,7 @@ extern char cmd_line[];
extern char ConfigFile[];
extern char syslinux_banner[];
extern char copyright_str[];
+extern char StackBuf[];
extern uint8_t KbdMap[256];
diff --git a/core/include/fs.h b/core/include/fs.h
index 40ca09a4..481e085c 100644
--- a/core/include/fs.h
+++ b/core/include/fs.h
@@ -234,4 +234,7 @@ uint32_t generic_getfssec(struct file *file, char *buf,
/* nonextextent.c */
int no_next_extent(struct inode *, uint32_t);
+/* fat.c */
+int vfat_copy_superblock(void *buf);
+
#endif /* FS_H */
diff --git a/core/isolinux.asm b/core/isolinux.asm
index 6e52736b..4790887c 100644
--- a/core/isolinux.asm
+++ b/core/isolinux.asm
@@ -167,6 +167,7 @@ _spec_len equ _spec_end - _spec_start
;; CD-ROM sector (2K) of the file, so the number one priority is actually
;; loading the rest.
;;
+ global StackBuf
StackBuf equ STACK_TOP-44 ; 44 bytes needed for
; the bootsector chainloading
; code!
diff --git a/core/pxelinux.asm b/core/pxelinux.asm
index e59a0e2e..5f72809e 100644
--- a/core/pxelinux.asm
+++ b/core/pxelinux.asm
@@ -89,6 +89,7 @@ LocalBootType resw 1 ; Local boot return code
DHCPMagic resb 1 ; PXELINUX magic flags
section .text16
+ global StackBuf
StackBuf equ STACK_TOP-44 ; Base of stack if we use our own
StackHome equ StackBuf