From: David Daney <david.daney@xxxxxxxxxx> Signed-off-by: David Daney <david.daney@xxxxxxxxxx> Signed-off-by: Andreas Herrmann <andreas.herrmann@xxxxxxxxxxxxxxxxxx> --- tools/kvm/mips/kvm.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) [andreas.herrmann: * Fixed compile warnings] diff --git a/tools/kvm/mips/kvm.c b/tools/kvm/mips/kvm.c index 9eecfb5..fc0428b 100644 --- a/tools/kvm/mips/kvm.c +++ b/tools/kvm/mips/kvm.c @@ -6,6 +6,7 @@ #include <ctype.h> #include <unistd.h> +#include <elf.h> struct kvm_ext kvm_req_ext[] = { { 0, 0 } @@ -99,6 +100,43 @@ int kvm__arch_setup_firmware(struct kvm *kvm) return 0; } +static void kvm__mips_install_cmdline(struct kvm *kvm) +{ + char *p = kvm->ram_start; + u64 cmdline_offset = 0x2000; + u64 argv_start = 0x3000; + u64 argv_offset = argv_start; + u64 argc = 0; + + sprintf(p + cmdline_offset, "mem=0x%llx@0 ", + (unsigned long long)kvm->ram_size); + + strcat(p + cmdline_offset, kvm->cfg.real_cmdline); /* maximum size is 2K */ + + while (p[cmdline_offset]) { + if (!isspace(p[cmdline_offset])) { + if (kvm->arch.is64bit) { + *(u64 *)(p + argv_offset) = 0xffffffff80000000ull + cmdline_offset; + argv_offset += sizeof(u64); + } else { + *(u32 *)(p + argv_offset) = 0x80000000u + cmdline_offset; + argv_offset += sizeof(u32); + } + argc++; + while(p[cmdline_offset] && !isspace(p[cmdline_offset])) + cmdline_offset++; + continue; + } + /* Must be a space character skip over these*/ + while(p[cmdline_offset] && isspace(p[cmdline_offset])) { + p[cmdline_offset] = 0; + cmdline_offset++; + } + } + kvm->arch.argc = argc; + kvm->arch.argv = 0xffffffff80000000ull + argv_start; +} + /* Load at the 1M point. */ #define KERNEL_LOAD_ADDR 0x1000000 int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline) @@ -123,6 +161,168 @@ int load_flat_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, const char * return true; } +struct kvm__arch_elf_info { + u64 load_addr; + u64 entry_point; + size_t len; + size_t offset; +}; + +static bool kvm__arch_get_elf_64_info(Elf64_Ehdr *ehdr, int fd_kernel, + struct kvm__arch_elf_info *ei) +{ + int i; + size_t nr; + Elf64_Phdr phdr; + + if (ehdr->e_phentsize != sizeof(phdr)) { + pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize); + return false; + } + + ei->entry_point = ehdr->e_entry; + + if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0) + die_perror("lseek"); + + phdr.p_type = PT_NULL; + for (i = 0; i < ehdr->e_phnum; i++) { + nr = read(fd_kernel, &phdr, sizeof(phdr)); + if (nr != sizeof(phdr)) { + pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr)); + return false; + } + if (phdr.p_type == PT_LOAD) + break; + } + if (phdr.p_type != PT_LOAD) { + pr_info("No PT_LOAD Program Header found."); + return false; + } + + ei->load_addr = phdr.p_paddr; + + if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull) + ei->load_addr &= 0x1ffffffful; /* Convert KSEG{0,1} to physical. */ + if ((ei->load_addr & 0xc000000000000000ull) == 0x8000000000000000ull) + ei->load_addr &= 0x07ffffffffffffffull; /* Convert XKPHYS to pysical */ + + + ei->len = phdr.p_filesz; + ei->offset = phdr.p_offset; + + return true; +} + +static bool kvm__arch_get_elf_32_info(Elf32_Ehdr *ehdr, int fd_kernel, + struct kvm__arch_elf_info *ei) +{ + int i; + size_t nr; + Elf32_Phdr phdr; + + if (ehdr->e_phentsize != sizeof(phdr)) { + pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize); + return false; + } + + ei->entry_point = (s64)((s32)ehdr->e_entry); + + if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0) + die_perror("lseek"); + + phdr.p_type = PT_NULL; + for (i = 0; i < ehdr->e_phnum; i++) { + nr = read(fd_kernel, &phdr, sizeof(phdr)); + if (nr != sizeof(phdr)) { + pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr)); + return false; + } + if (phdr.p_type == PT_LOAD) + break; + } + if (phdr.p_type != PT_LOAD) { + pr_info("No PT_LOAD Program Header found."); + return false; + } + + ei->load_addr = (s64)((s32)phdr.p_paddr); + + if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull) + ei->load_addr &= 0x1fffffffull; /* Convert KSEG{0,1} to physical. */ + + ei->len = phdr.p_filesz; + ei->offset = phdr.p_offset; + + return true; +} + +int load_elf_binary(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline) +{ + union { + Elf64_Ehdr ehdr; + Elf32_Ehdr ehdr32; + } eh; + + size_t nr; + char *p; + struct kvm__arch_elf_info ei; + + if (lseek(fd_kernel, 0, SEEK_SET) < 0) + die_perror("lseek"); + + nr = read(fd_kernel, &eh, sizeof(eh)); + if (nr != sizeof(eh)) { + pr_info("Couldn't read %d bytes for ELF header.", (int)sizeof(eh)); + return false; + } + + if (eh.ehdr.e_ident[EI_MAG0] != ELFMAG0 || + eh.ehdr.e_ident[EI_MAG1] != ELFMAG1 || + eh.ehdr.e_ident[EI_MAG2] != ELFMAG2 || + eh.ehdr.e_ident[EI_MAG3] != ELFMAG3 || + (eh.ehdr.e_ident[EI_CLASS] != ELFCLASS64 && eh.ehdr.e_ident[EI_CLASS] != ELFCLASS32) || + eh.ehdr.e_ident[EI_VERSION] != EV_CURRENT) { + pr_info("Incompatible ELF header."); + return false; + } + if (eh.ehdr.e_type != ET_EXEC || eh.ehdr.e_machine != EM_MIPS) { + pr_info("Incompatible ELF not MIPS EXEC."); + return false; + } + + if (eh.ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + if (!kvm__arch_get_elf_64_info(&eh.ehdr, fd_kernel, &ei)) + return false; + kvm->arch.is64bit = true; + } else { + if (!kvm__arch_get_elf_32_info(&eh.ehdr32, fd_kernel, &ei)) + return false; + kvm->arch.is64bit = false; + } + + kvm->arch.entry_point = ei.entry_point; + + if (lseek(fd_kernel, ei.offset, SEEK_SET) < 0) + die_perror("lseek"); + + p = guest_flat_to_host(kvm, ei.load_addr); + + pr_info("ELF Loading 0x%lx bytes from 0x%llx to 0x%llx", + (unsigned long)ei.len, (unsigned long long)ei.offset, (unsigned long long)ei.load_addr); + do { + nr = read(fd_kernel, p, ei.len); + if (nr < 0) + die_perror("read"); + p += nr; + ei.len -= nr; + } while (ei.len); + + kvm__mips_install_cmdline(kvm); + + return true; +} + void ioport__map_irq(u8 *irq) { } -- 1.7.9.5