1 /* ELF Boot loader
2 * As we have seek, this implementation can be straightforward.
3 * 2003-07 by SONE Takeshi
4 */
5
6 #include "config.h"
7 #include "kernel/kernel.h"
8 #include "libc/diskio.h"
9 #include "arch/common/elf_boot.h"
10 #include "libopenbios/elf_load.h"
11 #include "libopenbios/sys_info.h"
12 #include "libopenbios/ipchecksum.h"
13 #include "libopenbios/bindings.h"
14 #include "libopenbios/initprogram.h"
15 #include "libopenbios/ofmem.h"
16 #define printf printk
17 #define debug printk
18
19 #define DEBUG 0
20
21 #define MAX_HEADERS 0x20
22 #define BS 0x100 /* smallest step used when looking for the ELF header */
23
24 #ifdef CONFIG_PPC
25 extern void flush_icache_range( char *start, char *stop );
26 #endif
27
28 /* FreeBSD and possibly others mask the high 8 bits */
29 #define addr_fixup(addr) ((addr) & 0x00ffffff)
30
31 static char *image_name, *image_version;
32 static int fd;
33
34 /* Note: avoid name collision with platforms which have their own version of calloc() */
ob_calloc(size_t nmemb,size_t size)35 static void *ob_calloc(size_t nmemb, size_t size)
36 {
37 size_t alloc_size = nmemb * size;
38 void *mem;
39
40 if (alloc_size < nmemb || alloc_size < size) {
41 printf("calloc overflow: %u, %u\n", nmemb, size);
42 return NULL;
43 }
44
45 mem = malloc(alloc_size);
46 memset(mem, 0, alloc_size);
47
48 return mem;
49 }
50
check_mem_ranges(struct sys_info * info,Elf_phdr * phdr,int phnum)51 static int check_mem_ranges(struct sys_info *info,
52 Elf_phdr *phdr, int phnum)
53 {
54 int i, j;
55 unsigned long start, end;
56 unsigned long prog_start, prog_end;
57 struct memrange *mem;
58
59 prog_start = virt_to_phys(&_start);
60 prog_end = virt_to_phys(&_end);
61
62 for (i = 0; i < phnum; i++) {
63 if (phdr[i].p_type != PT_LOAD)
64 continue;
65 start = addr_fixup(phdr[i].p_paddr);
66 end = start + phdr[i].p_memsz;
67 if (start < prog_start && end > prog_start)
68 goto conflict;
69 if (start < prog_end && end > prog_end)
70 goto conflict;
71 mem=info->memrange;
72 for (j = 0; j < info->n_memranges; j++) {
73 if (mem[j].base <= start && mem[j].base + mem[j].size >= end)
74 break;
75 }
76 if (j >= info->n_memranges)
77 goto badseg;
78 }
79 return 1;
80
81 conflict:
82 printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
83
84 badseg:
85 printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
86 return 0;
87 }
88
process_image_notes(Elf_phdr * phdr,int phnum,unsigned short * sum_ptr,unsigned int offset)89 static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
90 unsigned short *sum_ptr,
91 unsigned int offset)
92 {
93 int i;
94 char *buf = NULL;
95 int retval = 0;
96 unsigned long addr, end;
97 Elf_Nhdr *nhdr;
98 const char *name;
99 void *desc;
100
101 for (i = 0; i < phnum; i++) {
102 if (phdr[i].p_type != PT_NOTE)
103 continue;
104 buf = malloc(phdr[i].p_filesz);
105 seek_io(fd, offset + phdr[i].p_offset);
106 if ((size_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
107 printf("Can't read note segment\n");
108 goto out;
109 }
110 addr = (unsigned long) buf;
111 end = addr + phdr[i].p_filesz;
112 while (addr < end) {
113 nhdr = (Elf_Nhdr *) addr;
114 addr += sizeof(Elf_Nhdr);
115 name = (const char *) addr;
116 addr += (nhdr->n_namesz+3) & ~3;
117 desc = (void *) addr;
118 addr += (nhdr->n_descsz+3) & ~3;
119
120 if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
121 && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
122 if (nhdr->n_type == EIN_PROGRAM_NAME) {
123 image_name = ob_calloc(1, nhdr->n_descsz + 1);
124 memcpy(image_name, desc, nhdr->n_descsz);
125 }
126 if (nhdr->n_type == EIN_PROGRAM_VERSION) {
127 image_version = ob_calloc(1, nhdr->n_descsz + 1);
128 memcpy(image_version, desc, nhdr->n_descsz);
129 }
130 if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
131 *sum_ptr = *(unsigned short *) desc;
132 debug("Image checksum: %#04x\n", *sum_ptr);
133 /* Where in the file */
134 retval = phdr[i].p_offset
135 + (unsigned long) desc - (unsigned long) buf;
136 }
137 }
138 }
139 }
140 out:
141 close_io(fd);
142 if (buf)
143 free(buf);
144 return retval;
145 }
146
load_segments(Elf_phdr * phdr,int phnum,unsigned long checksum_offset,unsigned int offset,unsigned long * bytes)147 static int load_segments(Elf_phdr *phdr, int phnum,
148 unsigned long checksum_offset,
149 unsigned int offset, unsigned long *bytes)
150 {
151 //unsigned int start_time, time;
152 int i;
153
154 *bytes = 0;
155 // start_time = currticks();
156 for (i = 0; i < phnum; i++) {
157 if (phdr[i].p_type != PT_LOAD)
158 continue;
159 debug("segment %d addr:" FMT_elf " file:" FMT_elf " mem:" FMT_elf " ",
160 i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz);
161 seek_io(fd, offset + phdr[i].p_offset);
162 debug("loading... ");
163 if ((size_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz)
164 != phdr[i].p_filesz) {
165 printf("Can't read program segment %d\n", i);
166 return 0;
167 }
168 bytes += phdr[i].p_filesz;
169 debug("clearing... ");
170 memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0,
171 phdr[i].p_memsz - phdr[i].p_filesz);
172 if (phdr[i].p_offset <= checksum_offset
173 && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
174 debug("clearing checksum... ");
175 memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset
176 - phdr[i].p_offset), 0, 2);
177 }
178 debug("ok\n");
179
180 }
181 // time = currticks() - start_time;
182 //debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
183 // time? bytes/time : 0);
184 debug("Loaded %lu bytes \n", *bytes);
185
186 return 1;
187 }
188
verify_image(Elf_ehdr * ehdr,Elf_phdr * phdr,int phnum,unsigned short image_sum)189 static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
190 unsigned short image_sum)
191 {
192 unsigned short sum, part_sum;
193 unsigned long offset;
194 int i;
195
196 sum = 0;
197 offset = 0;
198
199 part_sum = ipchksum(ehdr, sizeof *ehdr);
200 sum = add_ipchksums(offset, sum, part_sum);
201 offset += sizeof *ehdr;
202
203 part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
204 sum = add_ipchksums(offset, sum, part_sum);
205 offset += phnum * sizeof(*phdr);
206
207 for (i = 0; i < phnum; i++) {
208 if (phdr[i].p_type != PT_LOAD)
209 continue;
210 part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz);
211 sum = add_ipchksums(offset, sum, part_sum);
212 offset += phdr[i].p_memsz;
213 }
214
215 if (sum != image_sum) {
216 printf("Verify FAILED (image:%#04x vs computed:%#04x)\n",
217 image_sum, sum);
218 return 0;
219 }
220 return 1;
221 }
222
padded(unsigned s)223 static inline unsigned padded(unsigned s)
224 {
225 return (s + 3) & ~3;
226 }
227
add_boot_note(Elf_Bhdr * bhdr,const char * name,unsigned type,const char * desc,unsigned descsz)228 static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
229 unsigned type, const char *desc, unsigned descsz)
230 {
231 Elf_Nhdr nhdr;
232 unsigned ent_size, new_size, pad;
233 char *addr;
234
235 if (!bhdr)
236 return NULL;
237
238 nhdr.n_namesz = name? strlen(name)+1 : 0;
239 nhdr.n_descsz = descsz;
240 nhdr.n_type = type;
241 ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
242 if (bhdr->b_size + ent_size > 0xffff) {
243 printf("Boot notes too big\n");
244 free(bhdr);
245 return NULL;
246 }
247 if (bhdr->b_size + ent_size > bhdr->b_checksum) {
248 do {
249 new_size = bhdr->b_checksum * 2;
250 } while (new_size < bhdr->b_size + ent_size);
251 if (new_size > 0xffff)
252 new_size = 0xffff;
253 debug("expanding boot note size to %u\n", new_size);
254 #ifdef HAVE_REALLOC
255 bhdr = realloc(bhdr, new_size);
256 bhdr->b_checksum = new_size;
257 #else
258 printf("Boot notes too big\n");
259 free(bhdr);
260 return NULL;
261 #endif
262 }
263
264 addr = (char *) bhdr;
265 addr += bhdr->b_size;
266 memcpy(addr, &nhdr, sizeof(nhdr));
267 addr += sizeof(nhdr);
268
269 if (name && nhdr.n_namesz) {
270 memcpy(addr, name, nhdr.n_namesz);
271 addr += nhdr.n_namesz;
272 pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
273 memset(addr, 0, pad);
274 addr += pad;
275 }
276
277 memcpy(addr, desc, nhdr.n_descsz);
278 addr += nhdr.n_descsz;
279 pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
280 memset(addr, 0, pad);
281
282 bhdr->b_size += ent_size;
283 bhdr->b_records++;
284 return bhdr;
285 }
286
add_note_string(Elf_Bhdr * bhdr,const char * name,unsigned type,const char * desc)287 static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
288 unsigned type, const char *desc)
289 {
290 return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
291 }
292
build_boot_notes(struct sys_info * info,const char * cmdline)293 static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
294 {
295 Elf_Bhdr *bhdr;
296
297 bhdr = malloc(256);
298 bhdr->b_signature = ELF_BHDR_MAGIC;
299 bhdr->b_size = sizeof *bhdr;
300 bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
301 bhdr->b_records = 0;
302
303 if (info->firmware)
304 bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
305 bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
306 bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
307 if (cmdline)
308 bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
309 if (!bhdr)
310 return bhdr;
311 bhdr->b_checksum = 0;
312 bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
313 return bhdr;
314 }
315
316 int
is_elf(Elf_ehdr * ehdr)317 is_elf(Elf_ehdr *ehdr)
318 {
319 return (ehdr->e_ident[EI_MAG0] == ELFMAG0
320 && ehdr->e_ident[EI_MAG1] == ELFMAG1
321 && ehdr->e_ident[EI_MAG2] == ELFMAG2
322 && ehdr->e_ident[EI_MAG3] == ELFMAG3
323 && ehdr->e_ident[EI_CLASS] == ARCH_ELF_CLASS
324 && ehdr->e_ident[EI_DATA] == ARCH_ELF_DATA
325 && ehdr->e_ident[EI_VERSION] == EV_CURRENT
326 && ehdr->e_type == ET_EXEC
327 && ARCH_ELF_MACHINE_OK(ehdr->e_machine)
328 && ehdr->e_version == EV_CURRENT
329 && ehdr->e_phentsize == sizeof(Elf_phdr));
330 }
331
332 int
find_elf(Elf_ehdr * ehdr)333 find_elf(Elf_ehdr *ehdr)
334 {
335 int offset;
336
337 for (offset = 0; offset < MAX_HEADERS * BS; offset += BS) {
338 if ((size_t)read_io(fd, ehdr, sizeof ehdr) != sizeof ehdr) {
339 debug("Can't read ELF header\n");
340 return 0;
341 }
342
343 if (is_elf(ehdr)) {
344 debug("Found ELF header at offset %d\n", offset);
345 return offset;
346 }
347
348 seek_io(fd, offset);
349 }
350
351 debug("Not a bootable ELF image\n");
352 return 0;
353 }
354
355 Elf_phdr *
elf_readhdrs(int offset,Elf_ehdr * ehdr)356 elf_readhdrs(int offset, Elf_ehdr *ehdr)
357 {
358 unsigned long phdr_size;
359 Elf_phdr *phdr;
360
361 phdr_size = ehdr->e_phnum * sizeof(Elf_phdr);
362 phdr = malloc(phdr_size);
363 seek_io(fd, offset + ehdr->e_phoff);
364 if ((size_t)read_io(fd, phdr, phdr_size) != phdr_size) {
365 printf("Can't read program header\n");
366 return NULL;
367 }
368
369 return phdr;
370 }
371
372 int
elf_load(struct sys_info * info,ihandle_t dev,const char * cmdline,void ** boot_notes)373 elf_load(struct sys_info *info, ihandle_t dev, const char *cmdline, void **boot_notes)
374 {
375 Elf_ehdr ehdr;
376 Elf_phdr *phdr = NULL;
377 unsigned long checksum_offset, file_size;
378 unsigned short checksum = 0;
379 int retval = -1;
380 unsigned int offset;
381
382 image_name = image_version = NULL;
383
384 /* Mark the saved-program-state as invalid */
385 feval("0 state-valid !");
386
387 fd = open_ih(dev);
388 if (fd == -1) {
389 goto out;
390 }
391
392 offset = find_elf(&ehdr);
393 if (!offset) {
394 retval = LOADER_NOT_SUPPORT;
395 goto out;
396 }
397
398 #if DEBUG
399 printk("ELF header:\n");
400 printk(" ehdr.e_type = %d\n", (int)ehdr.e_type);
401 printk(" ehdr.e_machine = %d\n", (int)ehdr.e_machine);
402 printk(" ehdr.e_version = %d\n", (int)ehdr.e_version);
403 printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr.e_entry);
404 printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr.e_phoff);
405 printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr.e_shoff);
406 printk(" ehdr.e_flags = %d\n", (int)ehdr.e_flags);
407 printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr.e_ehsize);
408 printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr.e_phentsize);
409 printk(" ehdr.e_phnum = %d\n", (int)ehdr.e_phnum);
410 #endif
411
412 if (ehdr.e_phnum > MAX_HEADERS) {
413 printk ("elfload: too many program headers (MAX_HEADERS)\n");
414 retval = 0;
415 goto out;
416 }
417
418 phdr = elf_readhdrs(offset, &ehdr);
419 if (!phdr)
420 goto out;
421
422 if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
423 goto out;
424
425 checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset);
426
427 printf("Loading %s", image_name ? image_name : "image");
428 if (image_version)
429 printf(" version %s", image_version);
430 printf("...\n");
431
432 if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset, &file_size))
433 goto out;
434
435 if (checksum_offset) {
436 if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
437 goto out;
438 }
439
440 /* If we are attempting an ELF boot image, we pass a non-NULL pointer
441 into boot_notes and mark the image as elf-boot rather than standard
442 ELF */
443 if (boot_notes) {
444 *boot_notes = (void *)virt_to_phys(build_boot_notes(info, cmdline));
445 feval("elf-boot load-state >ls.file-type !");
446 PUSH((ucell)*boot_notes);
447 feval("elf-boot load-state >ls.param !");
448 } else {
449 feval("elf load-state >ls.file-type !");
450 }
451
452 //debug("current time: %lu\n", currticks());
453
454 debug("entry point is " FMT_elf "\n", addr_fixup(ehdr.e_entry));
455
456 // Initialise saved-program-state
457 PUSH(file_size);
458 feval("load-state >ls.file-size !");
459 feval("elf load-state >ls.file-type !");
460
461 out:
462 close_io(fd);
463 if (phdr)
464 free(phdr);
465 if (image_name)
466 free(image_name);
467 if (image_version)
468 free(image_version);
469 return retval;
470 }
471
472 void
elf_init_program(void)473 elf_init_program(void)
474 {
475 char *base;
476 int i;
477 Elf_ehdr *ehdr;
478 Elf_phdr *phdr;
479 size_t size, total_size = 0;
480 char *addr;
481 uintptr_t tmp;
482
483 /* TODO: manage ELF notes section */
484 feval("load-base");
485 base = (char*)cell2pointer(POP());
486
487 ehdr = (Elf_ehdr *)base;
488
489 if (!is_elf(ehdr)) {
490 debug("Not a valid ELF memory image\n");
491 return;
492 }
493
494 phdr = (Elf_phdr *)(base + ehdr->e_phoff);
495
496 for (i = 0; i < ehdr->e_phnum; i++) {
497
498 #if DEBUG
499 debug("filesz: %08lX memsz: %08lX p_offset: %08lX "
500 "p_vaddr %08lX\n",
501 (unsigned long)phdr[i].p_filesz, (unsigned long)phdr[i].p_memsz,
502 (unsigned long)phdr[i].p_offset, (unsigned long)phdr[i].p_vaddr );
503 #endif
504
505 size = MIN(phdr[i].p_filesz, phdr[i].p_memsz);
506 if (!size)
507 continue;
508 #if !defined(CONFIG_SPARC32) && !defined(CONFIG_X86)
509 if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 ) {
510 printk("Ignoring failed claim for va %lx memsz %lx!\n",
511 (unsigned long)phdr[i].p_vaddr,
512 (unsigned long)phdr[i].p_memsz);
513 }
514 #endif
515 /* Workaround for archs where sizeof(int) != pointer size */
516 tmp = phdr[i].p_vaddr;
517 addr = (char *)tmp;
518
519 memcpy(addr, base + phdr[i].p_offset, size);
520
521 total_size += size;
522
523 #ifdef CONFIG_PPC
524 flush_icache_range( addr, addr + size );
525 #endif
526 }
527
528 // Initialise load-state
529 PUSH(ehdr->e_entry);
530 feval("load-state >ls.entry !");
531
532 arch_init_program();
533
534 feval("-1 state-valid !");
535 }
536