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