#include "kvm/kvm.h" #include "kvm/ioport.h" #include "kvm/virtio-console.h" #include #include #include #include struct kvm_ext kvm_req_ext[] = { { 0, 0 } }; void kvm__arch_read_term(struct kvm *kvm) { virtio_console__inject_interrupt(kvm); } void kvm__init_ram(struct kvm *kvm) { u64 phys_start, phys_size; void *host_mem; if (kvm->ram_size <= KVM_MMIO_START) { /* one region for all memory */ phys_start = 0; phys_size = kvm->ram_size; host_mem = kvm->ram_start; kvm__register_mem(kvm, phys_start, phys_size, host_mem); } else { /* one region for memory that fits below MMIO range */ phys_start = 0; phys_size = KVM_MMIO_START; host_mem = kvm->ram_start; kvm__register_mem(kvm, phys_start, phys_size, host_mem); /* one region for rest of memory */ phys_start = KVM_MMIO_START + KVM_MMIO_SIZE; phys_size = kvm->ram_size - KVM_MMIO_START; host_mem = kvm->ram_start + KVM_MMIO_START; kvm__register_mem(kvm, phys_start, phys_size, host_mem); } } void kvm__arch_delete_ram(struct kvm *kvm) { munmap(kvm->ram_start, kvm->ram_size); } void kvm__arch_set_cmdline(char *cmdline, bool video) { } /* Architecture-specific KVM init */ void kvm__arch_init(struct kvm *kvm, const char *hugetlbfs_path, u64 ram_size) { int ret; kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, hugetlbfs_path, ram_size); kvm->ram_size = ram_size; if (kvm->ram_start == MAP_FAILED) die("out of memory"); madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE); ret = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP); if (ret < 0) die_perror("KVM_CREATE_IRQCHIP ioctl"); } void kvm__irq_line(struct kvm *kvm, int irq, int level) { struct kvm_irq_level irq_level; int ret; irq_level.irq = irq; irq_level.level = level ? 1 : 0; ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level); if (ret < 0) die_perror("KVM_IRQ_LINE ioctl"); } void kvm__irq_trigger(struct kvm *kvm, int irq) { struct kvm_irq_level irq_level; int ret; irq_level.irq = irq; irq_level.level = 1; ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level); if (ret < 0) die_perror("KVM_IRQ_LINE ioctl"); } void ioport__setup_arch(struct kvm *kvm) { } bool kvm__arch_cpu_supports_vm(void) { return true; } bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename) { return false; } 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; if ((u64) kvm->ram_size <= KVM_MMIO_START) sprintf(p + cmdline_offset, "mem=0x%llx@0 ", (unsigned long long)kvm->ram_size); else sprintf(p + cmdline_offset, "mem=0x%llx@0 mem=0x%llx@0x%llx ", (unsigned long long)KVM_MMIO_START, (unsigned long long)kvm->ram_size - KVM_MMIO_START, (unsigned long long)(KVM_MMIO_START + KVM_MMIO_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 static bool load_flat_binary(struct kvm *kvm, int fd_kernel) { void *p; void *k_start; ssize_t kernel_size; if (lseek(fd_kernel, 0, SEEK_SET) < 0) die_perror("lseek"); p = k_start = guest_flat_to_host(kvm, KERNEL_LOAD_ADDR); kernel_size = read_file(fd_kernel, p, kvm->cfg.ram_size - KERNEL_LOAD_ADDR); if (kernel_size == -1) { if (errno == ENOMEM) die("kernel too big for guest memory"); else die_perror("kernel read"); } kvm->arch.is64bit = true; kvm->arch.entry_point = 0xffffffff81000000ull; pr_info("Loaded kernel to 0x%x (%zd bytes)", KERNEL_LOAD_ADDR, kernel_size); 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; 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++) { if (read_in_full(fd_kernel, &phdr, sizeof(phdr)) != 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; 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++) { if (read_in_full(fd_kernel, &phdr, sizeof(phdr)) != 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; } static bool load_elf_binary(struct kvm *kvm, int fd_kernel) { union { Elf64_Ehdr ehdr; Elf32_Ehdr ehdr32; } eh; size_t nr; char *p; struct kvm__arch_elf_info ei; 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); if (read_in_full(fd_kernel, p, ei.len) != (ssize_t)ei.len) die_perror("read"); return true; } bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, const char *kernel_cmdline) { if (fd_initrd != -1) { pr_err("Initrd not supported on MIPS."); return false; } if (load_elf_binary(kvm, fd_kernel)) { kvm__mips_install_cmdline(kvm); return true; } return load_flat_binary(kvm, fd_kernel); } void ioport__map_irq(u8 *irq) { }