sparc32: move /uuid property setup to openbios.c
[openbios.git] / libopenbios / elf_load.c
blob4ea08a7953a5c93f6a420e69a333fd58ee1bc295
1 /* ELF Boot loader
2 * As we have seek, this implementation can be straightforward.
3 * 2003-07 by SONE Takeshi
4 */
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 #define printf printk
15 #define debug printk
17 #define DEBUG 0
19 #define MAX_HEADERS 0x20
20 #define BS 0x100 /* smallest step used when looking for the ELF header */
22 #ifdef CONFIG_PPC
23 extern void flush_icache_range( char *start, char *stop );
24 #endif
26 /* FreeBSD and possibly others mask the high 8 bits */
27 #define addr_fixup(addr) ((addr) & 0x00ffffff)
29 static char *image_name, *image_version;
30 static int fd;
32 /* Note: avoid name collision with platforms which have their own version of calloc() */
33 static void *ob_calloc(size_t nmemb, size_t size)
35 size_t alloc_size = nmemb * size;
36 void *mem;
38 if (alloc_size < nmemb || alloc_size < size) {
39 printf("calloc overflow: %u, %u\n", nmemb, size);
40 return NULL;
43 mem = malloc(alloc_size);
44 memset(mem, 0, alloc_size);
46 return mem;
49 static int check_mem_ranges(struct sys_info *info,
50 Elf_phdr *phdr, int phnum)
52 int i, j;
53 unsigned long start, end;
54 unsigned long prog_start, prog_end;
55 struct memrange *mem;
57 prog_start = virt_to_phys(&_start);
58 prog_end = virt_to_phys(&_end);
60 for (i = 0; i < phnum; i++) {
61 if (phdr[i].p_type != PT_LOAD)
62 continue;
63 start = addr_fixup(phdr[i].p_paddr);
64 end = start + phdr[i].p_memsz;
65 if (start < prog_start && end > prog_start)
66 goto conflict;
67 if (start < prog_end && end > prog_end)
68 goto conflict;
69 mem=info->memrange;
70 for (j = 0; j < info->n_memranges; j++) {
71 if (mem[j].base <= start && mem[j].base + mem[j].size >= end)
72 break;
74 if (j >= info->n_memranges)
75 goto badseg;
77 return 1;
79 conflict:
80 printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end);
82 badseg:
83 printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1);
84 return 0;
87 static unsigned long process_image_notes(Elf_phdr *phdr, int phnum,
88 unsigned short *sum_ptr,
89 unsigned int offset)
91 int i;
92 char *buf = NULL;
93 int retval = 0;
94 unsigned long addr, end;
95 Elf_Nhdr *nhdr;
96 const char *name;
97 void *desc;
99 for (i = 0; i < phnum; i++) {
100 if (phdr[i].p_type != PT_NOTE)
101 continue;
102 buf = malloc(phdr[i].p_filesz);
103 seek_io(fd, offset + phdr[i].p_offset);
104 if ((size_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) {
105 printf("Can't read note segment\n");
106 goto out;
108 addr = (unsigned long) buf;
109 end = addr + phdr[i].p_filesz;
110 while (addr < end) {
111 nhdr = (Elf_Nhdr *) addr;
112 addr += sizeof(Elf_Nhdr);
113 name = (const char *) addr;
114 addr += (nhdr->n_namesz+3) & ~3;
115 desc = (void *) addr;
116 addr += (nhdr->n_descsz+3) & ~3;
118 if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT)
119 && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) {
120 if (nhdr->n_type == EIN_PROGRAM_NAME) {
121 image_name = ob_calloc(1, nhdr->n_descsz + 1);
122 memcpy(image_name, desc, nhdr->n_descsz);
124 if (nhdr->n_type == EIN_PROGRAM_VERSION) {
125 image_version = ob_calloc(1, nhdr->n_descsz + 1);
126 memcpy(image_version, desc, nhdr->n_descsz);
128 if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) {
129 *sum_ptr = *(unsigned short *) desc;
130 debug("Image checksum: %#04x\n", *sum_ptr);
131 /* Where in the file */
132 retval = phdr[i].p_offset
133 + (unsigned long) desc - (unsigned long) buf;
138 out:
139 close_io(fd);
140 if (buf)
141 free(buf);
142 return retval;
145 static int load_segments(Elf_phdr *phdr, int phnum,
146 unsigned long checksum_offset,
147 unsigned int offset, unsigned long *bytes)
149 //unsigned int start_time, time;
150 int i;
152 *bytes = 0;
153 // start_time = currticks();
154 for (i = 0; i < phnum; i++) {
155 if (phdr[i].p_type != PT_LOAD)
156 continue;
157 debug("segment %d addr:" FMT_elf " file:" FMT_elf " mem:" FMT_elf " ",
158 i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz);
159 seek_io(fd, offset + phdr[i].p_offset);
160 debug("loading... ");
161 if ((size_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz)
162 != phdr[i].p_filesz) {
163 printf("Can't read program segment %d\n", i);
164 return 0;
166 bytes += phdr[i].p_filesz;
167 debug("clearing... ");
168 memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0,
169 phdr[i].p_memsz - phdr[i].p_filesz);
170 if (phdr[i].p_offset <= checksum_offset
171 && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) {
172 debug("clearing checksum... ");
173 memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset
174 - phdr[i].p_offset), 0, 2);
176 debug("ok\n");
179 // time = currticks() - start_time;
180 //debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time,
181 // time? bytes/time : 0);
182 debug("Loaded %lu bytes \n", *bytes);
184 return 1;
187 static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum,
188 unsigned short image_sum)
190 unsigned short sum, part_sum;
191 unsigned long offset;
192 int i;
194 sum = 0;
195 offset = 0;
197 part_sum = ipchksum(ehdr, sizeof *ehdr);
198 sum = add_ipchksums(offset, sum, part_sum);
199 offset += sizeof *ehdr;
201 part_sum = ipchksum(phdr, phnum * sizeof(*phdr));
202 sum = add_ipchksums(offset, sum, part_sum);
203 offset += phnum * sizeof(*phdr);
205 for (i = 0; i < phnum; i++) {
206 if (phdr[i].p_type != PT_LOAD)
207 continue;
208 part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz);
209 sum = add_ipchksums(offset, sum, part_sum);
210 offset += phdr[i].p_memsz;
213 if (sum != image_sum) {
214 printf("Verify FAILED (image:%#04x vs computed:%#04x)\n",
215 image_sum, sum);
216 return 0;
218 return 1;
221 static inline unsigned padded(unsigned s)
223 return (s + 3) & ~3;
226 static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name,
227 unsigned type, const char *desc, unsigned descsz)
229 Elf_Nhdr nhdr;
230 unsigned ent_size, new_size, pad;
231 char *addr;
233 if (!bhdr)
234 return NULL;
236 nhdr.n_namesz = name? strlen(name)+1 : 0;
237 nhdr.n_descsz = descsz;
238 nhdr.n_type = type;
239 ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz);
240 if (bhdr->b_size + ent_size > 0xffff) {
241 printf("Boot notes too big\n");
242 free(bhdr);
243 return NULL;
245 if (bhdr->b_size + ent_size > bhdr->b_checksum) {
246 do {
247 new_size = bhdr->b_checksum * 2;
248 } while (new_size < bhdr->b_size + ent_size);
249 if (new_size > 0xffff)
250 new_size = 0xffff;
251 debug("expanding boot note size to %u\n", new_size);
252 #ifdef HAVE_REALLOC
253 bhdr = realloc(bhdr, new_size);
254 bhdr->b_checksum = new_size;
255 #else
256 printf("Boot notes too big\n");
257 free(bhdr);
258 return NULL;
259 #endif
262 addr = (char *) bhdr;
263 addr += bhdr->b_size;
264 memcpy(addr, &nhdr, sizeof(nhdr));
265 addr += sizeof(nhdr);
267 if (name && nhdr.n_namesz) {
268 memcpy(addr, name, nhdr.n_namesz);
269 addr += nhdr.n_namesz;
270 pad = padded(nhdr.n_namesz) - nhdr.n_namesz;
271 memset(addr, 0, pad);
272 addr += pad;
275 memcpy(addr, desc, nhdr.n_descsz);
276 addr += nhdr.n_descsz;
277 pad = padded(nhdr.n_descsz) - nhdr.n_descsz;
278 memset(addr, 0, pad);
280 bhdr->b_size += ent_size;
281 bhdr->b_records++;
282 return bhdr;
285 static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name,
286 unsigned type, const char *desc)
288 return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1);
291 static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline)
293 Elf_Bhdr *bhdr;
295 bhdr = malloc(256);
296 bhdr->b_signature = ELF_BHDR_MAGIC;
297 bhdr->b_size = sizeof *bhdr;
298 bhdr->b_checksum = 256; /* XXX cache the current buffer size here */
299 bhdr->b_records = 0;
301 if (info->firmware)
302 bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware);
303 bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name);
304 bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version);
305 if (cmdline)
306 bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline);
307 if (!bhdr)
308 return bhdr;
309 bhdr->b_checksum = 0;
310 bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size);
311 return bhdr;
315 is_elf(Elf_ehdr *ehdr)
317 return (ehdr->e_ident[EI_MAG0] == ELFMAG0
318 && ehdr->e_ident[EI_MAG1] == ELFMAG1
319 && ehdr->e_ident[EI_MAG2] == ELFMAG2
320 && ehdr->e_ident[EI_MAG3] == ELFMAG3
321 && ehdr->e_ident[EI_CLASS] == ARCH_ELF_CLASS
322 && ehdr->e_ident[EI_DATA] == ARCH_ELF_DATA
323 && ehdr->e_ident[EI_VERSION] == EV_CURRENT
324 && ehdr->e_type == ET_EXEC
325 && ARCH_ELF_MACHINE_OK(ehdr->e_machine)
326 && ehdr->e_version == EV_CURRENT
327 && ehdr->e_phentsize == sizeof(Elf_phdr));
331 find_elf(Elf_ehdr *ehdr)
333 int offset;
335 for (offset = 0; offset < MAX_HEADERS * BS; offset += BS) {
336 if ((size_t)read_io(fd, ehdr, sizeof ehdr) != sizeof ehdr) {
337 debug("Can't read ELF header\n");
338 return 0;
341 if (is_elf(ehdr)) {
342 debug("Found ELF header at offset %d\n", offset);
343 return offset;
346 seek_io(fd, offset);
349 debug("Not a bootable ELF image\n");
350 return 0;
353 Elf_phdr *
354 elf_readhdrs(int offset, Elf_ehdr *ehdr)
356 unsigned long phdr_size;
357 Elf_phdr *phdr;
359 phdr_size = ehdr->e_phnum * sizeof(Elf_phdr);
360 phdr = malloc(phdr_size);
361 seek_io(fd, offset + ehdr->e_phoff);
362 if ((size_t)read_io(fd, phdr, phdr_size) != phdr_size) {
363 printf("Can't read program header\n");
364 return NULL;
367 return phdr;
370 int
371 elf_load(struct sys_info *info, ihandle_t dev, const char *cmdline, void **boot_notes)
373 Elf_ehdr ehdr;
374 Elf_phdr *phdr = NULL;
375 unsigned long checksum_offset, file_size;
376 unsigned short checksum = 0;
377 int retval = -1;
378 unsigned int offset;
380 image_name = image_version = NULL;
382 /* Mark the saved-program-state as invalid */
383 feval("0 state-valid !");
385 fd = open_ih(dev);
386 if (fd == -1) {
387 goto out;
390 offset = find_elf(&ehdr);
391 if (!offset) {
392 retval = LOADER_NOT_SUPPORT;
393 goto out;
396 #if DEBUG
397 printk("ELF header:\n");
398 printk(" ehdr.e_type = %d\n", (int)ehdr.e_type);
399 printk(" ehdr.e_machine = %d\n", (int)ehdr.e_machine);
400 printk(" ehdr.e_version = %d\n", (int)ehdr.e_version);
401 printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr.e_entry);
402 printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr.e_phoff);
403 printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr.e_shoff);
404 printk(" ehdr.e_flags = %d\n", (int)ehdr.e_flags);
405 printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr.e_ehsize);
406 printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr.e_phentsize);
407 printk(" ehdr.e_phnum = %d\n", (int)ehdr.e_phnum);
408 #endif
410 if (ehdr.e_phnum > MAX_HEADERS) {
411 printk ("elfload: too many program headers (MAX_HEADERS)\n");
412 retval = 0;
413 goto out;
416 phdr = elf_readhdrs(offset, &ehdr);
417 if (!phdr)
418 goto out;
420 if (!check_mem_ranges(info, phdr, ehdr.e_phnum))
421 goto out;
423 checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset);
425 printf("Loading %s", image_name ? image_name : "image");
426 if (image_version)
427 printf(" version %s", image_version);
428 printf("...\n");
430 if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset, &file_size))
431 goto out;
433 if (checksum_offset) {
434 if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum))
435 goto out;
438 /* If we are attempting an ELF boot image, we pass a non-NULL pointer
439 into boot_notes and mark the image as elf-boot rather than standard
440 ELF */
441 if (boot_notes) {
442 *boot_notes = (void *)virt_to_phys(build_boot_notes(info, cmdline));
443 feval("elf-boot saved-program-state >sps.file-type !");
444 } else {
445 feval("elf saved-program-state >sps.file-type !");
448 //debug("current time: %lu\n", currticks());
450 debug("entry point is " FMT_elf "\n", addr_fixup(ehdr.e_entry));
452 // Initialise saved-program-state
453 PUSH(addr_fixup(ehdr.e_entry));
454 feval("saved-program-state >sps.entry !");
455 PUSH(file_size);
456 feval("saved-program-state >sps.file-size !");
458 feval("-1 state-valid !");
460 out:
461 close_io(fd);
462 if (phdr)
463 free(phdr);
464 if (image_name)
465 free(image_name);
466 if (image_version)
467 free(image_version);
468 return retval;
471 void
472 elf_init_program(void)
474 char *base;
475 int i;
476 Elf_ehdr *ehdr;
477 Elf_phdr *phdr;
478 size_t size, total_size = 0;
479 char *addr;
480 cell tmp;
482 /* TODO: manage ELF notes section */
483 feval("0 state-valid !");
484 feval("load-base");
485 base = (char*)POP();
487 ehdr = (Elf_ehdr *)base;
489 if (!is_elf(ehdr)) {
490 debug("Not a valid ELF memory image\n");
491 return;
494 phdr = (Elf_phdr *)(base + ehdr->e_phoff);
496 for (i = 0; i < ehdr->e_phnum; i++) {
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
505 size = MIN(phdr[i].p_filesz, phdr[i].p_memsz);
506 if (!size)
507 continue;
508 #if 0
509 if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 ) {
510 printk("Claim failed!\n");
511 return;
513 #endif
514 /* Workaround for archs where sizeof(int) != pointer size */
515 tmp = phdr[i].p_vaddr;
516 addr = (char *)tmp;
518 memcpy(addr, base + phdr[i].p_offset, size);
520 total_size += size;
522 #ifdef CONFIG_PPC
523 flush_icache_range( addr, addr + size );
524 #endif
527 // Initialise saved-program-state
528 PUSH(ehdr->e_entry);
529 feval("saved-program-state >sps.entry !");
530 PUSH(total_size);
531 feval("saved-program-state >sps.file-size !");
532 feval("elf saved-program-state >sps.file-type !");
534 feval("-1 state-valid !");