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