2 * As we have seek, this implementation can be straightforward.
3 * 2003-07 by SONE Takeshi
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"
19 #define MAX_HEADERS 0x20
20 #define BS 0x100 /* smallest step used when looking for the ELF header */
23 extern void flush_icache_range( char *start
, char *stop
);
26 /* FreeBSD and possibly others mask the high 8 bits */
27 #define addr_fixup(addr) ((addr) & 0x00ffffff)
29 static char *image_name
, *image_version
;
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
;
38 if (alloc_size
< nmemb
|| alloc_size
< size
) {
39 printf("calloc overflow: %u, %u\n", nmemb
, size
);
43 mem
= malloc(alloc_size
);
44 memset(mem
, 0, alloc_size
);
49 static int check_mem_ranges(struct sys_info
*info
,
50 Elf_phdr
*phdr
, int phnum
)
53 unsigned long start
, end
;
54 unsigned long prog_start
, prog_end
;
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
)
63 start
= addr_fixup(phdr
[i
].p_paddr
);
64 end
= start
+ phdr
[i
].p_memsz
;
65 if (start
< prog_start
&& end
> prog_start
)
67 if (start
< prog_end
&& end
> prog_end
)
70 for (j
= 0; j
< info
->n_memranges
; j
++) {
71 if (mem
[j
].base
<= start
&& mem
[j
].base
+ mem
[j
].size
>= end
)
74 if (j
>= info
->n_memranges
)
80 printf("%s occupies [%#lx-%#lx]\n", program_name
, prog_start
, prog_end
);
83 printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i
, start
, end
-1);
87 static unsigned long process_image_notes(Elf_phdr
*phdr
, int phnum
,
88 unsigned short *sum_ptr
,
94 unsigned long addr
, end
;
99 for (i
= 0; i
< phnum
; i
++) {
100 if (phdr
[i
].p_type
!= PT_NOTE
)
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");
108 addr
= (unsigned long) buf
;
109 end
= addr
+ phdr
[i
].p_filesz
;
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
;
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;
153 // start_time = currticks();
154 for (i
= 0; i
< phnum
; i
++) {
155 if (phdr
[i
].p_type
!= PT_LOAD
)
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
);
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);
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
);
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
;
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
)
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",
221 static inline unsigned padded(unsigned s
)
226 static Elf_Bhdr
*add_boot_note(Elf_Bhdr
*bhdr
, const char *name
,
227 unsigned type
, const char *desc
, unsigned descsz
)
230 unsigned ent_size
, new_size
, pad
;
236 nhdr
.n_namesz
= name
? strlen(name
)+1 : 0;
237 nhdr
.n_descsz
= descsz
;
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");
245 if (bhdr
->b_size
+ ent_size
> bhdr
->b_checksum
) {
247 new_size
= bhdr
->b_checksum
* 2;
248 } while (new_size
< bhdr
->b_size
+ ent_size
);
249 if (new_size
> 0xffff)
251 debug("expanding boot note size to %u\n", new_size
);
253 bhdr
= realloc(bhdr
, new_size
);
254 bhdr
->b_checksum
= new_size
;
256 printf("Boot notes too big\n");
262 addr
= (char *) bhdr
;
263 addr
+= bhdr
->b_size
;
264 memcpy(addr
, &nhdr
, sizeof(nhdr
));
265 addr
+= sizeof(nhdr
);
267 memcpy(addr
, name
, nhdr
.n_namesz
);
268 addr
+= nhdr
.n_namesz
;
269 pad
= padded(nhdr
.n_namesz
) - nhdr
.n_namesz
;
270 memset(addr
, 0, pad
);
273 memcpy(addr
, desc
, nhdr
.n_descsz
);
274 addr
+= nhdr
.n_descsz
;
275 pad
= padded(nhdr
.n_descsz
) - nhdr
.n_descsz
;
276 memset(addr
, 0, pad
);
278 bhdr
->b_size
+= ent_size
;
283 static inline Elf_Bhdr
*add_note_string(Elf_Bhdr
*bhdr
, const char *name
,
284 unsigned type
, const char *desc
)
286 return add_boot_note(bhdr
, name
, type
, desc
, strlen(desc
) + 1);
289 static Elf_Bhdr
*build_boot_notes(struct sys_info
*info
, const char *cmdline
)
294 bhdr
->b_signature
= ELF_BHDR_MAGIC
;
295 bhdr
->b_size
= sizeof *bhdr
;
296 bhdr
->b_checksum
= 256; /* XXX cache the current buffer size here */
300 bhdr
= add_note_string(bhdr
, NULL
, EBN_FIRMWARE_TYPE
, info
->firmware
);
301 bhdr
= add_note_string(bhdr
, NULL
, EBN_BOOTLOADER_NAME
, program_name
);
302 bhdr
= add_note_string(bhdr
, NULL
, EBN_BOOTLOADER_VERSION
, program_version
);
304 bhdr
= add_note_string(bhdr
, NULL
, EBN_COMMAND_LINE
, cmdline
);
307 bhdr
->b_checksum
= 0;
308 bhdr
->b_checksum
= ipchksum(bhdr
, bhdr
->b_size
);
313 is_elf(Elf_ehdr
*ehdr
)
315 return (ehdr
->e_ident
[EI_MAG0
] == ELFMAG0
316 && ehdr
->e_ident
[EI_MAG1
] == ELFMAG1
317 && ehdr
->e_ident
[EI_MAG2
] == ELFMAG2
318 && ehdr
->e_ident
[EI_MAG3
] == ELFMAG3
319 && ehdr
->e_ident
[EI_CLASS
] == ARCH_ELF_CLASS
320 && ehdr
->e_ident
[EI_DATA
] == ARCH_ELF_DATA
321 && ehdr
->e_ident
[EI_VERSION
] == EV_CURRENT
322 && ehdr
->e_type
== ET_EXEC
323 && ARCH_ELF_MACHINE_OK(ehdr
->e_machine
)
324 && ehdr
->e_version
== EV_CURRENT
325 && ehdr
->e_phentsize
== sizeof(Elf_phdr
));
329 find_elf(Elf_ehdr
*ehdr
)
333 for (offset
= 0; offset
< MAX_HEADERS
* BS
; offset
+= BS
) {
334 if ((size_t)read_io(fd
, ehdr
, sizeof ehdr
) != sizeof ehdr
) {
335 debug("Can't read ELF header\n");
340 debug("Found ELF header at offset %d\n", offset
);
347 debug("Not a bootable ELF image\n");
352 elf_readhdrs(int offset
, Elf_ehdr
*ehdr
)
354 unsigned long phdr_size
;
357 phdr_size
= ehdr
->e_phnum
* sizeof(Elf_phdr
);
358 phdr
= malloc(phdr_size
);
359 seek_io(fd
, offset
+ ehdr
->e_phoff
);
360 if ((size_t)read_io(fd
, phdr
, phdr_size
) != phdr_size
) {
361 printf("Can't read program header\n");
369 elf_load(struct sys_info
*info
, const char *filename
, const char *cmdline
, void **boot_notes
)
372 Elf_phdr
*phdr
= NULL
;
373 unsigned long checksum_offset
, file_size
;
374 unsigned short checksum
= 0;
378 image_name
= image_version
= NULL
;
380 /* Mark the saved-program-state as invalid */
381 feval("0 state-valid !");
383 fd
= open_io(filename
);
387 offset
= find_elf(&ehdr
);
389 retval
= LOADER_NOT_SUPPORT
;
394 printk("ELF header:\n");
395 printk(" ehdr.e_type = %d\n", (int)ehdr
.e_type
);
396 printk(" ehdr.e_machine = %d\n", (int)ehdr
.e_machine
);
397 printk(" ehdr.e_version = %d\n", (int)ehdr
.e_version
);
398 printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr
.e_entry
);
399 printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr
.e_phoff
);
400 printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr
.e_shoff
);
401 printk(" ehdr.e_flags = %d\n", (int)ehdr
.e_flags
);
402 printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr
.e_ehsize
);
403 printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr
.e_phentsize
);
404 printk(" ehdr.e_phnum = %d\n", (int)ehdr
.e_phnum
);
407 if (ehdr
.e_phnum
> MAX_HEADERS
) {
408 printk ("elfload: too many program headers (MAX_HEADERS)\n");
413 phdr
= elf_readhdrs(offset
, &ehdr
);
417 if (!check_mem_ranges(info
, phdr
, ehdr
.e_phnum
))
420 checksum_offset
= process_image_notes(phdr
, ehdr
.e_phnum
, &checksum
, offset
);
422 printf("Loading %s", image_name
? image_name
: "image");
424 printf(" version %s", image_version
);
427 if (!load_segments(phdr
, ehdr
.e_phnum
, checksum_offset
, offset
, &file_size
))
430 if (checksum_offset
) {
431 if (!verify_image(&ehdr
, phdr
, ehdr
.e_phnum
, checksum
))
435 /* If we are attempting an ELF boot image, we pass a non-NULL pointer
436 into boot_notes and mark the image as elf-boot rather than standard
439 *boot_notes
= (void *)virt_to_phys(build_boot_notes(info
, cmdline
));
440 feval("elf-boot saved-program-state >sps.file-type !");
442 feval("elf saved-program-state >sps.file-type !");
445 //debug("current time: %lu\n", currticks());
447 debug("entry point is " FMT_elf
"\n", addr_fixup(ehdr
.e_entry
));
449 // Initialise saved-program-state
450 PUSH(addr_fixup(ehdr
.e_entry
));
451 feval("saved-program-state >sps.entry !");
453 feval("saved-program-state >sps.file-size !");
455 feval("-1 state-valid !");
469 elf_init_program(void)
475 size_t size
, total_size
= 0;
479 /* TODO: manage ELF notes section */
480 feval("0 state-valid !");
484 ehdr
= (Elf_ehdr
*)base
;
487 debug("Not a valid ELF memory image\n");
491 phdr
= (Elf_phdr
*)(base
+ ehdr
->e_phoff
);
493 for (i
= 0; i
< ehdr
->e_phnum
; i
++) {
496 debug("filesz: %08lX memsz: %08lX p_offset: %08lX "
498 (ulong
)phdr
[i
].p_filesz
, (ulong
)phdr
[i
].p_memsz
,
499 (ulong
)phdr
[i
].p_offset
, (ulong
)phdr
[i
].p_vaddr
);
502 size
= MIN(phdr
[i
].p_filesz
, phdr
[i
].p_memsz
);
506 if( ofmem_claim( phdr
[i
].p_vaddr
, phdr
[i
].p_memsz
, 0 ) == -1 ) {
507 printk("Claim failed!\n");
511 /* Workaround for archs where sizeof(int) != pointer size */
512 tmp
= phdr
[i
].p_vaddr
;
515 memcpy(addr
, base
+ phdr
[i
].p_offset
, size
);
520 flush_icache_range( addr
, addr
+ size
);
524 // Initialise saved-program-state
526 feval("saved-program-state >sps.entry !");
528 feval("saved-program-state >sps.file-size !");
529 feval("elf saved-program-state >sps.file-type !");
531 feval("-1 state-valid !");