From: David Daney <[email protected]>

Signed-off-by: David Daney <[email protected]>
Signed-off-by: Andreas Herrmann <[email protected]>
---
 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

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to