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
error(const char * msg)54 static inline void error(const char *msg)
55 {
56 fputs(msg, stderr);
57 }
58
boot_elf(void * ptr,size_t len,char ** argv)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(®s, 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(®s, 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, ®s);
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
main(int argc,char * argv[])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