From: Blue Swirl Date: Sun, 20 Sep 2009 14:58:02 +0000 (+0000) Subject: Compile loader only once X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=ca20cf32ab3d945155141ef737f5d08ebb373e1d;p=qemu.git Compile loader only once Callers must pass ELF machine, byte swapping and symbol LSB clearing information to ELF loader. A.out loader needs page size information, pass that too as a parameter. Extract prototypes to a separate file. Move loader.[ch] and elf_ops.h under hw. Adjust callers. Also use target_phys_addr_t instead of target_ulong for addresses: loader addresses aren't virtual. Signed-off-by: Blue Swirl --- diff --git a/Makefile.hw b/Makefile.hw index 11227bf873..cb9c9a4a85 100644 --- a/Makefile.hw +++ b/Makefile.hw @@ -11,6 +11,7 @@ VPATH=$(SRC_PATH):$(SRC_PATH)/hw QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu obj-y = +obj-y += loader.o obj-y += virtio.o obj-y += fw_cfg.o obj-y += watchdog.o diff --git a/Makefile.target b/Makefile.target index d9e98fe3c5..bc3998a1de 100644 --- a/Makefile.target +++ b/Makefile.target @@ -155,7 +155,7 @@ endif # System emulator target ifdef CONFIG_SOFTMMU -obj-y = vl.o monitor.o pci.o loader.o isa_mmio.o machine.o \ +obj-y = vl.o monitor.o pci.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly diff --git a/disas.h b/disas.h index 0789b57218..06abab21eb 100644 --- a/disas.h +++ b/disas.h @@ -3,6 +3,7 @@ #include "qemu-common.h" +#ifdef NEED_CPU_H /* Disassemble this for me please... (debugging). */ void disas(FILE *out, void *code, unsigned long size); void target_disas(FILE *out, target_ulong code, target_ulong size, int flags); @@ -15,12 +16,13 @@ void monitor_disas(Monitor *mon, CPUState *env, /* Look up symbol for debugging purpose. Returns "" if unknown. */ const char *lookup_symbol(target_ulong orig_addr); +#endif struct syminfo; struct elf32_sym; struct elf64_sym; -typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_ulong orig_addr); +typedef const char *(*lookup_symbol_t)(struct syminfo *s, target_phys_addr_t orig_addr); struct syminfo { lookup_symbol_t lookup_symbol; diff --git a/elf_ops.h b/elf_ops.h deleted file mode 100644 index 15928cbb5b..0000000000 --- a/elf_ops.h +++ /dev/null @@ -1,271 +0,0 @@ -static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) -{ - bswap16s(&ehdr->e_type); /* Object file type */ - bswap16s(&ehdr->e_machine); /* Architecture */ - bswap32s(&ehdr->e_version); /* Object file version */ - bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ - bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ - bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ - bswap32s(&ehdr->e_flags); /* Processor-specific flags */ - bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ - bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ - bswap16s(&ehdr->e_phnum); /* Program header table entry count */ - bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ - bswap16s(&ehdr->e_shnum); /* Section header table entry count */ - bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ -} - -static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) -{ - bswap32s(&phdr->p_type); /* Segment type */ - bswapSZs(&phdr->p_offset); /* Segment file offset */ - bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ - bswapSZs(&phdr->p_paddr); /* Segment physical address */ - bswapSZs(&phdr->p_filesz); /* Segment size in file */ - bswapSZs(&phdr->p_memsz); /* Segment size in memory */ - bswap32s(&phdr->p_flags); /* Segment flags */ - bswapSZs(&phdr->p_align); /* Segment alignment */ -} - -static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) -{ - bswap32s(&shdr->sh_name); - bswap32s(&shdr->sh_type); - bswapSZs(&shdr->sh_flags); - bswapSZs(&shdr->sh_addr); - bswapSZs(&shdr->sh_offset); - bswapSZs(&shdr->sh_size); - bswap32s(&shdr->sh_link); - bswap32s(&shdr->sh_info); - bswapSZs(&shdr->sh_addralign); - bswapSZs(&shdr->sh_entsize); -} - -static void glue(bswap_sym, SZ)(struct elf_sym *sym) -{ - bswap32s(&sym->st_name); - bswapSZs(&sym->st_value); - bswapSZs(&sym->st_size); - bswap16s(&sym->st_shndx); -} - -static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, - int n, int type) -{ - int i; - for(i=0;ist_value < sym->st_value) { - result = -1; - } else if (key->st_value >= sym->st_value + sym->st_size) { - result = 1; - } - return result; -} - -static const char *glue(lookup_symbol, SZ)(struct syminfo *s, target_ulong orig_addr) -{ - struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); - struct elf_sym key; - struct elf_sym *sym; - - key.st_value = orig_addr; - - sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ)); - if (sym != NULL) { - return s->disas_strtab + sym->st_name; - } - - return ""; -} - -static int glue(symcmp, SZ)(const void *s0, const void *s1) -{ - struct elf_sym *sym0 = (struct elf_sym *)s0; - struct elf_sym *sym1 = (struct elf_sym *)s1; - return (sym0->st_value < sym1->st_value) - ? -1 - : ((sym0->st_value > sym1->st_value) ? 1 : 0); -} - -static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab) -{ - struct elf_shdr *symtab, *strtab, *shdr_table = NULL; - struct elf_sym *syms = NULL; - struct syminfo *s; - int nsyms, i; - char *str = NULL; - - shdr_table = load_at(fd, ehdr->e_shoff, - sizeof(struct elf_shdr) * ehdr->e_shnum); - if (!shdr_table) - return -1; - - if (must_swab) { - for (i = 0; i < ehdr->e_shnum; i++) { - glue(bswap_shdr, SZ)(shdr_table + i); - } - } - - symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); - if (!symtab) - goto fail; - syms = load_at(fd, symtab->sh_offset, symtab->sh_size); - if (!syms) - goto fail; - - nsyms = symtab->sh_size / sizeof(struct elf_sym); - - i = 0; - while (i < nsyms) { - if (must_swab) - glue(bswap_sym, SZ)(&syms[i]); - /* We are only interested in function symbols. - Throw everything else away. */ - if (syms[i].st_shndx == SHN_UNDEF || - syms[i].st_shndx >= SHN_LORESERVE || - ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { - nsyms--; - if (i < nsyms) { - syms[i] = syms[nsyms]; - } - continue; - } -#if defined(TARGET_ARM) || defined (TARGET_MIPS) - /* The bottom address bit marks a Thumb or MIPS16 symbol. */ - syms[i].st_value &= ~(target_ulong)1; -#endif - i++; - } - syms = qemu_realloc(syms, nsyms * sizeof(*syms)); - - qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); - - /* String table */ - if (symtab->sh_link >= ehdr->e_shnum) - goto fail; - strtab = &shdr_table[symtab->sh_link]; - - str = load_at(fd, strtab->sh_offset, strtab->sh_size); - if (!str) - goto fail; - - /* Commit */ - s = qemu_mallocz(sizeof(*s)); - s->lookup_symbol = glue(lookup_symbol, SZ); - glue(s->disas_symtab.elf, SZ) = syms; - s->disas_num_syms = nsyms; - s->disas_strtab = str; - s->next = syminfos; - syminfos = s; - qemu_free(shdr_table); - return 0; - fail: - qemu_free(syms); - qemu_free(str); - qemu_free(shdr_table); - return -1; -} - -static int glue(load_elf, SZ)(int fd, int64_t address_offset, - int must_swab, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr) -{ - struct elfhdr ehdr; - struct elf_phdr *phdr = NULL, *ph; - int size, i, total_size; - elf_word mem_size; - uint64_t addr, low = (uint64_t)-1, high = 0; - uint8_t *data = NULL; - - if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) - goto fail; - if (must_swab) { - glue(bswap_ehdr, SZ)(&ehdr); - } - - switch (ELF_MACHINE) { - case EM_PPC64: - if (EM_PPC64 != ehdr.e_machine) - if (EM_PPC != ehdr.e_machine) - goto fail; - break; - case EM_X86_64: - if (EM_X86_64 != ehdr.e_machine) - if (EM_386 != ehdr.e_machine) - goto fail; - break; - default: - if (ELF_MACHINE != ehdr.e_machine) - goto fail; - } - - if (pentry) - *pentry = (uint64_t)(elf_sword)ehdr.e_entry; - - glue(load_symbols, SZ)(&ehdr, fd, must_swab); - - size = ehdr.e_phnum * sizeof(phdr[0]); - lseek(fd, ehdr.e_phoff, SEEK_SET); - phdr = qemu_mallocz(size); - if (!phdr) - goto fail; - if (read(fd, phdr, size) != size) - goto fail; - if (must_swab) { - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - glue(bswap_phdr, SZ)(ph); - } - } - - total_size = 0; - for(i = 0; i < ehdr.e_phnum; i++) { - ph = &phdr[i]; - if (ph->p_type == PT_LOAD) { - mem_size = ph->p_memsz; - /* XXX: avoid allocating */ - data = qemu_mallocz(mem_size); - if (ph->p_filesz > 0) { - if (lseek(fd, ph->p_offset, SEEK_SET) < 0) - goto fail; - if (read(fd, data, ph->p_filesz) != ph->p_filesz) - goto fail; - } - /* address_offset is hack for kernel images that are - linked at the wrong physical address. */ - addr = ph->p_paddr + address_offset; - - cpu_physical_memory_write_rom(addr, data, mem_size); - - total_size += mem_size; - if (addr < low) - low = addr; - if ((addr + mem_size) > high) - high = addr + mem_size; - - qemu_free(data); - data = NULL; - } - } - qemu_free(phdr); - if (lowaddr) - *lowaddr = (uint64_t)(elf_sword)low; - if (highaddr) - *highaddr = (uint64_t)(elf_sword)high; - return total_size; - fail: - qemu_free(data); - qemu_free(phdr); - return -1; -} diff --git a/hw/an5206.c b/hw/an5206.c index d417d923b9..a4b83b0f44 100644 --- a/hw/an5206.c +++ b/hw/an5206.c @@ -11,6 +11,8 @@ #include "mcf.h" #include "sysemu.h" #include "boards.h" +#include "loader.h" +#include "elf.h" #define KERNEL_LOAD_ADDR 0x10000 #define AN5206_MBAR_ADDR 0x10000000 @@ -35,7 +37,7 @@ static void an5206_init(ram_addr_t ram_size, CPUState *env; int kernel_size; uint64_t elf_entry; - target_ulong entry; + target_phys_addr_t entry; if (!cpu_model) cpu_model = "m5206"; @@ -66,7 +68,8 @@ static void an5206_init(ram_addr_t ram_size, exit(1); } - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, + 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff --git a/hw/arm_boot.c b/hw/arm_boot.c index 35f0130db1..a8a38c5a36 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -10,6 +10,8 @@ #include "hw.h" #include "arm-misc.h" #include "sysemu.h" +#include "loader.h" +#include "elf.h" #define KERNEL_ARGS_ADDR 0x100 #define KERNEL_LOAD_ADDR 0x00010000 @@ -191,7 +193,8 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) int n; int is_linux = 0; uint64_t elf_entry; - target_ulong entry; + target_phys_addr_t entry; + int big_endian; /* Load the kernel. */ if (!info->kernel_filename) { @@ -206,8 +209,15 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info) qemu_register_reset(main_cpu_reset, env); } +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif + /* Assume that raw images are linux kernels, and ELF images are not. */ - kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL); + kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL, + big_endian, ELF_MACHINE, 1); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(info->kernel_filename, &entry, NULL, diff --git a/hw/armv7m.c b/hw/armv7m.c index 059a356e21..a96288d0dd 100644 --- a/hw/armv7m.c +++ b/hw/armv7m.c @@ -10,6 +10,8 @@ #include "sysbus.h" #include "arm-misc.h" #include "sysemu.h" +#include "loader.h" +#include "elf.h" /* Bitbanded IO. Each word corresponds to a single bit. */ @@ -166,6 +168,7 @@ qemu_irq *armv7m_init(int flash_size, int sram_size, uint64_t entry; uint64_t lowaddr; int i; + int big_endian; flash_size *= 1024; sram_size *= 1024; @@ -206,7 +209,14 @@ qemu_irq *armv7m_init(int flash_size, int sram_size, pic[i] = qdev_get_gpio_in(nvic, i); } - image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL); +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif + + image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL, + big_endian, ELF_MACHINE, 1); if (image_size < 0) { image_size = load_image_targphys(kernel_filename, 0, flash_size); lowaddr = 0; diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c index b5163b655f..81a41c9446 100644 --- a/hw/axis_dev88.c +++ b/hw/axis_dev88.c @@ -28,6 +28,8 @@ #include "boards.h" #include "sysemu.h" #include "etraxfs.h" +#include "loader.h" +#include "elf.h" #define D(x) #define DNAND(x) @@ -344,7 +346,7 @@ void axisdev88_init (ram_addr_t ram_size, /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis devboard SDK. */ kernel_size = load_elf(kernel_filename, -0x80000000LL, - &entry, NULL, &high); + &entry, NULL, &high, 0, ELF_MACHINE, 0); bootstrap_pc = entry; if (kernel_size < 0) { /* Takes a kimage from the axis devboard SDK. */ diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c index 5718ab64e1..ce45a597db 100644 --- a/hw/dummy_m68k.c +++ b/hw/dummy_m68k.c @@ -9,6 +9,8 @@ #include "hw.h" #include "sysemu.h" #include "boards.h" +#include "loader.h" +#include "elf.h" #define KERNEL_LOAD_ADDR 0x10000 @@ -22,7 +24,7 @@ static void dummy_m68k_init(ram_addr_t ram_size, CPUState *env; int kernel_size; uint64_t elf_entry; - target_ulong entry; + target_phys_addr_t entry; if (!cpu_model) cpu_model = "cfv4e"; @@ -41,7 +43,8 @@ static void dummy_m68k_init(ram_addr_t ram_size, /* Load kernel. */ if (kernel_filename) { - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, + 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff --git a/hw/elf_ops.h b/hw/elf_ops.h new file mode 100644 index 0000000000..8376465a10 --- /dev/null +++ b/hw/elf_ops.h @@ -0,0 +1,274 @@ +static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswapSZs(&ehdr->e_entry); /* Entry point virtual address */ + bswapSZs(&ehdr->e_phoff); /* Program header table file offset */ + bswapSZs(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswapSZs(&phdr->p_offset); /* Segment file offset */ + bswapSZs(&phdr->p_vaddr); /* Segment virtual address */ + bswapSZs(&phdr->p_paddr); /* Segment physical address */ + bswapSZs(&phdr->p_filesz); /* Segment size in file */ + bswapSZs(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswapSZs(&phdr->p_align); /* Segment alignment */ +} + +static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswapSZs(&shdr->sh_flags); + bswapSZs(&shdr->sh_addr); + bswapSZs(&shdr->sh_offset); + bswapSZs(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswapSZs(&shdr->sh_addralign); + bswapSZs(&shdr->sh_entsize); +} + +static void glue(bswap_sym, SZ)(struct elf_sym *sym) +{ + bswap32s(&sym->st_name); + bswapSZs(&sym->st_value); + bswapSZs(&sym->st_size); + bswap16s(&sym->st_shndx); +} + +static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table, + int n, int type) +{ + int i; + for(i=0;ist_value < sym->st_value) { + result = -1; + } else if (key->st_value >= sym->st_value + sym->st_size) { + result = 1; + } + return result; +} + +static const char *glue(lookup_symbol, SZ)(struct syminfo *s, + target_phys_addr_t orig_addr) +{ + struct elf_sym *syms = glue(s->disas_symtab.elf, SZ); + struct elf_sym key; + struct elf_sym *sym; + + key.st_value = orig_addr; + + sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), glue(symfind, SZ)); + if (sym != NULL) { + return s->disas_strtab + sym->st_name; + } + + return ""; +} + +static int glue(symcmp, SZ)(const void *s0, const void *s1) +{ + struct elf_sym *sym0 = (struct elf_sym *)s0; + struct elf_sym *sym1 = (struct elf_sym *)s1; + return (sym0->st_value < sym1->st_value) + ? -1 + : ((sym0->st_value > sym1->st_value) ? 1 : 0); +} + +static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab, + int clear_lsb) +{ + struct elf_shdr *symtab, *strtab, *shdr_table = NULL; + struct elf_sym *syms = NULL; + struct syminfo *s; + int nsyms, i; + char *str = NULL; + + shdr_table = load_at(fd, ehdr->e_shoff, + sizeof(struct elf_shdr) * ehdr->e_shnum); + if (!shdr_table) + return -1; + + if (must_swab) { + for (i = 0; i < ehdr->e_shnum; i++) { + glue(bswap_shdr, SZ)(shdr_table + i); + } + } + + symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB); + if (!symtab) + goto fail; + syms = load_at(fd, symtab->sh_offset, symtab->sh_size); + if (!syms) + goto fail; + + nsyms = symtab->sh_size / sizeof(struct elf_sym); + + i = 0; + while (i < nsyms) { + if (must_swab) + glue(bswap_sym, SZ)(&syms[i]); + /* We are only interested in function symbols. + Throw everything else away. */ + if (syms[i].st_shndx == SHN_UNDEF || + syms[i].st_shndx >= SHN_LORESERVE || + ELF_ST_TYPE(syms[i].st_info) != STT_FUNC) { + nsyms--; + if (i < nsyms) { + syms[i] = syms[nsyms]; + } + continue; + } + if (clear_lsb) { + /* The bottom address bit marks a Thumb or MIPS16 symbol. */ + syms[i].st_value &= ~(glue(glue(Elf, SZ), _Addr))1; + } + i++; + } + syms = qemu_realloc(syms, nsyms * sizeof(*syms)); + + qsort(syms, nsyms, sizeof(*syms), glue(symcmp, SZ)); + + /* String table */ + if (symtab->sh_link >= ehdr->e_shnum) + goto fail; + strtab = &shdr_table[symtab->sh_link]; + + str = load_at(fd, strtab->sh_offset, strtab->sh_size); + if (!str) + goto fail; + + /* Commit */ + s = qemu_mallocz(sizeof(*s)); + s->lookup_symbol = glue(lookup_symbol, SZ); + glue(s->disas_symtab.elf, SZ) = syms; + s->disas_num_syms = nsyms; + s->disas_strtab = str; + s->next = syminfos; + syminfos = s; + qemu_free(shdr_table); + return 0; + fail: + qemu_free(syms); + qemu_free(str); + qemu_free(shdr_table); + return -1; +} + +static int glue(load_elf, SZ)(int fd, int64_t address_offset, + int must_swab, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + int elf_machine, int clear_lsb) +{ + struct elfhdr ehdr; + struct elf_phdr *phdr = NULL, *ph; + int size, i, total_size; + elf_word mem_size; + uint64_t addr, low = (uint64_t)-1, high = 0; + uint8_t *data = NULL; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail; + if (must_swab) { + glue(bswap_ehdr, SZ)(&ehdr); + } + + switch (elf_machine) { + case EM_PPC64: + if (EM_PPC64 != ehdr.e_machine) + if (EM_PPC != ehdr.e_machine) + goto fail; + break; + case EM_X86_64: + if (EM_X86_64 != ehdr.e_machine) + if (EM_386 != ehdr.e_machine) + goto fail; + break; + default: + if (elf_machine != ehdr.e_machine) + goto fail; + } + + if (pentry) + *pentry = (uint64_t)(elf_sword)ehdr.e_entry; + + glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb); + + size = ehdr.e_phnum * sizeof(phdr[0]); + lseek(fd, ehdr.e_phoff, SEEK_SET); + phdr = qemu_mallocz(size); + if (!phdr) + goto fail; + if (read(fd, phdr, size) != size) + goto fail; + if (must_swab) { + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + glue(bswap_phdr, SZ)(ph); + } + } + + total_size = 0; + for(i = 0; i < ehdr.e_phnum; i++) { + ph = &phdr[i]; + if (ph->p_type == PT_LOAD) { + mem_size = ph->p_memsz; + /* XXX: avoid allocating */ + data = qemu_mallocz(mem_size); + if (ph->p_filesz > 0) { + if (lseek(fd, ph->p_offset, SEEK_SET) < 0) + goto fail; + if (read(fd, data, ph->p_filesz) != ph->p_filesz) + goto fail; + } + /* address_offset is hack for kernel images that are + linked at the wrong physical address. */ + addr = ph->p_paddr + address_offset; + + cpu_physical_memory_write_rom(addr, data, mem_size); + + total_size += mem_size; + if (addr < low) + low = addr; + if ((addr + mem_size) > high) + high = addr + mem_size; + + qemu_free(data); + data = NULL; + } + } + qemu_free(phdr); + if (lowaddr) + *lowaddr = (uint64_t)(elf_sword)low; + if (highaddr) + *highaddr = (uint64_t)(elf_sword)high; + return total_size; + fail: + qemu_free(data); + qemu_free(phdr); + return -1; +} diff --git a/hw/etraxfs.c b/hw/etraxfs.c index ab6a3a302f..4f451c54c9 100644 --- a/hw/etraxfs.c +++ b/hw/etraxfs.c @@ -28,6 +28,8 @@ #include "net.h" #include "flash.h" #include "etraxfs.h" +#include "loader.h" +#include "elf.h" #define FLASH_SIZE 0x2000000 #define INTMEM_SIZE (128 * 1024) @@ -136,7 +138,7 @@ void bareetraxfs_init (ram_addr_t ram_size, /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis devboard SDK. */ kernel_size = load_elf(kernel_filename, -0x80000000LL, - &entry, NULL, &high); + &entry, NULL, &high, 0, ELF_MACHINE, 0); bootstrap_pc = entry; if (kernel_size < 0) { /* Takes a kimage from the axis devboard SDK. */ diff --git a/hw/loader.c b/hw/loader.c new file mode 100644 index 0000000000..5d83a66041 --- /dev/null +++ b/hw/loader.c @@ -0,0 +1,546 @@ +/* + * QEMU Executable loader + * + * Copyright (c) 2006 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * Gunzip functionality in this file is derived from u-boot: + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "hw.h" +#include "disas.h" +#include "sysemu.h" +#include "uboot_image.h" +#include "loader.h" + +#include + +/* return the size or -1 if error */ +int get_image_size(const char *filename) +{ + int fd, size; + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + size = lseek(fd, 0, SEEK_END); + close(fd); + return size; +} + +/* return the size or -1 if error */ +/* deprecated, because caller does not specify buffer size! */ +int load_image(const char *filename, uint8_t *addr) +{ + int fd, size; + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); + if (read(fd, addr, size) != size) { + close(fd); + return -1; + } + close(fd); + return size; +} + +/* return the amount read, just like fread. 0 may mean error or eof */ +int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f) +{ + uint8_t buf[4096]; + target_phys_addr_t dst_begin = dst_addr; + size_t want, did; + + while (nbytes) { + want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes; + did = fread(buf, 1, want, f); + + cpu_physical_memory_write_rom(dst_addr, buf, did); + dst_addr += did; + nbytes -= did; + if (did != want) + break; + } + return dst_addr - dst_begin; +} + +/* returns 0 on error, 1 if ok */ +int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f) +{ + return fread_targphys(dst_addr, nbytes, f) == nbytes; +} + +/* read()-like version */ +int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes) +{ + uint8_t buf[4096]; + target_phys_addr_t dst_begin = dst_addr; + size_t want, did; + + while (nbytes) { + want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes; + did = read(fd, buf, want); + if (did != want) break; + + cpu_physical_memory_write_rom(dst_addr, buf, did); + dst_addr += did; + nbytes -= did; + } + return dst_addr - dst_begin; +} + +/* return the size or -1 if error */ +int load_image_targphys(const char *filename, + target_phys_addr_t addr, int max_sz) +{ + FILE *f; + size_t got; + + f = fopen(filename, "rb"); + if (!f) return -1; + + got = fread_targphys(addr, max_sz, f); + if (ferror(f)) { fclose(f); return -1; } + fclose(f); + + return got; +} + +void pstrcpy_targphys(target_phys_addr_t dest, int buf_size, + const char *source) +{ + static const uint8_t nul_byte = 0; + const char *nulp; + + if (buf_size <= 0) return; + nulp = memchr(source, 0, buf_size); + if (nulp) { + cpu_physical_memory_write_rom(dest, (uint8_t *)source, + (nulp - source) + 1); + } else { + cpu_physical_memory_write_rom(dest, (uint8_t *)source, buf_size - 1); + cpu_physical_memory_write_rom(dest, &nul_byte, 1); + } +} + +/* A.OUT loader */ + +struct exec +{ + uint32_t a_info; /* Use macros N_MAGIC, etc for access */ + uint32_t a_text; /* length of text, in bytes */ + uint32_t a_data; /* length of data, in bytes */ + uint32_t a_bss; /* length of uninitialized data area, in bytes */ + uint32_t a_syms; /* length of symbol table data in file, in bytes */ + uint32_t a_entry; /* start address */ + uint32_t a_trsize; /* length of relocation info for text, in bytes */ + uint32_t a_drsize; /* length of relocation info for data, in bytes */ +}; + +static void bswap_ahdr(struct exec *e) +{ + bswap32s(&e->a_info); + bswap32s(&e->a_text); + bswap32s(&e->a_data); + bswap32s(&e->a_bss); + bswap32s(&e->a_syms); + bswap32s(&e->a_entry); + bswap32s(&e->a_trsize); + bswap32s(&e->a_drsize); +} + +#define N_MAGIC(exec) ((exec).a_info & 0xffff) +#define OMAGIC 0407 +#define NMAGIC 0410 +#define ZMAGIC 0413 +#define QMAGIC 0314 +#define _N_HDROFF(x) (1024 - sizeof (struct exec)) +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ + (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) +#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0) +#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1)) + +#define _N_TXTENDADDR(x, target_page_size) (N_TXTADDR(x, target_page_size)+(x).a_text) + +#define N_DATADDR(x, target_page_size) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x, target_page_size)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size))) + + +int load_aout(const char *filename, target_phys_addr_t addr, int max_sz, + int bswap_needed, target_phys_addr_t target_page_size) +{ + int fd, size, ret; + struct exec e; + uint32_t magic; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + + size = read(fd, &e, sizeof(e)); + if (size < 0) + goto fail; + + if (bswap_needed) { + bswap_ahdr(&e); + } + + magic = N_MAGIC(e); + switch (magic) { + case ZMAGIC: + case QMAGIC: + case OMAGIC: + if (e.a_text + e.a_data > max_sz) + goto fail; + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read_targphys(fd, addr, e.a_text + e.a_data); + if (size < 0) + goto fail; + break; + case NMAGIC: + if (N_DATADDR(e, target_page_size) + e.a_data > max_sz) + goto fail; + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read_targphys(fd, addr, e.a_text); + if (size < 0) + goto fail; + ret = read_targphys(fd, addr + N_DATADDR(e, target_page_size), + e.a_data); + if (ret < 0) + goto fail; + size += ret; + break; + default: + goto fail; + } + close(fd); + return size; + fail: + close(fd); + return -1; +} + +/* ELF loader */ + +static void *load_at(int fd, int offset, int size) +{ + void *ptr; + if (lseek(fd, offset, SEEK_SET) < 0) + return NULL; + ptr = qemu_malloc(size); + if (read(fd, ptr, size) != size) { + qemu_free(ptr); + return NULL; + } + return ptr; +} + +#ifdef ELF_CLASS +#undef ELF_CLASS +#endif + +#define ELF_CLASS ELFCLASS32 +#include "elf.h" + +#define SZ 32 +#define elf_word uint32_t +#define elf_sword int32_t +#define bswapSZs bswap32s +#include "elf_ops.h" + +#undef elfhdr +#undef elf_phdr +#undef elf_shdr +#undef elf_sym +#undef elf_note +#undef elf_word +#undef elf_sword +#undef bswapSZs +#undef SZ +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_note elf64_note +#define elf_shdr elf64_shdr +#define elf_sym elf64_sym +#define elf_word uint64_t +#define elf_sword int64_t +#define bswapSZs bswap64s +#define SZ 64 +#include "elf_ops.h" + +/* return < 0 if error, otherwise the number of bytes loaded in memory */ +int load_elf(const char *filename, int64_t address_offset, + uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, + int big_endian, int elf_machine, int clear_lsb) +{ + int fd, data_order, target_data_order, must_swab, ret; + uint8_t e_ident[EI_NIDENT]; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) { + perror(filename); + return -1; + } + if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) + goto fail; + if (e_ident[0] != ELFMAG0 || + e_ident[1] != ELFMAG1 || + e_ident[2] != ELFMAG2 || + e_ident[3] != ELFMAG3) + goto fail; +#ifdef HOST_WORDS_BIGENDIAN + data_order = ELFDATA2MSB; +#else + data_order = ELFDATA2LSB; +#endif + must_swab = data_order != e_ident[EI_DATA]; + if (big_endian) { + target_data_order = ELFDATA2MSB; + } else { + target_data_order = ELFDATA2LSB; + } + + if (target_data_order != e_ident[EI_DATA]) + return -1; + + lseek(fd, 0, SEEK_SET); + if (e_ident[EI_CLASS] == ELFCLASS64) { + ret = load_elf64(fd, address_offset, must_swab, pentry, + lowaddr, highaddr, elf_machine, clear_lsb); + } else { + ret = load_elf32(fd, address_offset, must_swab, pentry, + lowaddr, highaddr, elf_machine, clear_lsb); + } + + close(fd); + return ret; + + fail: + close(fd); + return -1; +} + +static void bswap_uboot_header(uboot_image_header_t *hdr) +{ +#ifndef HOST_WORDS_BIGENDIAN + bswap32s(&hdr->ih_magic); + bswap32s(&hdr->ih_hcrc); + bswap32s(&hdr->ih_time); + bswap32s(&hdr->ih_size); + bswap32s(&hdr->ih_load); + bswap32s(&hdr->ih_ep); + bswap32s(&hdr->ih_dcrc); +#endif +} + + +#define ZALLOC_ALIGNMENT 16 + +static void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p; + + size *= items; + size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); + + p = qemu_malloc(size); + + return (p); +} + +static void zfree(void *x, void *addr) +{ + qemu_free(addr); +} + + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + +/* This is the maximum in uboot, so if a uImage overflows this, it would + * overflow on real hardware too. */ +#define UBOOT_MAX_GUNZIP_BYTES 0x800000 + +static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, + size_t srclen) +{ + z_stream s; + ssize_t dstbytes; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + puts ("Error: Bad gzipped data\n"); + return -1; + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= srclen) { + puts ("Error: gunzip out of data in header\n"); + return -1; + } + + s.zalloc = zalloc; + s.zfree = zfree; + + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf ("Error: inflateInit2() returned %d\n", r); + return (-1); + } + s.next_in = src + i; + s.avail_in = srclen - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf ("Error: inflate() returned %d\n", r); + return -1; + } + dstbytes = s.next_out - (unsigned char *) dst; + inflateEnd(&s); + + return dstbytes; +} + +/* Load a U-Boot image. */ +int load_uimage(const char *filename, target_phys_addr_t *ep, + target_phys_addr_t *loadaddr, int *is_linux) +{ + int fd; + int size; + uboot_image_header_t h; + uboot_image_header_t *hdr = &h; + uint8_t *data = NULL; + int ret = -1; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + + size = read(fd, hdr, sizeof(uboot_image_header_t)); + if (size < 0) + goto out; + + bswap_uboot_header(hdr); + + if (hdr->ih_magic != IH_MAGIC) + goto out; + + /* TODO: Implement other image types. */ + if (hdr->ih_type != IH_TYPE_KERNEL) { + fprintf(stderr, "Can only load u-boot image type \"kernel\"\n"); + goto out; + } + + switch (hdr->ih_comp) { + case IH_COMP_NONE: + case IH_COMP_GZIP: + break; + default: + fprintf(stderr, + "Unable to load u-boot images with compression type %d\n", + hdr->ih_comp); + goto out; + } + + /* TODO: Check CPU type. */ + if (is_linux) { + if (hdr->ih_os == IH_OS_LINUX) + *is_linux = 1; + else + *is_linux = 0; + } + + *ep = hdr->ih_ep; + data = qemu_malloc(hdr->ih_size); + + if (read(fd, data, hdr->ih_size) != hdr->ih_size) { + fprintf(stderr, "Error reading file\n"); + goto out; + } + + if (hdr->ih_comp == IH_COMP_GZIP) { + uint8_t *compressed_data; + size_t max_bytes; + ssize_t bytes; + + compressed_data = data; + max_bytes = UBOOT_MAX_GUNZIP_BYTES; + data = qemu_malloc(max_bytes); + + bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size); + qemu_free(compressed_data); + if (bytes < 0) { + fprintf(stderr, "Unable to decompress gzipped image!\n"); + goto out; + } + hdr->ih_size = bytes; + } + + cpu_physical_memory_write_rom(hdr->ih_load, data, hdr->ih_size); + + if (loadaddr) + *loadaddr = hdr->ih_load; + + ret = hdr->ih_size; + +out: + if (data) + qemu_free(data); + close(fd); + return ret; +} diff --git a/hw/loader.h b/hw/loader.h new file mode 100644 index 0000000000..3632008928 --- /dev/null +++ b/hw/loader.h @@ -0,0 +1,21 @@ +#ifndef LOADER_H +#define LOADER_H + +/* loader.c */ +int get_image_size(const char *filename); +int load_image(const char *filename, uint8_t *addr); /* deprecated */ +int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz); +int load_elf(const char *filename, int64_t address_offset, + uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, + int big_endian, int elf_machine, int clear_lsb); +int load_aout(const char *filename, target_phys_addr_t addr, int max_sz, + int bswap_needed, target_phys_addr_t target_page_size); +int load_uimage(const char *filename, target_phys_addr_t *ep, + target_phys_addr_t *loadaddr, int *is_linux); + +int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f); +int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f); +int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes); +void pstrcpy_targphys(target_phys_addr_t dest, int buf_size, + const char *source); +#endif diff --git a/hw/mcf5208.c b/hw/mcf5208.c index 95a03fc0e6..5598611462 100644 --- a/hw/mcf5208.c +++ b/hw/mcf5208.c @@ -11,6 +11,8 @@ #include "sysemu.h" #include "net.h" #include "boards.h" +#include "loader.h" +#include "elf.h" #define SYS_FREQ 66000000 @@ -201,7 +203,7 @@ static void mcf5208evb_init(ram_addr_t ram_size, CPUState *env; int kernel_size; uint64_t elf_entry; - target_ulong entry; + target_phys_addr_t entry; qemu_irq *pic; if (!cpu_model) @@ -268,7 +270,8 @@ static void mcf5208evb_init(ram_addr_t ram_size, exit(1); } - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, + 1, ELF_MACHINE, 0); entry = elf_entry; if (kernel_size < 0) { kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL); diff --git a/hw/mips_jazz.c b/hw/mips_jazz.c index d62a584a9a..1a499fa397 100644 --- a/hw/mips_jazz.c +++ b/hw/mips_jazz.c @@ -33,6 +33,7 @@ #include "net.h" #include "scsi.h" #include "mips-bios.h" +#include "loader.h" enum jazz_model_e { diff --git a/hw/mips_malta.c b/hw/mips_malta.c index 0a6eaa479a..4d72da8716 100644 --- a/hw/mips_malta.c +++ b/hw/mips_malta.c @@ -39,6 +39,8 @@ #include "qemu-log.h" #include "mips-bios.h" #include "ide.h" +#include "loader.h" +#include "elf.h" //#define DEBUG_BOARD_INIT @@ -687,10 +689,17 @@ static int64_t load_kernel (CPUState *env) int index = 0; long initrd_size; ram_addr_t initrd_offset; + int big_endian; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif if (load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high) < 0) { + (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1) < 0) { fprintf(stderr, "qemu: could not load kernel '%s'\n", loaderparams.kernel_filename); exit(1); diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c index 6080dc825e..9aed40e1bd 100644 --- a/hw/mips_mipssim.c +++ b/hw/mips_mipssim.c @@ -32,6 +32,8 @@ #include "sysemu.h" #include "boards.h" #include "mips-bios.h" +#include "loader.h" +#include "elf.h" #ifdef TARGET_MIPS64 #define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL) @@ -54,10 +56,17 @@ static void load_kernel (CPUState *env) long kernel_size; long initrd_size; ram_addr_t initrd_offset; + int big_endian; + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, (uint64_t *)&entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high); + (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c index fcc7fed5f1..b3abc61551 100644 --- a/hw/mips_r4k.c +++ b/hw/mips_r4k.c @@ -18,6 +18,8 @@ #include "qemu-log.h" #include "mips-bios.h" #include "ide.h" +#include "loader.h" +#include "elf.h" #define PHYS_TO_VIRT(x) ((x) | ~(target_ulong)0x7fffffff) @@ -77,10 +79,16 @@ static void load_kernel (CPUState *env) long kernel_size, initrd_size; ram_addr_t initrd_offset; int ret; + int big_endian; +#ifdef TARGET_WORDS_BIGENDIAN + big_endian = 1; +#else + big_endian = 0; +#endif kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND, (uint64_t *)&entry, (uint64_t *)&kernel_low, - (uint64_t *)&kernel_high); + (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1); if (kernel_size >= 0) { if ((entry & ~0x7fffffffULL) == 0x80000000) entry = (int32_t)entry; diff --git a/hw/nseries.c b/hw/nseries.c index e9b68a7f10..066a0f980f 100644 --- a/hw/nseries.c +++ b/hw/nseries.c @@ -30,6 +30,7 @@ #include "flash.h" #include "hw.h" #include "bt.h" +#include "loader.h" /* Nokia N8x0 support */ struct n800_s { diff --git a/hw/palm.c b/hw/palm.c index bba972276e..6d19167512 100644 --- a/hw/palm.c +++ b/hw/palm.c @@ -24,6 +24,7 @@ #include "boards.h" #include "arm-misc.h" #include "devices.h" +#include "loader.h" static uint32_t static_readb(void *opaque, target_phys_addr_t offset) { diff --git a/hw/pc.c b/hw/pc.c index 58de372da6..bc2875e36d 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -37,6 +37,8 @@ #include "watchdog.h" #include "smbios.h" #include "ide.h" +#include "loader.h" +#include "elf.h" /* output Bochs bios info messages */ //#define DEBUG_BIOS @@ -657,7 +659,8 @@ static int load_multiboot(void *fw_cfg, uint64_t elf_entry; int kernel_size; fclose(f); - kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL); + kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL, + 0, ELF_MACHINE, 0); if (kernel_size < 0) { fprintf(stderr, "Error while loading elf kernel\n"); exit(1); diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c index a04794d7f3..f343dbf7b8 100644 --- a/hw/petalogix_s3adsp1800_mmu.c +++ b/hw/petalogix_s3adsp1800_mmu.c @@ -32,6 +32,8 @@ #include "boards.h" #include "device_tree.h" #include "xilinx.h" +#include "loader.h" +#include "elf.h" #define LMB_BRAM_SIZE (128 * 1024) #define FLASH_SIZE (16 * 1024 * 1024) @@ -155,11 +157,13 @@ petalogix_s3adsp1800_init(ram_addr_t ram_size, /* Boots a kernel elf binary. */ kernel_size = load_elf(kernel_filename, 0, - &entry, &low, &high); + &entry, &low, &high, + 1, ELF_MACHINE, 0); base32 = entry; if (base32 == 0xc0000000) { kernel_size = load_elf(kernel_filename, -0x30000000LL, - &entry, NULL, NULL); + &entry, NULL, NULL, + 1, ELF_MACHINE, 0); } /* Always boot into physical ram. */ bootstrap_pc = ddr_base + (entry & 0x0fffffff); diff --git a/hw/ppc.c b/hw/ppc.c index 2d66b9dc59..09ee2e46dd 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -27,6 +27,7 @@ #include "sysemu.h" #include "nvram.h" #include "qemu-log.h" +#include "loader.h" //#define PPC_DEBUG_IRQ //#define PPC_DEBUG_TB diff --git a/hw/ppc405_boards.c b/hw/ppc405_boards.c index 0d7860edd6..9aa99c1781 100644 --- a/hw/ppc405_boards.c +++ b/hw/ppc405_boards.c @@ -30,6 +30,7 @@ #include "block.h" #include "boards.h" #include "qemu-log.h" +#include "loader.h" #define BIOS_FILENAME "ppc405_rom.bin" #define BIOS_SIZE (2048 * 1024) diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c index 3c59f33099..8a6b7ced92 100644 --- a/hw/ppc440_bamboo.c +++ b/hw/ppc440_bamboo.c @@ -22,6 +22,8 @@ #include "kvm.h" #include "kvm_ppc.h" #include "device_tree.h" +#include "loader.h" +#include "elf.h" #define BINARY_DEVICE_TREE_FILE "bamboo.dtb" @@ -93,8 +95,8 @@ static void bamboo_init(ram_addr_t ram_size, CPUState *env; uint64_t elf_entry; uint64_t elf_lowaddr; - target_ulong entry = 0; - target_ulong loadaddr = 0; + target_phys_addr_t entry = 0; + target_phys_addr_t loadaddr = 0; target_long kernel_size = 0; target_ulong initrd_base = 0; target_long initrd_size = 0; @@ -126,7 +128,7 @@ static void bamboo_init(ram_addr_t ram_size, kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); if (kernel_size < 0) { kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr, - NULL); + NULL, 1, ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c index 9a491eb7c6..6bd5234d8f 100644 --- a/hw/ppc_newworld.c +++ b/hw/ppc_newworld.c @@ -36,6 +36,8 @@ #include "escc.h" #include "openpic.h" #include "ide.h" +#include "loader.h" +#include "elf.h" #define MAX_IDE_BUS 2 #define VGA_BIOS_SIZE 65536 @@ -145,7 +147,8 @@ static void ppc_core99_init (ram_addr_t ram_size, /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL); + bios_size = load_elf(filename, 0, NULL, NULL, NULL, 1, ELF_MACHINE, 0); + qemu_free(filename); } else { bios_size = -1; @@ -187,19 +190,28 @@ static void ppc_core99_init (ram_addr_t ram_size, if (linux_boot) { uint64_t lowaddr = 0; + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif kernel_base = KERNEL_LOAD_ADDR; /* Now we can load the kernel. The first step tries to load the kernel supposing PhysAddr = 0x00000000. If that was wrong the kernel is loaded again, the new PhysAddr being computed from lowaddr. */ - kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL); + kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL, + 1, ELF_MACHINE, 0); if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) { kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr, - NULL, NULL, NULL); + NULL, NULL, NULL, 1, ELF_MACHINE, 0); } if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, - ram_size - kernel_base); + ram_size - kernel_base, bswap_needed, + TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, kernel_base, diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c index 6933650711..bb8c969ca2 100644 --- a/hw/ppc_oldworld.c +++ b/hw/ppc_oldworld.c @@ -36,6 +36,8 @@ #include "fw_cfg.h" #include "escc.h" #include "ide.h" +#include "loader.h" +#include "elf.h" #define MAX_IDE_BUS 2 #define VGA_BIOS_SIZE 65536 @@ -180,7 +182,8 @@ static void ppc_heathrow_init (ram_addr_t ram_size, /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL); + bios_size = load_elf(filename, 0, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); qemu_free(filename); } else { bios_size = -1; @@ -222,18 +225,27 @@ static void ppc_heathrow_init (ram_addr_t ram_size, if (linux_boot) { uint64_t lowaddr = 0; + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif kernel_base = KERNEL_LOAD_ADDR; /* Now we can load the kernel. The first step tries to load the kernel supposing PhysAddr = 0x00000000. If that was wrong the kernel is loaded again, the new PhysAddr being computed from lowaddr. */ - kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL); + kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL, + 1, ELF_MACHINE, 0); if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) { kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr, - NULL, NULL, NULL); + NULL, NULL, NULL, 1, ELF_MACHINE, 0); } if (kernel_size < 0) kernel_size = load_aout(kernel_filename, kernel_base, - ram_size - kernel_base); + ram_size - kernel_base, bswap_needed, + TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, kernel_base, diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 5392072982..eb281f85f7 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -33,6 +33,7 @@ #include "boards.h" #include "qemu-log.h" #include "ide.h" +#include "loader.h" //#define HARD_DEBUG_PPC_IO //#define DEBUG_PPC_IO diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c index 51208215f1..504419458b 100644 --- a/hw/ppce500_mpc8544ds.c +++ b/hw/ppce500_mpc8544ds.c @@ -29,6 +29,8 @@ #include "device_tree.h" #include "openpic.h" #include "ppce500.h" +#include "loader.h" +#include "elf.h" #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" #define UIMAGE_LOAD_BASE 0 @@ -160,8 +162,8 @@ static void mpc8544ds_init(ram_addr_t ram_size, CPUState *env; uint64_t elf_entry; uint64_t elf_lowaddr; - target_ulong entry=0; - target_ulong loadaddr=UIMAGE_LOAD_BASE; + target_phys_addr_t entry=0; + target_phys_addr_t loadaddr=UIMAGE_LOAD_BASE; target_long kernel_size=0; target_ulong dt_base=DTB_LOAD_BASE; target_ulong initrd_base=INITRD_LOAD_BASE; @@ -226,7 +228,7 @@ static void mpc8544ds_init(ram_addr_t ram_size, kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL); if (kernel_size < 0) { kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr, - NULL); + NULL, 1, ELF_MACHINE, 0); entry = elf_entry; loadaddr = elf_lowaddr; } diff --git a/hw/r2d.c b/hw/r2d.c index ff514a482f..ea19ff623d 100644 --- a/hw/r2d.c +++ b/hw/r2d.c @@ -32,6 +32,7 @@ #include "net.h" #include "sh7750_regs.h" #include "ide.h" +#include "loader.h" #define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */ #define SDRAM_SIZE 0x04000000 diff --git a/hw/shix.c b/hw/shix.c index 19b0155a49..638bf16e34 100644 --- a/hw/shix.c +++ b/hw/shix.c @@ -32,6 +32,7 @@ #include "sh.h" #include "sysemu.h" #include "boards.h" +#include "loader.h" #define BIOS_FILENAME "shix_bios.bin" #define BIOS_ADDRESS 0xA0000000 diff --git a/hw/smbios.c b/hw/smbios.c index e28beba2c8..a3ae1de824 100644 --- a/hw/smbios.c +++ b/hw/smbios.c @@ -13,6 +13,7 @@ #include "sysemu.h" #include "smbios.h" +#include "loader.h" /* * Structures shared with the BIOS diff --git a/hw/sun4m.c b/hw/sun4m.c index d97072393b..a869d15a81 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -37,6 +37,8 @@ #include "fw_cfg.h" #include "escc.h" #include "qdev-addr.h" +#include "loader.h" +#include "elf.h" //#define DEBUG_IRQ @@ -302,11 +304,19 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif kernel_size = load_elf(kernel_filename, -0xf0000000ULL, NULL, NULL, - NULL); + NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR); + RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, @@ -608,7 +618,8 @@ static void prom_init(target_phys_addr_t addr, const char *bios_name) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL); + ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/sun4u.c b/hw/sun4u.c index 427ee764ce..2c97d9d702 100644 --- a/hw/sun4u.c +++ b/hw/sun4u.c @@ -34,6 +34,8 @@ #include "fw_cfg.h" #include "sysbus.h" #include "ide.h" +#include "loader.h" +#include "elf.h" //#define DEBUG_IRQ @@ -164,10 +166,19 @@ static unsigned long sun4u_load_kernel(const char *kernel_filename, kernel_size = 0; if (linux_boot) { - kernel_size = load_elf(kernel_filename, 0, NULL, NULL, NULL); + int bswap_needed; + +#ifdef BSWAP_NEEDED + bswap_needed = 1; +#else + bswap_needed = 0; +#endif + kernel_size = load_elf(kernel_filename, 0, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, - RAM_size - KERNEL_LOAD_ADDR); + RAM_size - KERNEL_LOAD_ADDR, bswap_needed, + TARGET_PAGE_SIZE); if (kernel_size < 0) kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, @@ -418,7 +429,8 @@ static void prom_init(target_phys_addr_t addr, const char *bios_name) } filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL); + ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL, + 1, ELF_MACHINE, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); } diff --git a/hw/tc58128.c b/hw/tc58128.c index 21e808547e..264aa028da 100644 --- a/hw/tc58128.c +++ b/hw/tc58128.c @@ -1,6 +1,7 @@ #include "hw.h" #include "sh.h" #include "sysemu.h" +#include "loader.h" #define CE1 0x0100 #define CE2 0x0200 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 10d4781da8..7c8e771df1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1315,10 +1315,10 @@ static void load_symbols(struct elfhdr *hdr, int fd) s->disas_num_syms = nsyms; #if ELF_CLASS == ELFCLASS32 s->disas_symtab.elf32 = syms; - s->lookup_symbol = lookup_symbolxx; + s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; #else s->disas_symtab.elf64 = syms; - s->lookup_symbol = lookup_symbolxx; + s->lookup_symbol = (lookup_symbol_t)lookup_symbolxx; #endif s->next = syminfos; syminfos = s; diff --git a/loader.c b/loader.c deleted file mode 100644 index 0cbcf9c6a3..0000000000 --- a/loader.c +++ /dev/null @@ -1,544 +0,0 @@ -/* - * QEMU Executable loader - * - * Copyright (c) 2006 Fabrice Bellard - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * Gunzip functionality in this file is derived from u-boot: - * - * (C) Copyright 2008 Semihalf - * - * (C) Copyright 2000-2005 - * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "qemu-common.h" -#include "disas.h" -#include "sysemu.h" -#include "uboot_image.h" - -#include - -/* return the size or -1 if error */ -int get_image_size(const char *filename) -{ - int fd, size; - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - size = lseek(fd, 0, SEEK_END); - close(fd); - return size; -} - -/* return the size or -1 if error */ -/* deprecated, because caller does not specify buffer size! */ -int load_image(const char *filename, uint8_t *addr) -{ - int fd, size; - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - size = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - if (read(fd, addr, size) != size) { - close(fd); - return -1; - } - close(fd); - return size; -} - -/* return the amount read, just like fread. 0 may mean error or eof */ -int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f) -{ - uint8_t buf[4096]; - target_phys_addr_t dst_begin = dst_addr; - size_t want, did; - - while (nbytes) { - want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes; - did = fread(buf, 1, want, f); - - cpu_physical_memory_write_rom(dst_addr, buf, did); - dst_addr += did; - nbytes -= did; - if (did != want) - break; - } - return dst_addr - dst_begin; -} - -/* returns 0 on error, 1 if ok */ -int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f) -{ - return fread_targphys(dst_addr, nbytes, f) == nbytes; -} - -/* read()-like version */ -int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes) -{ - uint8_t buf[4096]; - target_phys_addr_t dst_begin = dst_addr; - size_t want, did; - - while (nbytes) { - want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes; - did = read(fd, buf, want); - if (did != want) break; - - cpu_physical_memory_write_rom(dst_addr, buf, did); - dst_addr += did; - nbytes -= did; - } - return dst_addr - dst_begin; -} - -/* return the size or -1 if error */ -int load_image_targphys(const char *filename, - target_phys_addr_t addr, int max_sz) -{ - FILE *f; - size_t got; - - f = fopen(filename, "rb"); - if (!f) return -1; - - got = fread_targphys(addr, max_sz, f); - if (ferror(f)) { fclose(f); return -1; } - fclose(f); - - return got; -} - -void pstrcpy_targphys(target_phys_addr_t dest, int buf_size, - const char *source) -{ - static const uint8_t nul_byte = 0; - const char *nulp; - - if (buf_size <= 0) return; - nulp = memchr(source, 0, buf_size); - if (nulp) { - cpu_physical_memory_write_rom(dest, (uint8_t *)source, - (nulp - source) + 1); - } else { - cpu_physical_memory_write_rom(dest, (uint8_t *)source, buf_size - 1); - cpu_physical_memory_write_rom(dest, &nul_byte, 1); - } -} - -/* A.OUT loader */ - -struct exec -{ - uint32_t a_info; /* Use macros N_MAGIC, etc for access */ - uint32_t a_text; /* length of text, in bytes */ - uint32_t a_data; /* length of data, in bytes */ - uint32_t a_bss; /* length of uninitialized data area, in bytes */ - uint32_t a_syms; /* length of symbol table data in file, in bytes */ - uint32_t a_entry; /* start address */ - uint32_t a_trsize; /* length of relocation info for text, in bytes */ - uint32_t a_drsize; /* length of relocation info for data, in bytes */ -}; - -#ifdef BSWAP_NEEDED -static void bswap_ahdr(struct exec *e) -{ - bswap32s(&e->a_info); - bswap32s(&e->a_text); - bswap32s(&e->a_data); - bswap32s(&e->a_bss); - bswap32s(&e->a_syms); - bswap32s(&e->a_entry); - bswap32s(&e->a_trsize); - bswap32s(&e->a_drsize); -} -#else -#define bswap_ahdr(x) do { } while (0) -#endif - -#define N_MAGIC(exec) ((exec).a_info & 0xffff) -#define OMAGIC 0407 -#define NMAGIC 0410 -#define ZMAGIC 0413 -#define QMAGIC 0314 -#define _N_HDROFF(x) (1024 - sizeof (struct exec)) -#define N_TXTOFF(x) \ - (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \ - (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec))) -#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0) -#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1)) - -#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) - -#define N_DATADDR(x) \ - (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ - : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) - - -int load_aout(const char *filename, target_phys_addr_t addr, int max_sz) -{ - int fd, size, ret; - struct exec e; - uint32_t magic; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - - size = read(fd, &e, sizeof(e)); - if (size < 0) - goto fail; - - bswap_ahdr(&e); - - magic = N_MAGIC(e); - switch (magic) { - case ZMAGIC: - case QMAGIC: - case OMAGIC: - if (e.a_text + e.a_data > max_sz) - goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(fd, addr, e.a_text + e.a_data); - if (size < 0) - goto fail; - break; - case NMAGIC: - if (N_DATADDR(e) + e.a_data > max_sz) - goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(fd, addr, e.a_text); - if (size < 0) - goto fail; - ret = read_targphys(fd, addr + N_DATADDR(e), e.a_data); - if (ret < 0) - goto fail; - size += ret; - break; - default: - goto fail; - } - close(fd); - return size; - fail: - close(fd); - return -1; -} - -/* ELF loader */ - -static void *load_at(int fd, int offset, int size) -{ - void *ptr; - if (lseek(fd, offset, SEEK_SET) < 0) - return NULL; - ptr = qemu_malloc(size); - if (read(fd, ptr, size) != size) { - qemu_free(ptr); - return NULL; - } - return ptr; -} - -#ifdef ELF_CLASS -#undef ELF_CLASS -#endif - -#define ELF_CLASS ELFCLASS32 -#include "elf.h" - -#define SZ 32 -#define elf_word uint32_t -#define elf_sword int32_t -#define bswapSZs bswap32s -#include "elf_ops.h" - -#undef elfhdr -#undef elf_phdr -#undef elf_shdr -#undef elf_sym -#undef elf_note -#undef elf_word -#undef elf_sword -#undef bswapSZs -#undef SZ -#define elfhdr elf64_hdr -#define elf_phdr elf64_phdr -#define elf_note elf64_note -#define elf_shdr elf64_shdr -#define elf_sym elf64_sym -#define elf_word uint64_t -#define elf_sword int64_t -#define bswapSZs bswap64s -#define SZ 64 -#include "elf_ops.h" - -/* return < 0 if error, otherwise the number of bytes loaded in memory */ -int load_elf(const char *filename, int64_t address_offset, - uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr) -{ - int fd, data_order, host_data_order, must_swab, ret; - uint8_t e_ident[EI_NIDENT]; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) { - perror(filename); - return -1; - } - if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident)) - goto fail; - if (e_ident[0] != ELFMAG0 || - e_ident[1] != ELFMAG1 || - e_ident[2] != ELFMAG2 || - e_ident[3] != ELFMAG3) - goto fail; -#ifdef HOST_WORDS_BIGENDIAN - data_order = ELFDATA2MSB; -#else - data_order = ELFDATA2LSB; -#endif - must_swab = data_order != e_ident[EI_DATA]; - -#ifdef TARGET_WORDS_BIGENDIAN - host_data_order = ELFDATA2MSB; -#else - host_data_order = ELFDATA2LSB; -#endif - if (host_data_order != e_ident[EI_DATA]) - return -1; - - lseek(fd, 0, SEEK_SET); - if (e_ident[EI_CLASS] == ELFCLASS64) { - ret = load_elf64(fd, address_offset, must_swab, pentry, - lowaddr, highaddr); - } else { - ret = load_elf32(fd, address_offset, must_swab, pentry, - lowaddr, highaddr); - } - - close(fd); - return ret; - - fail: - close(fd); - return -1; -} - -static void bswap_uboot_header(uboot_image_header_t *hdr) -{ -#ifndef HOST_WORDS_BIGENDIAN - bswap32s(&hdr->ih_magic); - bswap32s(&hdr->ih_hcrc); - bswap32s(&hdr->ih_time); - bswap32s(&hdr->ih_size); - bswap32s(&hdr->ih_load); - bswap32s(&hdr->ih_ep); - bswap32s(&hdr->ih_dcrc); -#endif -} - - -#define ZALLOC_ALIGNMENT 16 - -static void *zalloc(void *x, unsigned items, unsigned size) -{ - void *p; - - size *= items; - size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1); - - p = qemu_malloc(size); - - return (p); -} - -static void zfree(void *x, void *addr) -{ - qemu_free(addr); -} - - -#define HEAD_CRC 2 -#define EXTRA_FIELD 4 -#define ORIG_NAME 8 -#define COMMENT 0x10 -#define RESERVED 0xe0 - -#define DEFLATED 8 - -/* This is the maximum in uboot, so if a uImage overflows this, it would - * overflow on real hardware too. */ -#define UBOOT_MAX_GUNZIP_BYTES 0x800000 - -static ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, - size_t srclen) -{ - z_stream s; - ssize_t dstbytes; - int r, i, flags; - - /* skip header */ - i = 10; - flags = src[3]; - if (src[2] != DEFLATED || (flags & RESERVED) != 0) { - puts ("Error: Bad gzipped data\n"); - return -1; - } - if ((flags & EXTRA_FIELD) != 0) - i = 12 + src[10] + (src[11] << 8); - if ((flags & ORIG_NAME) != 0) - while (src[i++] != 0) - ; - if ((flags & COMMENT) != 0) - while (src[i++] != 0) - ; - if ((flags & HEAD_CRC) != 0) - i += 2; - if (i >= srclen) { - puts ("Error: gunzip out of data in header\n"); - return -1; - } - - s.zalloc = zalloc; - s.zfree = zfree; - - r = inflateInit2(&s, -MAX_WBITS); - if (r != Z_OK) { - printf ("Error: inflateInit2() returned %d\n", r); - return (-1); - } - s.next_in = src + i; - s.avail_in = srclen - i; - s.next_out = dst; - s.avail_out = dstlen; - r = inflate(&s, Z_FINISH); - if (r != Z_OK && r != Z_STREAM_END) { - printf ("Error: inflate() returned %d\n", r); - return -1; - } - dstbytes = s.next_out - (unsigned char *) dst; - inflateEnd(&s); - - return dstbytes; -} - -/* Load a U-Boot image. */ -int load_uimage(const char *filename, target_ulong *ep, target_ulong *loadaddr, - int *is_linux) -{ - int fd; - int size; - uboot_image_header_t h; - uboot_image_header_t *hdr = &h; - uint8_t *data = NULL; - int ret = -1; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - - size = read(fd, hdr, sizeof(uboot_image_header_t)); - if (size < 0) - goto out; - - bswap_uboot_header(hdr); - - if (hdr->ih_magic != IH_MAGIC) - goto out; - - /* TODO: Implement other image types. */ - if (hdr->ih_type != IH_TYPE_KERNEL) { - fprintf(stderr, "Can only load u-boot image type \"kernel\"\n"); - goto out; - } - - switch (hdr->ih_comp) { - case IH_COMP_NONE: - case IH_COMP_GZIP: - break; - default: - fprintf(stderr, - "Unable to load u-boot images with compression type %d\n", - hdr->ih_comp); - goto out; - } - - /* TODO: Check CPU type. */ - if (is_linux) { - if (hdr->ih_os == IH_OS_LINUX) - *is_linux = 1; - else - *is_linux = 0; - } - - *ep = hdr->ih_ep; - data = qemu_malloc(hdr->ih_size); - - if (read(fd, data, hdr->ih_size) != hdr->ih_size) { - fprintf(stderr, "Error reading file\n"); - goto out; - } - - if (hdr->ih_comp == IH_COMP_GZIP) { - uint8_t *compressed_data; - size_t max_bytes; - ssize_t bytes; - - compressed_data = data; - max_bytes = UBOOT_MAX_GUNZIP_BYTES; - data = qemu_malloc(max_bytes); - - bytes = gunzip(data, max_bytes, compressed_data, hdr->ih_size); - qemu_free(compressed_data); - if (bytes < 0) { - fprintf(stderr, "Unable to decompress gzipped image!\n"); - goto out; - } - hdr->ih_size = bytes; - } - - cpu_physical_memory_write_rom(hdr->ih_load, data, hdr->ih_size); - - if (loadaddr) - *loadaddr = hdr->ih_load; - - ret = hdr->ih_size; - -out: - if (data) - qemu_free(data); - close(fd); - return ret; -} diff --git a/sysemu.h b/sysemu.h index 644a97d624..8bf90ee357 100644 --- a/sysemu.h +++ b/sysemu.h @@ -237,24 +237,6 @@ extern CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) -#ifdef NEED_CPU_H -/* loader.c */ -int get_image_size(const char *filename); -int load_image(const char *filename, uint8_t *addr); /* deprecated */ -int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz); -int load_elf(const char *filename, int64_t address_offset, - uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr); -int load_aout(const char *filename, target_phys_addr_t addr, int max_sz); -int load_uimage(const char *filename, target_ulong *ep, target_ulong *loadaddr, - int *is_linux); - -int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f); -int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f); -int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes); -void pstrcpy_targphys(target_phys_addr_t dest, int buf_size, - const char *source); -#endif - #ifdef HAS_AUDIO struct soundhw { const char *name;