diff options
Diffstat (limited to 'qemu/roms/openbios/libopenbios/elf_load.c')
-rw-r--r-- | qemu/roms/openbios/libopenbios/elf_load.c | 537 |
1 files changed, 0 insertions, 537 deletions
diff --git a/qemu/roms/openbios/libopenbios/elf_load.c b/qemu/roms/openbios/libopenbios/elf_load.c deleted file mode 100644 index 9c7850e11..000000000 --- a/qemu/roms/openbios/libopenbios/elf_load.c +++ /dev/null @@ -1,537 +0,0 @@ -/* ELF Boot loader - * As we have seek, this implementation can be straightforward. - * 2003-07 by SONE Takeshi - */ - -#include "config.h" -#include "kernel/kernel.h" -#include "libc/diskio.h" -#include "arch/common/elf_boot.h" -#include "libopenbios/elf_load.h" -#include "libopenbios/sys_info.h" -#include "libopenbios/ipchecksum.h" -#include "libopenbios/bindings.h" -#include "libopenbios/ofmem.h" -#define printf printk -#define debug printk - -#define DEBUG 0 - -#define MAX_HEADERS 0x20 -#define BS 0x100 /* smallest step used when looking for the ELF header */ - -#ifdef CONFIG_PPC -extern void flush_icache_range( char *start, char *stop ); -#endif - -/* FreeBSD and possibly others mask the high 8 bits */ -#define addr_fixup(addr) ((addr) & 0x00ffffff) - -static char *image_name, *image_version; -static int fd; - -/* Note: avoid name collision with platforms which have their own version of calloc() */ -static void *ob_calloc(size_t nmemb, size_t size) -{ - size_t alloc_size = nmemb * size; - void *mem; - - if (alloc_size < nmemb || alloc_size < size) { - printf("calloc overflow: %u, %u\n", nmemb, size); - return NULL; - } - - mem = malloc(alloc_size); - memset(mem, 0, alloc_size); - - return mem; -} - -static int check_mem_ranges(struct sys_info *info, - Elf_phdr *phdr, int phnum) -{ - int i, j; - unsigned long start, end; - unsigned long prog_start, prog_end; - struct memrange *mem; - - prog_start = virt_to_phys(&_start); - prog_end = virt_to_phys(&_end); - - for (i = 0; i < phnum; i++) { - if (phdr[i].p_type != PT_LOAD) - continue; - start = addr_fixup(phdr[i].p_paddr); - end = start + phdr[i].p_memsz; - if (start < prog_start && end > prog_start) - goto conflict; - if (start < prog_end && end > prog_end) - goto conflict; - mem=info->memrange; - for (j = 0; j < info->n_memranges; j++) { - if (mem[j].base <= start && mem[j].base + mem[j].size >= end) - break; - } - if (j >= info->n_memranges) - goto badseg; - } - return 1; - -conflict: - printf("%s occupies [%#lx-%#lx]\n", program_name, prog_start, prog_end); - -badseg: - printf("Segment %d [%#lx-%#lx] doesn't fit into memory\n", i, start, end-1); - return 0; -} - -static unsigned long process_image_notes(Elf_phdr *phdr, int phnum, - unsigned short *sum_ptr, - unsigned int offset) -{ - int i; - char *buf = NULL; - int retval = 0; - unsigned long addr, end; - Elf_Nhdr *nhdr; - const char *name; - void *desc; - - for (i = 0; i < phnum; i++) { - if (phdr[i].p_type != PT_NOTE) - continue; - buf = malloc(phdr[i].p_filesz); - seek_io(fd, offset + phdr[i].p_offset); - if ((size_t)read_io(fd, buf, phdr[i].p_filesz) != phdr[i].p_filesz) { - printf("Can't read note segment\n"); - goto out; - } - addr = (unsigned long) buf; - end = addr + phdr[i].p_filesz; - while (addr < end) { - nhdr = (Elf_Nhdr *) addr; - addr += sizeof(Elf_Nhdr); - name = (const char *) addr; - addr += (nhdr->n_namesz+3) & ~3; - desc = (void *) addr; - addr += (nhdr->n_descsz+3) & ~3; - - if (nhdr->n_namesz==sizeof(ELF_NOTE_BOOT) - && memcmp(name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT))==0) { - if (nhdr->n_type == EIN_PROGRAM_NAME) { - image_name = ob_calloc(1, nhdr->n_descsz + 1); - memcpy(image_name, desc, nhdr->n_descsz); - } - if (nhdr->n_type == EIN_PROGRAM_VERSION) { - image_version = ob_calloc(1, nhdr->n_descsz + 1); - memcpy(image_version, desc, nhdr->n_descsz); - } - if (nhdr->n_type == EIN_PROGRAM_CHECKSUM) { - *sum_ptr = *(unsigned short *) desc; - debug("Image checksum: %#04x\n", *sum_ptr); - /* Where in the file */ - retval = phdr[i].p_offset - + (unsigned long) desc - (unsigned long) buf; - } - } - } - } -out: - close_io(fd); - if (buf) - free(buf); - return retval; -} - -static int load_segments(Elf_phdr *phdr, int phnum, - unsigned long checksum_offset, - unsigned int offset, unsigned long *bytes) -{ - //unsigned int start_time, time; - int i; - - *bytes = 0; - // start_time = currticks(); - for (i = 0; i < phnum; i++) { - if (phdr[i].p_type != PT_LOAD) - continue; - debug("segment %d addr:" FMT_elf " file:" FMT_elf " mem:" FMT_elf " ", - i, addr_fixup(phdr[i].p_paddr), phdr[i].p_filesz, phdr[i].p_memsz); - seek_io(fd, offset + phdr[i].p_offset); - debug("loading... "); - if ((size_t)read_io(fd, phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_filesz) - != phdr[i].p_filesz) { - printf("Can't read program segment %d\n", i); - return 0; - } - bytes += phdr[i].p_filesz; - debug("clearing... "); - memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + phdr[i].p_filesz), 0, - phdr[i].p_memsz - phdr[i].p_filesz); - if (phdr[i].p_offset <= checksum_offset - && phdr[i].p_offset + phdr[i].p_filesz >= checksum_offset+2) { - debug("clearing checksum... "); - memset(phys_to_virt(addr_fixup(phdr[i].p_paddr) + checksum_offset - - phdr[i].p_offset), 0, 2); - } - debug("ok\n"); - - } - // time = currticks() - start_time; - //debug("Loaded %lu bytes in %ums (%luKB/s)\n", bytes, time, - // time? bytes/time : 0); - debug("Loaded %lu bytes \n", *bytes); - - return 1; -} - -static int verify_image(Elf_ehdr *ehdr, Elf_phdr *phdr, int phnum, - unsigned short image_sum) -{ - unsigned short sum, part_sum; - unsigned long offset; - int i; - - sum = 0; - offset = 0; - - part_sum = ipchksum(ehdr, sizeof *ehdr); - sum = add_ipchksums(offset, sum, part_sum); - offset += sizeof *ehdr; - - part_sum = ipchksum(phdr, phnum * sizeof(*phdr)); - sum = add_ipchksums(offset, sum, part_sum); - offset += phnum * sizeof(*phdr); - - for (i = 0; i < phnum; i++) { - if (phdr[i].p_type != PT_LOAD) - continue; - part_sum = ipchksum(phys_to_virt(addr_fixup(phdr[i].p_paddr)), phdr[i].p_memsz); - sum = add_ipchksums(offset, sum, part_sum); - offset += phdr[i].p_memsz; - } - - if (sum != image_sum) { - printf("Verify FAILED (image:%#04x vs computed:%#04x)\n", - image_sum, sum); - return 0; - } - return 1; -} - -static inline unsigned padded(unsigned s) -{ - return (s + 3) & ~3; -} - -static Elf_Bhdr *add_boot_note(Elf_Bhdr *bhdr, const char *name, - unsigned type, const char *desc, unsigned descsz) -{ - Elf_Nhdr nhdr; - unsigned ent_size, new_size, pad; - char *addr; - - if (!bhdr) - return NULL; - - nhdr.n_namesz = name? strlen(name)+1 : 0; - nhdr.n_descsz = descsz; - nhdr.n_type = type; - ent_size = sizeof(nhdr) + padded(nhdr.n_namesz) + padded(nhdr.n_descsz); - if (bhdr->b_size + ent_size > 0xffff) { - printf("Boot notes too big\n"); - free(bhdr); - return NULL; - } - if (bhdr->b_size + ent_size > bhdr->b_checksum) { - do { - new_size = bhdr->b_checksum * 2; - } while (new_size < bhdr->b_size + ent_size); - if (new_size > 0xffff) - new_size = 0xffff; - debug("expanding boot note size to %u\n", new_size); -#ifdef HAVE_REALLOC - bhdr = realloc(bhdr, new_size); - bhdr->b_checksum = new_size; -#else - printf("Boot notes too big\n"); - free(bhdr); - return NULL; -#endif - } - - addr = (char *) bhdr; - addr += bhdr->b_size; - memcpy(addr, &nhdr, sizeof(nhdr)); - addr += sizeof(nhdr); - - if (name && nhdr.n_namesz) { - memcpy(addr, name, nhdr.n_namesz); - addr += nhdr.n_namesz; - pad = padded(nhdr.n_namesz) - nhdr.n_namesz; - memset(addr, 0, pad); - addr += pad; - } - - memcpy(addr, desc, nhdr.n_descsz); - addr += nhdr.n_descsz; - pad = padded(nhdr.n_descsz) - nhdr.n_descsz; - memset(addr, 0, pad); - - bhdr->b_size += ent_size; - bhdr->b_records++; - return bhdr; -} - -static inline Elf_Bhdr *add_note_string(Elf_Bhdr *bhdr, const char *name, - unsigned type, const char *desc) -{ - return add_boot_note(bhdr, name, type, desc, strlen(desc) + 1); -} - -static Elf_Bhdr *build_boot_notes(struct sys_info *info, const char *cmdline) -{ - Elf_Bhdr *bhdr; - - bhdr = malloc(256); - bhdr->b_signature = ELF_BHDR_MAGIC; - bhdr->b_size = sizeof *bhdr; - bhdr->b_checksum = 256; /* XXX cache the current buffer size here */ - bhdr->b_records = 0; - - if (info->firmware) - bhdr = add_note_string(bhdr, NULL, EBN_FIRMWARE_TYPE, info->firmware); - bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_NAME, program_name); - bhdr = add_note_string(bhdr, NULL, EBN_BOOTLOADER_VERSION, program_version); - if (cmdline) - bhdr = add_note_string(bhdr, NULL, EBN_COMMAND_LINE, cmdline); - if (!bhdr) - return bhdr; - bhdr->b_checksum = 0; - bhdr->b_checksum = ipchksum(bhdr, bhdr->b_size); - return bhdr; -} - -int -is_elf(Elf_ehdr *ehdr) -{ - return (ehdr->e_ident[EI_MAG0] == ELFMAG0 - && ehdr->e_ident[EI_MAG1] == ELFMAG1 - && ehdr->e_ident[EI_MAG2] == ELFMAG2 - && ehdr->e_ident[EI_MAG3] == ELFMAG3 - && ehdr->e_ident[EI_CLASS] == ARCH_ELF_CLASS - && ehdr->e_ident[EI_DATA] == ARCH_ELF_DATA - && ehdr->e_ident[EI_VERSION] == EV_CURRENT - && ehdr->e_type == ET_EXEC - && ARCH_ELF_MACHINE_OK(ehdr->e_machine) - && ehdr->e_version == EV_CURRENT - && ehdr->e_phentsize == sizeof(Elf_phdr)); -} - -int -find_elf(Elf_ehdr *ehdr) -{ - int offset; - - for (offset = 0; offset < MAX_HEADERS * BS; offset += BS) { - if ((size_t)read_io(fd, ehdr, sizeof ehdr) != sizeof ehdr) { - debug("Can't read ELF header\n"); - return 0; - } - - if (is_elf(ehdr)) { - debug("Found ELF header at offset %d\n", offset); - return offset; - } - - seek_io(fd, offset); - } - - debug("Not a bootable ELF image\n"); - return 0; -} - -Elf_phdr * -elf_readhdrs(int offset, Elf_ehdr *ehdr) -{ - unsigned long phdr_size; - Elf_phdr *phdr; - - phdr_size = ehdr->e_phnum * sizeof(Elf_phdr); - phdr = malloc(phdr_size); - seek_io(fd, offset + ehdr->e_phoff); - if ((size_t)read_io(fd, phdr, phdr_size) != phdr_size) { - printf("Can't read program header\n"); - return NULL; - } - - return phdr; -} - -int -elf_load(struct sys_info *info, ihandle_t dev, const char *cmdline, void **boot_notes) -{ - Elf_ehdr ehdr; - Elf_phdr *phdr = NULL; - unsigned long checksum_offset, file_size; - unsigned short checksum = 0; - int retval = -1; - unsigned int offset; - - image_name = image_version = NULL; - - /* Mark the saved-program-state as invalid */ - feval("0 state-valid !"); - - fd = open_ih(dev); - if (fd == -1) { - goto out; - } - - offset = find_elf(&ehdr); - if (!offset) { - retval = LOADER_NOT_SUPPORT; - goto out; - } - -#if DEBUG - printk("ELF header:\n"); - printk(" ehdr.e_type = %d\n", (int)ehdr.e_type); - printk(" ehdr.e_machine = %d\n", (int)ehdr.e_machine); - printk(" ehdr.e_version = %d\n", (int)ehdr.e_version); - printk(" ehdr.e_entry = 0x%08x\n", (int)ehdr.e_entry); - printk(" ehdr.e_phoff = 0x%08x\n", (int)ehdr.e_phoff); - printk(" ehdr.e_shoff = 0x%08x\n", (int)ehdr.e_shoff); - printk(" ehdr.e_flags = %d\n", (int)ehdr.e_flags); - printk(" ehdr.e_ehsize = 0x%08x\n", (int)ehdr.e_ehsize); - printk(" ehdr.e_phentsize = 0x%08x\n", (int)ehdr.e_phentsize); - printk(" ehdr.e_phnum = %d\n", (int)ehdr.e_phnum); -#endif - - if (ehdr.e_phnum > MAX_HEADERS) { - printk ("elfload: too many program headers (MAX_HEADERS)\n"); - retval = 0; - goto out; - } - - phdr = elf_readhdrs(offset, &ehdr); - if (!phdr) - goto out; - - if (!check_mem_ranges(info, phdr, ehdr.e_phnum)) - goto out; - - checksum_offset = process_image_notes(phdr, ehdr.e_phnum, &checksum, offset); - - printf("Loading %s", image_name ? image_name : "image"); - if (image_version) - printf(" version %s", image_version); - printf("...\n"); - - if (!load_segments(phdr, ehdr.e_phnum, checksum_offset, offset, &file_size)) - goto out; - - if (checksum_offset) { - if (!verify_image(&ehdr, phdr, ehdr.e_phnum, checksum)) - goto out; - } - - /* If we are attempting an ELF boot image, we pass a non-NULL pointer - into boot_notes and mark the image as elf-boot rather than standard - ELF */ - if (boot_notes) { - *boot_notes = (void *)virt_to_phys(build_boot_notes(info, cmdline)); - feval("elf-boot saved-program-state >sps.file-type !"); - } else { - feval("elf saved-program-state >sps.file-type !"); - } - - //debug("current time: %lu\n", currticks()); - - debug("entry point is " FMT_elf "\n", addr_fixup(ehdr.e_entry)); - - // Initialise saved-program-state - PUSH(addr_fixup(ehdr.e_entry)); - feval("saved-program-state >sps.entry !"); - PUSH(file_size); - feval("saved-program-state >sps.file-size !"); - - feval("-1 state-valid !"); - -out: - close_io(fd); - if (phdr) - free(phdr); - if (image_name) - free(image_name); - if (image_version) - free(image_version); - return retval; -} - -void -elf_init_program(void) -{ - char *base; - int i; - Elf_ehdr *ehdr; - Elf_phdr *phdr; - size_t size, total_size = 0; - char *addr; - uintptr_t tmp; - - /* TODO: manage ELF notes section */ - feval("0 state-valid !"); - feval("load-base"); - base = (char*)cell2pointer(POP()); - - ehdr = (Elf_ehdr *)base; - - if (!is_elf(ehdr)) { - debug("Not a valid ELF memory image\n"); - return; - } - - phdr = (Elf_phdr *)(base + ehdr->e_phoff); - - for (i = 0; i < ehdr->e_phnum; i++) { - -#if DEBUG - debug("filesz: %08lX memsz: %08lX p_offset: %08lX " - "p_vaddr %08lX\n", - (unsigned long)phdr[i].p_filesz, (unsigned long)phdr[i].p_memsz, - (unsigned long)phdr[i].p_offset, (unsigned long)phdr[i].p_vaddr ); -#endif - - size = MIN(phdr[i].p_filesz, phdr[i].p_memsz); - if (!size) - continue; -#if !defined(CONFIG_SPARC32) && !defined(CONFIG_X86) - if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 ) { - printk("Ignoring failed claim for va %lx memsz %lx!\n", - (unsigned long)phdr[i].p_vaddr, - (unsigned long)phdr[i].p_memsz); - } -#endif - /* Workaround for archs where sizeof(int) != pointer size */ - tmp = phdr[i].p_vaddr; - addr = (char *)tmp; - - memcpy(addr, base + phdr[i].p_offset, size); - - total_size += size; - -#ifdef CONFIG_PPC - flush_icache_range( addr, addr + size ); -#endif - } - - // Initialise saved-program-state - PUSH(ehdr->e_entry); - feval("saved-program-state >sps.entry !"); - PUSH(total_size); - feval("saved-program-state >sps.file-size !"); - feval("elf saved-program-state >sps.file-type !"); - - feval("-1 state-valid !"); -} |