diff options
Diffstat (limited to 'contrib/syslinux-4.02/com32/modules/elf.c')
-rw-r--r-- | contrib/syslinux-4.02/com32/modules/elf.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/com32/modules/elf.c b/contrib/syslinux-4.02/com32/modules/elf.c new file mode 100644 index 0000000..182afa6 --- /dev/null +++ b/contrib/syslinux-4.02/com32/modules/elf.c @@ -0,0 +1,287 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +/* + * elf.c + * + * Module to load a protected-mode ELF kernel + */ + +#include <stdio.h> +#include <stdlib.h> +#include <inttypes.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <minmax.h> +#include <sys/stat.h> +#include <elf.h> +#include <console.h> +#include <dprintf.h> + +#include <syslinux/loadfile.h> +#include <syslinux/movebits.h> +#include <syslinux/bootpm.h> + +/* If we don't have this much memory for the stack, signal failure */ +#define MIN_STACK 512 + +static inline void error(const char *msg) +{ + fputs(msg, stderr); +} + +int boot_elf(void *ptr, size_t len, char **argv) +{ + char *cptr = ptr; + Elf32_Ehdr *eh = ptr; + Elf32_Phdr *ph; + unsigned int i; + struct syslinux_movelist *ml = NULL; + struct syslinux_memmap *mmap = NULL, *amap = NULL; + struct syslinux_pm_regs regs; + int argc; + addr_t argsize; + char **argp; + addr_t lstart, llen; + char *stack_frame = NULL; + addr_t stack_frame_size; + addr_t stack_pointer; + uint32_t *spp; + char *sfp; + addr_t sfa; + + memset(®s, 0, sizeof regs); + + /* + * Note: mmap is the memory map (containing free and zeroed regions) + * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep + * track ourselves which target memory ranges have already been + * allocated. + */ + + if (len < sizeof(Elf32_Ehdr)) + goto bail; + + /* Must be ELF, 32-bit, littleendian, version 1 */ + if (memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6)) + goto bail; + + /* Is this a worthwhile test? In particular x86-64 normally + would imply ELF64 support, which we could do as long as + the addresses are 32-bit addresses, and entry is 32 bits. + 64-bit addresses would take a lot more work. */ + if (eh->e_machine != EM_386 && eh->e_machine != EM_486 && + eh->e_machine != EM_X86_64) + goto bail; + + if (eh->e_version != EV_CURRENT) + goto bail; + + if (eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len) + goto bail; + + if (eh->e_phentsize < sizeof(Elf32_Phdr)) + goto bail; + + if (!eh->e_phnum) + goto bail; + + if (eh->e_phoff + eh->e_phentsize * eh->e_phnum > len) + goto bail; + + mmap = syslinux_memory_map(); + amap = syslinux_dup_memmap(mmap); + if (!mmap || !amap) + goto bail; + +#if DEBUG + dprintf("Initial memory map:\n"); + syslinux_dump_memmap(stdout, mmap); +#endif + + ph = (Elf32_Phdr *) (cptr + eh->e_phoff); + + for (i = 0; i < eh->e_phnum; i++) { + if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) { + /* This loads at p_paddr, which is arguably the correct semantics. + The SysV spec says that SysV loads at p_vaddr (and thus Linux does, + too); that is, however, a major brainfuckage in the spec. */ + addr_t addr = ph->p_paddr; + addr_t msize = ph->p_memsz; + addr_t dsize = min(msize, ph->p_filesz); + + dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n", + addr, dsize, msize); + + if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) { + printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n", + addr, msize); + goto bail; /* Memory region unavailable */ + } + + /* Mark this region as allocated in the available map */ + if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC)) + goto bail; + + if (ph->p_filesz) { + /* Data present region. Create a move entry for it. */ + if (syslinux_add_movelist + (&ml, addr, (addr_t) cptr + ph->p_offset, dsize)) + goto bail; + } + if (msize > dsize) { + /* Zero-filled region. Mark as a zero region in the memory map. */ + if (syslinux_add_memmap + (&mmap, addr + dsize, msize - dsize, SMT_ZERO)) + goto bail; + } + } else { + /* Ignore this program header */ + } + + ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize); + } + + /* Create the invocation record (initial stack frame) */ + + argsize = argc = 0; + for (argp = argv; *argp; argp++) { + dprintf("argv[%2d] = \"%s\"\n", argc, *argp); + argc++; + argsize += strlen(*argp) + 1; + } + + /* We need the argument strings, argument pointers, + argc, plus four zero-word terminators. */ + stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long); + stack_frame_size = (stack_frame_size + 15) & ~15; + stack_frame = calloc(stack_frame_size, 1); + if (!stack_frame) + goto bail; + +#if DEBUG + dprintf("Right before syslinux_memmap_largest()...\n"); + syslinux_dump_memmap(stdout, amap); +#endif + + if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen)) + goto bail; /* NO free memory?! */ + + if (llen < stack_frame_size + MIN_STACK + 16) + goto bail; /* Insufficient memory */ + + /* Initial stack pointer address */ + stack_pointer = (lstart + llen - stack_frame_size) & ~15; + + dprintf("Stack frame at 0x%08x len 0x%08x\n", + stack_pointer, stack_frame_size); + + /* Create the stack frame. sfp is the pointer in current memory for + the next argument string, sfa is the address in its final resting place. + spp is the pointer into the argument array in current memory. */ + spp = (uint32_t *) stack_frame; + sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long); + sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long); + + *spp++ = argc; + for (argp = argv; *argp; argp++) { + int bytes = strlen(*argp) + 1; /* Including final null */ + *spp++ = sfa; + memcpy(sfp, *argp, bytes); + sfp += bytes; + sfa += bytes; + } + /* Zero fields are aready taken care of by calloc() */ + + /* ... and we'll want to move it into the right place... */ +#if DEBUG + if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size) + != SMT_FREE) { + dprintf("Stack frame area not free (how did that happen?)!\n"); + goto bail; /* Memory region unavailable */ + } +#endif + + if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC)) + goto bail; + + if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame, + stack_frame_size)) + goto bail; + + memset(®s, 0, sizeof regs); + regs.eip = eh->e_entry; + regs.esp = stack_pointer; + +#if DEBUG + dprintf("Final memory map:\n"); + syslinux_dump_memmap(stdout, mmap); + + dprintf("Final available map:\n"); + syslinux_dump_memmap(stdout, amap); + + dprintf("Movelist:\n"); + syslinux_dump_movelist(stdout, ml); +#endif + + /* This should not return... */ + fputs("Booting...\n", stdout); + syslinux_shuffle_boot_pm(ml, mmap, 0, ®s); + +bail: + if (stack_frame) + free(stack_frame); + syslinux_free_memmap(amap); + syslinux_free_memmap(mmap); + syslinux_free_movelist(ml); + + return -1; +} + +int main(int argc, char *argv[]) +{ + void *data; + size_t data_len; + + openconsole(&dev_null_r, &dev_stdcon_w); + + if (argc < 2) { + error("Usage: elf.c32 elf_file arguments...\n"); + return 1; + } + + if (zloadfile(argv[1], &data, &data_len)) { + error("Unable to load file\n"); + return 1; + } + + boot_elf(data, data_len, &argv[1]); + error("Invalid ELF file or insufficient memory\n"); + return 1; +} |