[syslinux:elflink] elflink: Fix boot sector booting

syslinux-bot for Matt Fleming matt.fleming at intel.com
Thu Jun 7 15:27:08 PDT 2012


Commit-ID:  c0d18deeee22f3466fd863d64b432660965ec66e
Gitweb:     http://www.syslinux.org/commit/c0d18deeee22f3466fd863d64b432660965ec66e
Author:     Matt Fleming <matt.fleming at intel.com>
AuthorDate: Tue, 1 May 2012 08:55:45 +0100
Committer:  Matt Fleming <matt.fleming at intel.com>
CommitDate: Thu, 7 Jun 2012 14:19:20 +0100

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 at intel.com>

---
 com32/elflink/ldlinux/Makefile                |    2 +-
 com32/elflink/ldlinux/config.h                |    2 +
 com32/elflink/ldlinux/execute.c               |  143 +++++++++++++++++++++++++
 {core/fs => com32/elflink/ldlinux}/loadhigh.c |    2 +-
 com32/lib/Makefile                            |    3 +-
 core/diskboot.inc                             |    1 +
 core/fs/fat/fat.c                             |   28 +++++
 core/include/core.h                           |    1 +
 core/include/fs.h                             |    3 +
 core/isolinux.asm                             |    1 +
 core/pxelinux.asm                             |    1 +
 11 files changed, 184 insertions(+), 3 deletions(-)

diff --git a/com32/elflink/ldlinux/Makefile b/com32/elflink/ldlinux/Makefile
index ca4c7e2..75c618f 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 b15a082..4583202 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 afe999e..dd9854e 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
similarity index 98%
rename from core/fs/loadhigh.c
rename to com32/elflink/ldlinux/loadhigh.c
index bd9d353..0f2f842 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 a0ddb9d..4edf0a4 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 141986e..3e42044 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 fbc4386..2c8dc31 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 7d36e98..e19f2f1 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 40ca09a..481e085 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 6e52736..4790887 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 e59a0e2..5f72809 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
 


More information about the Syslinux-commits mailing list