182afa605c57eab0c2c1753e291d5c622faa4960
[profile/ivi/syslinux.git] / com32 / modules / elf.c
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 #if DEBUG
124     dprintf("Initial memory map:\n");
125     syslinux_dump_memmap(stdout, mmap);
126 #endif
127
128     ph = (Elf32_Phdr *) (cptr + eh->e_phoff);
129
130     for (i = 0; i < eh->e_phnum; i++) {
131         if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
132             /* This loads at p_paddr, which is arguably the correct semantics.
133                The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
134                too); that is, however, a major brainfuckage in the spec. */
135             addr_t addr = ph->p_paddr;
136             addr_t msize = ph->p_memsz;
137             addr_t dsize = min(msize, ph->p_filesz);
138
139             dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
140                     addr, dsize, msize);
141
142             if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
143                 printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
144                        addr, msize);
145                 goto bail;      /* Memory region unavailable */
146             }
147
148             /* Mark this region as allocated in the available map */
149             if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC))
150                 goto bail;
151
152             if (ph->p_filesz) {
153                 /* Data present region.  Create a move entry for it. */
154                 if (syslinux_add_movelist
155                     (&ml, addr, (addr_t) cptr + ph->p_offset, dsize))
156                     goto bail;
157             }
158             if (msize > dsize) {
159                 /* Zero-filled region.  Mark as a zero region in the memory map. */
160                 if (syslinux_add_memmap
161                     (&mmap, addr + dsize, msize - dsize, SMT_ZERO))
162                     goto bail;
163             }
164         } else {
165             /* Ignore this program header */
166         }
167
168         ph = (Elf32_Phdr *) ((char *)ph + eh->e_phentsize);
169     }
170
171     /* Create the invocation record (initial stack frame) */
172
173     argsize = argc = 0;
174     for (argp = argv; *argp; argp++) {
175         dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
176         argc++;
177         argsize += strlen(*argp) + 1;
178     }
179
180     /* We need the argument strings, argument pointers,
181        argc, plus four zero-word terminators. */
182     stack_frame_size = argsize + argc * sizeof(char *) + 5 * sizeof(long);
183     stack_frame_size = (stack_frame_size + 15) & ~15;
184     stack_frame = calloc(stack_frame_size, 1);
185     if (!stack_frame)
186         goto bail;
187
188 #if DEBUG
189     dprintf("Right before syslinux_memmap_largest()...\n");
190     syslinux_dump_memmap(stdout, amap);
191 #endif
192
193     if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
194         goto bail;              /* NO free memory?! */
195
196     if (llen < stack_frame_size + MIN_STACK + 16)
197         goto bail;              /* Insufficient memory  */
198
199     /* Initial stack pointer address */
200     stack_pointer = (lstart + llen - stack_frame_size) & ~15;
201
202     dprintf("Stack frame at 0x%08x len 0x%08x\n",
203             stack_pointer, stack_frame_size);
204
205     /* Create the stack frame.  sfp is the pointer in current memory for
206        the next argument string, sfa is the address in its final resting place.
207        spp is the pointer into the argument array in current memory. */
208     spp = (uint32_t *) stack_frame;
209     sfp = stack_frame + argc * sizeof(char *) + 5 * sizeof(long);
210     sfa = stack_pointer + argc * sizeof(char *) + 5 * sizeof(long);
211
212     *spp++ = argc;
213     for (argp = argv; *argp; argp++) {
214         int bytes = strlen(*argp) + 1;  /* Including final null */
215         *spp++ = sfa;
216         memcpy(sfp, *argp, bytes);
217         sfp += bytes;
218         sfa += bytes;
219     }
220     /* Zero fields are aready taken care of by calloc() */
221
222     /* ... and we'll want to move it into the right place... */
223 #if DEBUG
224     if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
225         != SMT_FREE) {
226         dprintf("Stack frame area not free (how did that happen?)!\n");
227         goto bail;              /* Memory region unavailable */
228     }
229 #endif
230
231     if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
232         goto bail;
233
234     if (syslinux_add_movelist(&ml, stack_pointer, (addr_t) stack_frame,
235                               stack_frame_size))
236         goto bail;
237
238     memset(&regs, 0, sizeof regs);
239     regs.eip = eh->e_entry;
240     regs.esp = stack_pointer;
241
242 #if DEBUG
243     dprintf("Final memory map:\n");
244     syslinux_dump_memmap(stdout, mmap);
245
246     dprintf("Final available map:\n");
247     syslinux_dump_memmap(stdout, amap);
248
249     dprintf("Movelist:\n");
250     syslinux_dump_movelist(stdout, ml);
251 #endif
252
253     /* This should not return... */
254     fputs("Booting...\n", stdout);
255     syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);
256
257 bail:
258     if (stack_frame)
259         free(stack_frame);
260     syslinux_free_memmap(amap);
261     syslinux_free_memmap(mmap);
262     syslinux_free_movelist(ml);
263
264     return -1;
265 }
266
267 int main(int argc, char *argv[])
268 {
269     void *data;
270     size_t data_len;
271
272     openconsole(&dev_null_r, &dev_stdcon_w);
273
274     if (argc < 2) {
275         error("Usage: elf.c32 elf_file arguments...\n");
276         return 1;
277     }
278
279     if (zloadfile(argv[1], &data, &data_len)) {
280         error("Unable to load file\n");
281         return 1;
282     }
283
284     boot_elf(data, data_len, &argv[1]);
285     error("Invalid ELF file or insufficient memory\n");
286     return 1;
287 }