diff options
author | H. Peter Anvin <hpa@zytor.com> | 2012-06-07 15:12:07 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2012-06-07 15:12:07 -0700 |
commit | 4afa3c9082377c6bd96b1bde412f03ba9fa003ac (patch) | |
tree | f6c4d016cedd0f5bc43589df95aba1bca4b4ed91 /com32/elflink | |
parent | 98e33fa25fb8feeb66cbf20b9cba0d5adff3e658 (diff) | |
parent | c0d18deeee22f3466fd863d64b432660965ec66e (diff) | |
download | syslinux-4afa3c9082377c6bd96b1bde412f03ba9fa003ac.tar.gz |
Merge remote-tracking branch 'mfleming/elflink-dot-zero' into elflink
Resolved Conflicts:
com32/elflink/ldlinux/execute.c
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
Diffstat (limited to 'com32/elflink')
-rw-r--r-- | com32/elflink/ldlinux/Makefile | 2 | ||||
-rw-r--r-- | com32/elflink/ldlinux/config.h | 2 | ||||
-rw-r--r-- | com32/elflink/ldlinux/execute.c | 143 | ||||
-rw-r--r-- | com32/elflink/ldlinux/loadhigh.c | 112 |
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 e22c999b..5e47a622 100644 --- a/com32/elflink/ldlinux/execute.c +++ b/com32/elflink/ldlinux/execute.c @@ -17,11 +17,17 @@ #include <com32.h> #include <sys/exec.h> +#include <sys/io.h> #include "core.h" #include "menu.h" #include "fs.h" #include "config.h" #include "localboot.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[] = { @@ -47,6 +53,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); @@ -101,12 +108,148 @@ void execute(const char *cmdline, enum kernel_type type) start_ldlinux(argv); } else if (type == KT_LOCALBOOT) { local_boot(strtoul(kernel, NULL, 0)); + } 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(®); + + 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(®s, 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, ®s); + 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); +} |