Home | History | Annotate | Download | only in modules
      1 /* ----------------------------------------------------------------------- *
      2  *
      3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
      4  *
      5  *   Permission is hereby granted, free of charge, to any person
      6  *   obtaining a copy of this software and associated documentation
      7  *   files (the "Software"), to deal in the Software without
      8  *   restriction, including without limitation the rights to use,
      9  *   copy, modify, merge, publish, distribute, sublicense, and/or
     10  *   sell copies of the Software, and to permit persons to whom
     11  *   the Software is furnished to do so, subject to the following
     12  *   conditions:
     13  *
     14  *   The above copyright notice and this permission notice shall
     15  *   be included in all copies or substantial portions of the Software.
     16  *
     17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     24  *   OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  * ----------------------------------------------------------------------- */
     27 
     28 /*
     29  * elf.c
     30  *
     31  * Module to load a protected-mode ELF kernel
     32  */
     33 
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <inttypes.h>
     37 #include <string.h>
     38 #include <fcntl.h>
     39 #include <unistd.h>
     40 #include <errno.h>
     41 #include <minmax.h>
     42 #include <sys/stat.h>
     43 #include <elf.h>
     44 #include <console.h>
     45 #include <dprintf.h>
     46 
     47 #include <syslinux/loadfile.h>
     48 #include <syslinux/movebits.h>
     49 #include <syslinux/bootpm.h>
     50 
     51 /* If we don't have this much memory for the stack, signal failure */
     52 #define MIN_STACK	512
     53 
     54 static inline void error(const char *msg)
     55 {
     56     fputs(msg, stderr);
     57 }
     58 
     59 int boot_elf(void *ptr, size_t len, char **argv)
     60 {
     61     char *cptr = ptr;
     62     Elf32_Ehdr *eh = ptr;
     63     Elf32_Phdr *ph;
     64     unsigned int i;
     65     struct syslinux_movelist *ml = NULL;
     66     struct syslinux_memmap *mmap = NULL, *amap = NULL;
     67     struct syslinux_pm_regs regs;
     68     int argc;
     69     addr_t argsize;
     70     char **argp;
     71     addr_t lstart, llen;
     72     char *stack_frame = NULL;
     73     addr_t stack_frame_size;
     74     addr_t stack_pointer;
     75     uint32_t *spp;
     76     char *sfp;
     77     addr_t sfa;
     78 
     79     memset(&regs, 0, sizeof regs);
     80 
     81     /*
     82      * Note: mmap is the memory map (containing free and zeroed regions)
     83      * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep
     84      * track ourselves which target memory ranges have already been
     85      * allocated.
     86      */
     87 
     88     if (len < sizeof(Elf32_Ehdr))
     89 	goto bail;
     90 
     91     /* Must be ELF, 32-bit, littleendian, version 1 */
     92     if (memcmp(eh->e_ident, "\x7f" "ELF\1\1\1", 6))
     93 	goto bail;
     94 
     95     /* Is this a worthwhile test?  In particular x86-64 normally
     96        would imply ELF64 support, which we could do as long as
     97        the addresses are 32-bit addresses, and entry is 32 bits.
     98        64-bit addresses would take a lot more work. */
     99     if (eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
    100 	eh->e_machine != EM_X86_64)
    101 	goto bail;
    102 
    103     if (eh->e_version != EV_CURRENT)
    104 	goto bail;
    105 
    106     if (eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len)
    107 	goto bail;
    108 
    109     if (eh->e_phentsize < sizeof(Elf32_Phdr))
    110 	goto bail;
    111 
    112     if (!eh->e_phnum)
    113 	goto bail;
    114 
    115     if (eh->e_phoff + eh->e_phentsize * eh->e_phnum > len)
    116 	goto bail;
    117 
    118     mmap = syslinux_memory_map();
    119     amap = syslinux_dup_memmap(mmap);
    120     if (!mmap || !amap)
    121 	goto bail;
    122 
    123     dprintf("Initial memory map:\n");
    124     syslinux_dump_memmap(mmap);
    125 
    126     ph = (Elf32_Phdr *) (cptr + eh->e_phoff);
    127 
    128     for (i = 0; i < eh->e_phnum; i++) {
    129 	if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
    130 	    /* This loads at p_paddr, which is arguably the correct semantics.
    131 	       The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
    132 	       too); that is, however, a major brainfuckage in the spec. */
    133 	    addr_t addr = ph->p_paddr;
    134 	    addr_t msize = ph->p_memsz;
    135 	    addr_t dsize = min(msize, ph->p_filesz);
    136 
    137 	    dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
    138 		    addr, dsize, msize);
    139 
    140 	    if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
    141 		printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
    142 		       addr, msize);
    143 		goto bail;	/* Memory region unavailable */
    144 	    }
    145 
    146 	    /* Mark this region as allocated in the available map */
    147 	    if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC))
    148 		goto bail;
    149 
    150 	    if (ph->p_filesz) {
    151 		/* Data present region.  Create a move entry for it. */
    152 		if (syslinux_add_movelist
    153 		    (&ml, addr, (addr_t) cptr + ph->p_offset, dsize))
    154 		    goto bail;
    155 	    }
    156 	    if (msize > dsize) {
    157 		/* Zero-filled region.  Mark as a zero region in the memory map. */
    158 		if (syslinux_add_memmap
    159 		    (&mmap, addr + dsize, msize - dsize, SMT_ZERO))
    160 		    goto bail;
    161 	    }
    162 	} else {
    163 	    /* Ignore this program header */
    164 	}
    165 
    166 	ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize);
    167     }
    168 
    169     /* Create the invocation record (initial stack frame) */
    170 
    171     argsize = argc = 0;
    172     for (argp = argv; *argp; argp++) {
    173 	dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
    174 	argc++;
    175 	argsize += strlen(*argp) + 1;
    176     }
    177 
    178     /* We need the argument strings, argument pointers,
    179        argc, plus four zero-word terminators. */
    180     stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long);
    181     stack_frame_size = (stack_frame_size + 15) & ~15;
    182     stack_frame = calloc(stack_frame_size, 1);
    183     if (!stack_frame)
    184 	goto bail;
    185 
    186     dprintf("Right before syslinux_memmap_largest()...\n");
    187     syslinux_dump_memmap(amap);
    188 
    189     if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
    190 	goto bail;		/* NO free memory?! */
    191 
    192     if (llen < stack_frame_size + MIN_STACK + 16)
    193 	goto bail;		/* Insufficient memory  */
    194 
    195     /* Initial stack pointer address */
    196     stack_pointer = (lstart + llen - stack_frame_size) & ~15;
    197 
    198     dprintf("Stack frame at 0x%08x len 0x%08x\n",
    199 	    stack_pointer, stack_frame_size);
    200 
    201     /* Create the stack frame.  sfp is the pointer in current memory for
    202        the next argument string, sfa is the address in its final resting place.
    203        spp is the pointer into the argument array in current memory. */
    204     spp = (uint32_t *) stack_frame;
    205     sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long);
    206     sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long);
    207 
    208     *spp++ = argc;
    209     for (argp = argv; *argp; argp++) {
    210 	int bytes = strlen(*argp) + 1;	/* Including final null */
    211 	*spp++ = sfa;
    212 	memcpy(sfp, *argp, bytes);
    213 	sfp += bytes;
    214 	sfa += bytes;
    215     }
    216     /* Zero fields are aready taken care of by calloc() */
    217 
    218     /* ... and we'll want to move it into the right place... */
    219 #if DEBUG
    220     if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
    221 	!= SMT_FREE) {
    222 	dprintf("Stack frame area not free (how did that happen?)!\n");
    223 	goto bail;		/* Memory region unavailable */
    224     }
    225 #endif
    226 
    227     if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
    228 	goto bail;
    229 
    230     if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame,
    231 			      stack_frame_size))
    232 	goto bail;
    233 
    234     memset(&regs, 0, sizeof regs);
    235     regs.eip = eh->e_entry;
    236     regs.esp = stack_pointer;
    237 
    238     dprintf("Final memory map:\n");
    239     syslinux_dump_memmap(mmap);
    240 
    241     dprintf("Final available map:\n");
    242     syslinux_dump_memmap(amap);
    243 
    244     dprintf("Movelist:\n");
    245     syslinux_dump_movelist(ml);
    246 
    247     /* This should not return... */
    248     fputs("Booting...\n", stdout);
    249     syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);
    250 
    251 bail:
    252     if (stack_frame)
    253 	free(stack_frame);
    254     syslinux_free_memmap(amap);
    255     syslinux_free_memmap(mmap);
    256     syslinux_free_movelist(ml);
    257 
    258     return -1;
    259 }
    260 
    261 int main(int argc, char *argv[])
    262 {
    263     void *data;
    264     size_t data_len;
    265 
    266     if (argc < 2) {
    267 	error("Usage: elf.c32 elf_file arguments...\n");
    268 	return 1;
    269     }
    270 
    271     if (zloadfile(argv[1], &data, &data_len)) {
    272 	error("Unable to load file\n");
    273 	return 1;
    274     }
    275 
    276     boot_elf(data, data_len, &argv[1]);
    277     error("Invalid ELF file or insufficient memory\n");
    278     return 1;
    279 }
    280