core: do aligned transfers in bcopy32
[syslinux.git] / com32 / modules / elf.c
blob9e4a18c7930a43d1cf27bc581b48107dea296e54
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
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:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
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.
26 * ----------------------------------------------------------------------- */
29 * elf.c
31 * Module to load a protected-mode ELF kernel
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>
46 #include <syslinux/loadfile.h>
47 #include <syslinux/movebits.h>
48 #include <syslinux/bootpm.h>
50 /* If we don't have this much memory for the stack, signal failure */
51 #define MIN_STACK 512
53 #define DEBUG 0
54 #if DEBUG
55 # define dprintf printf
56 #else
57 # define dprintf(f, ...) ((void)0)
58 #endif
60 static inline void error(const char *msg)
62 fputs(msg, stderr);
65 int boot_elf(void *ptr, size_t len, char **argv)
67 char *cptr = ptr;
68 Elf32_Ehdr *eh = ptr;
69 Elf32_Phdr *ph;
70 unsigned int i;
71 struct syslinux_movelist *ml = NULL;
72 struct syslinux_memmap *mmap = NULL, *amap = NULL;
73 struct syslinux_pm_regs regs;
74 int argc;
75 addr_t argsize;
76 char **argp;
77 addr_t lstart, llen;
78 char *stack_frame = NULL;
79 addr_t stack_frame_size;
80 addr_t stack_pointer;
81 uint32_t *spp;
82 char *sfp;
83 addr_t sfa;
85 memset(&regs, 0, sizeof regs);
88 * Note: mmap is the memory map (containing free and zeroed regions)
89 * needed by syslinux_shuffle_boot_pm(); amap is a map where we keep
90 * track ourselves which target memory ranges have already been
91 * allocated.
94 if ( len < sizeof(Elf32_Ehdr) )
95 goto bail;
97 /* Must be ELF, 32-bit, littleendian, version 1 */
98 if ( memcmp(eh->e_ident, "\x7f""ELF\1\1\1", 6) )
99 goto bail;
101 /* Is this a worthwhile test? In particular x86-64 normally
102 would imply ELF64 support, which we could do as long as
103 the addresses are 32-bit addresses, and entry is 32 bits.
104 64-bit addresses would take a lot more work. */
105 if ( eh->e_machine != EM_386 && eh->e_machine != EM_486 &&
106 eh->e_machine != EM_X86_64 )
107 goto bail;
109 if ( eh->e_version != EV_CURRENT )
110 goto bail;
112 if ( eh->e_ehsize < sizeof(Elf32_Ehdr) || eh->e_ehsize >= len )
113 goto bail;
115 if ( eh->e_phentsize < sizeof(Elf32_Phdr) )
116 goto bail;
118 if ( !eh->e_phnum )
119 goto bail;
121 if ( eh->e_phoff+eh->e_phentsize*eh->e_phnum > len )
122 goto bail;
124 mmap = syslinux_memory_map();
125 amap = syslinux_dup_memmap(mmap);
126 if (!mmap || !amap)
127 goto bail;
129 #if DEBUG
130 dprintf("Initial memory map:\n");
131 syslinux_dump_memmap(stdout, mmap);
132 #endif
134 ph = (Elf32_Phdr *)(cptr+eh->e_phoff);
136 for (i = 0; i < eh->e_phnum; i++) {
137 if (ph->p_type == PT_LOAD || ph->p_type == PT_PHDR) {
138 /* This loads at p_paddr, which is arguably the correct semantics.
139 The SysV spec says that SysV loads at p_vaddr (and thus Linux does,
140 too); that is, however, a major brainfuckage in the spec. */
141 addr_t addr = ph->p_paddr;
142 addr_t msize = ph->p_memsz;
143 addr_t dsize = min(msize, ph->p_filesz);
145 dprintf("Segment at 0x%08x data 0x%08x len 0x%08x\n",
146 addr, dsize, msize);
148 if (syslinux_memmap_type(amap, addr, msize) != SMT_FREE) {
149 printf("Memory segment at 0x%08x (len 0x%08x) is unavailable\n",
150 addr, msize);
151 goto bail; /* Memory region unavailable */
154 /* Mark this region as allocated in the available map */
155 if (syslinux_add_memmap(&amap, addr, dsize, SMT_ALLOC))
156 goto bail;
158 if (ph->p_filesz) {
159 /* Data present region. Create a move entry for it. */
160 if (syslinux_add_movelist(&ml, addr, (addr_t)cptr+ph->p_offset,
161 dsize))
162 goto bail;
164 if (msize > dsize) {
165 /* Zero-filled region. Mark as a zero region in the memory map. */
166 if (syslinux_add_memmap(&mmap, addr+dsize, msize-dsize, SMT_ZERO))
167 goto bail;
169 } else {
170 /* Ignore this program header */
173 ph = (Elf32_Phdr *)((char *)ph + eh->e_phentsize);
176 /* Create the invocation record (initial stack frame) */
178 argsize = argc = 0;
179 for (argp = argv; *argp; argp++) {
180 dprintf("argv[%2d] = \"%s\"\n", argc, *argp);
181 argc++;
182 argsize += strlen(*argp)+1;
185 /* We need the argument strings, argument pointers,
186 argc, plus four zero-word terminators. */
187 stack_frame_size = argsize + argc*sizeof(char *) + 5*sizeof(long);
188 stack_frame_size = (stack_frame_size+15) & ~15;
189 stack_frame = calloc(stack_frame_size, 1);
190 if (!stack_frame)
191 goto bail;
193 #if DEBUG
194 dprintf("Right before syslinux_memmap_largest()...\n");
195 syslinux_dump_memmap(stdout, amap);
196 #endif
198 if (syslinux_memmap_largest(amap, SMT_FREE, &lstart, &llen))
199 goto bail; /* NO free memory?! */
201 if (llen < stack_frame_size+MIN_STACK+16)
202 goto bail; /* Insufficient memory */
204 /* Initial stack pointer address */
205 stack_pointer = (lstart+llen-stack_frame_size) & ~15;
207 dprintf("Stack frame at 0x%08x len 0x%08x\n",
208 stack_pointer, stack_frame_size);
210 /* Create the stack frame. sfp is the pointer in current memory for
211 the next argument string, sfa is the address in its final resting place.
212 spp is the pointer into the argument array in current memory. */
213 spp = (uint32_t *)stack_frame;
214 sfp = stack_frame + argc*sizeof(char *) + 5*sizeof(long);
215 sfa = stack_pointer + argc*sizeof(char *) + 5*sizeof(long);
217 *spp++ = argc;
218 for (argp = argv; *argp; argp++) {
219 int bytes = strlen(*argp) + 1; /* Including final null */
220 *spp++ = sfa;
221 memcpy(sfp, *argp, bytes);
222 sfp += bytes;
223 sfa += bytes;
225 /* Zero fields are aready taken care of by calloc() */
227 /* ... and we'll want to move it into the right place... */
228 #if DEBUG
229 if (syslinux_memmap_type(amap, stack_pointer, stack_frame_size)
230 != SMT_FREE) {
231 dprintf("Stack frame area not free (how did that happen?)!\n");
232 goto bail; /* Memory region unavailable */
234 #endif
236 if (syslinux_add_memmap(&amap, stack_pointer, stack_frame_size, SMT_ALLOC))
237 goto bail;
239 if (syslinux_add_movelist(&ml, stack_pointer, (addr_t)stack_frame,
240 stack_frame_size))
241 goto bail;
243 memset(&regs, 0, sizeof regs);
244 regs.eip = eh->e_entry;
245 regs.esp = stack_pointer;
247 #if DEBUG
248 dprintf("Final memory map:\n");
249 syslinux_dump_memmap(stdout, mmap);
251 dprintf("Final available map:\n");
252 syslinux_dump_memmap(stdout, amap);
254 dprintf("Movelist:\n");
255 syslinux_dump_movelist(stdout, ml);
256 #endif
258 /* This should not return... */
259 fputs("Booting...\n", stdout);
260 syslinux_shuffle_boot_pm(ml, mmap, 0, &regs);
262 bail:
263 if (stack_frame)
264 free(stack_frame);
265 syslinux_free_memmap(amap);
266 syslinux_free_memmap(mmap);
267 syslinux_free_movelist(ml);
269 return -1;
272 int main(int argc, char *argv[])
274 void *data;
275 size_t data_len;
277 openconsole(&dev_null_r, &dev_stdcon_w);
279 if (argc < 2) {
280 error("Usage: elf.c32 elf_file arguments...\n");
281 return 1;
284 if (zloadfile(argv[1], &data, &data_len)) {
285 error("Unable to load file\n");
286 return 1;
289 boot_elf(data, data_len, &argv[1]);
290 error("Invalid ELF file or insufficient memory\n");
291 return 1;