| /* ----------------------------------------------------------------------- * |
| * |
| * 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; |
| |
| dprintf("Initial memory map:\n"); |
| syslinux_dump_memmap(mmap); |
| |
| 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; |
| |
| dprintf("Right before syslinux_memmap_largest()...\n"); |
| syslinux_dump_memmap(amap); |
| |
| 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; |
| |
| dprintf("Final memory map:\n"); |
| syslinux_dump_memmap(mmap); |
| |
| dprintf("Final available map:\n"); |
| syslinux_dump_memmap(amap); |
| |
| dprintf("Movelist:\n"); |
| syslinux_dump_movelist(ml); |
| |
| /* 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; |
| |
| 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; |
| } |