16f6f384cSAlexandru Elisei #include "kvm/8250-serial.h" 27281a8dbSDavid Daney #include "kvm/kvm.h" 37281a8dbSDavid Daney #include "kvm/ioport.h" 47281a8dbSDavid Daney #include "kvm/virtio-console.h" 57281a8dbSDavid Daney 67281a8dbSDavid Daney #include <linux/kvm.h> 77281a8dbSDavid Daney 87281a8dbSDavid Daney #include <ctype.h> 97281a8dbSDavid Daney #include <unistd.h> 101ade2d7dSDavid Daney #include <elf.h> 117281a8dbSDavid Daney 127281a8dbSDavid Daney struct kvm_ext kvm_req_ext[] = { 137281a8dbSDavid Daney { 0, 0 } 147281a8dbSDavid Daney }; 157281a8dbSDavid Daney 16abe3f28aSAlexandru Elisei void kvm__arch_validate_cfg(struct kvm *kvm) 17abe3f28aSAlexandru Elisei { 18abe3f28aSAlexandru Elisei } 19abe3f28aSAlexandru Elisei 207281a8dbSDavid Daney void kvm__arch_read_term(struct kvm *kvm) 217281a8dbSDavid Daney { 227281a8dbSDavid Daney virtio_console__inject_interrupt(kvm); 237281a8dbSDavid Daney } 247281a8dbSDavid Daney 257281a8dbSDavid Daney void kvm__init_ram(struct kvm *kvm) 267281a8dbSDavid Daney { 277281a8dbSDavid Daney u64 phys_start, phys_size; 287281a8dbSDavid Daney void *host_mem; 297281a8dbSDavid Daney 3057896feeSAndreas Herrmann if (kvm->ram_size <= KVM_MMIO_START) { 3157896feeSAndreas Herrmann /* one region for all memory */ 327281a8dbSDavid Daney phys_start = 0; 337281a8dbSDavid Daney phys_size = kvm->ram_size; 347281a8dbSDavid Daney host_mem = kvm->ram_start; 357281a8dbSDavid Daney 368f46c736SJean-Philippe Brucker kvm__register_ram(kvm, phys_start, phys_size, host_mem); 3757896feeSAndreas Herrmann } else { 3857896feeSAndreas Herrmann /* one region for memory that fits below MMIO range */ 3957896feeSAndreas Herrmann phys_start = 0; 4057896feeSAndreas Herrmann phys_size = KVM_MMIO_START; 4157896feeSAndreas Herrmann host_mem = kvm->ram_start; 4257896feeSAndreas Herrmann 438f46c736SJean-Philippe Brucker kvm__register_ram(kvm, phys_start, phys_size, host_mem); 4457896feeSAndreas Herrmann 4557896feeSAndreas Herrmann /* one region for rest of memory */ 4657896feeSAndreas Herrmann phys_start = KVM_MMIO_START + KVM_MMIO_SIZE; 4757896feeSAndreas Herrmann phys_size = kvm->ram_size - KVM_MMIO_START; 4857896feeSAndreas Herrmann host_mem = kvm->ram_start + KVM_MMIO_START; 4957896feeSAndreas Herrmann 508f46c736SJean-Philippe Brucker kvm__register_ram(kvm, phys_start, phys_size, host_mem); 5157896feeSAndreas Herrmann } 527281a8dbSDavid Daney } 537281a8dbSDavid Daney 547281a8dbSDavid Daney void kvm__arch_delete_ram(struct kvm *kvm) 557281a8dbSDavid Daney { 567281a8dbSDavid Daney munmap(kvm->ram_start, kvm->ram_size); 577281a8dbSDavid Daney } 587281a8dbSDavid Daney 597281a8dbSDavid Daney void kvm__arch_set_cmdline(char *cmdline, bool video) 607281a8dbSDavid Daney { 617281a8dbSDavid Daney 627281a8dbSDavid Daney } 637281a8dbSDavid Daney 647281a8dbSDavid Daney /* Architecture-specific KVM init */ 65*5e9c654eSJulien Grall void kvm__arch_init(struct kvm *kvm) 667281a8dbSDavid Daney { 677281a8dbSDavid Daney int ret; 687281a8dbSDavid Daney 69*5e9c654eSJulien Grall kvm->ram_size = kvm->cfg.ram_size; 70*5e9c654eSJulien Grall kvm->ram_start = mmap_anon_or_hugetlbfs(kvm, kvm->cfg.hugetlbfs_path, 71*5e9c654eSJulien Grall kvm->ram_size); 727281a8dbSDavid Daney 737281a8dbSDavid Daney if (kvm->ram_start == MAP_FAILED) 747281a8dbSDavid Daney die("out of memory"); 757281a8dbSDavid Daney 767281a8dbSDavid Daney madvise(kvm->ram_start, kvm->ram_size, MADV_MERGEABLE); 777281a8dbSDavid Daney 787281a8dbSDavid Daney ret = ioctl(kvm->vm_fd, KVM_CREATE_IRQCHIP); 797281a8dbSDavid Daney if (ret < 0) 807281a8dbSDavid Daney die_perror("KVM_CREATE_IRQCHIP ioctl"); 817281a8dbSDavid Daney } 827281a8dbSDavid Daney 837281a8dbSDavid Daney void kvm__irq_line(struct kvm *kvm, int irq, int level) 847281a8dbSDavid Daney { 857281a8dbSDavid Daney struct kvm_irq_level irq_level; 867281a8dbSDavid Daney int ret; 877281a8dbSDavid Daney 887281a8dbSDavid Daney irq_level.irq = irq; 897281a8dbSDavid Daney irq_level.level = level ? 1 : 0; 907281a8dbSDavid Daney 917281a8dbSDavid Daney ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level); 927281a8dbSDavid Daney if (ret < 0) 937281a8dbSDavid Daney die_perror("KVM_IRQ_LINE ioctl"); 947281a8dbSDavid Daney } 957281a8dbSDavid Daney 967281a8dbSDavid Daney void kvm__irq_trigger(struct kvm *kvm, int irq) 977281a8dbSDavid Daney { 987281a8dbSDavid Daney struct kvm_irq_level irq_level; 997281a8dbSDavid Daney int ret; 1007281a8dbSDavid Daney 1017281a8dbSDavid Daney irq_level.irq = irq; 1027281a8dbSDavid Daney irq_level.level = 1; 1037281a8dbSDavid Daney 1047281a8dbSDavid Daney ret = ioctl(kvm->vm_fd, KVM_IRQ_LINE, &irq_level); 1057281a8dbSDavid Daney if (ret < 0) 1067281a8dbSDavid Daney die_perror("KVM_IRQ_LINE ioctl"); 1077281a8dbSDavid Daney } 1087281a8dbSDavid Daney 1097281a8dbSDavid Daney bool kvm__arch_cpu_supports_vm(void) 1107281a8dbSDavid Daney { 1117281a8dbSDavid Daney return true; 1127281a8dbSDavid Daney } 1137281a8dbSDavid Daney bool kvm__load_firmware(struct kvm *kvm, const char *firmware_filename) 1147281a8dbSDavid Daney { 1157281a8dbSDavid Daney return false; 1167281a8dbSDavid Daney } 1177281a8dbSDavid Daney int kvm__arch_setup_firmware(struct kvm *kvm) 1187281a8dbSDavid Daney { 1197281a8dbSDavid Daney return 0; 1207281a8dbSDavid Daney } 1217281a8dbSDavid Daney 1221ade2d7dSDavid Daney static void kvm__mips_install_cmdline(struct kvm *kvm) 1231ade2d7dSDavid Daney { 1241ade2d7dSDavid Daney char *p = kvm->ram_start; 1251ade2d7dSDavid Daney u64 cmdline_offset = 0x2000; 1261ade2d7dSDavid Daney u64 argv_start = 0x3000; 1271ade2d7dSDavid Daney u64 argv_offset = argv_start; 1281ade2d7dSDavid Daney u64 argc = 0; 1291ade2d7dSDavid Daney 13057896feeSAndreas Herrmann 13157896feeSAndreas Herrmann if ((u64) kvm->ram_size <= KVM_MMIO_START) 1321ade2d7dSDavid Daney sprintf(p + cmdline_offset, "mem=0x%llx@0 ", 1331ade2d7dSDavid Daney (unsigned long long)kvm->ram_size); 13457896feeSAndreas Herrmann else 13557896feeSAndreas Herrmann sprintf(p + cmdline_offset, "mem=0x%llx@0 mem=0x%llx@0x%llx ", 13657896feeSAndreas Herrmann (unsigned long long)KVM_MMIO_START, 13757896feeSAndreas Herrmann (unsigned long long)kvm->ram_size - KVM_MMIO_START, 13857896feeSAndreas Herrmann (unsigned long long)(KVM_MMIO_START + KVM_MMIO_SIZE)); 1391ade2d7dSDavid Daney 1405613ae26SAlexandru Elisei if (kvm->cfg.real_cmdline) 1411ade2d7dSDavid Daney strcat(p + cmdline_offset, kvm->cfg.real_cmdline); /* maximum size is 2K */ 1421ade2d7dSDavid Daney 1431ade2d7dSDavid Daney while (p[cmdline_offset]) { 1441ade2d7dSDavid Daney if (!isspace(p[cmdline_offset])) { 1451ade2d7dSDavid Daney if (kvm->arch.is64bit) { 1461ade2d7dSDavid Daney *(u64 *)(p + argv_offset) = 0xffffffff80000000ull + cmdline_offset; 1471ade2d7dSDavid Daney argv_offset += sizeof(u64); 1481ade2d7dSDavid Daney } else { 1491ade2d7dSDavid Daney *(u32 *)(p + argv_offset) = 0x80000000u + cmdline_offset; 1501ade2d7dSDavid Daney argv_offset += sizeof(u32); 1511ade2d7dSDavid Daney } 1521ade2d7dSDavid Daney argc++; 1531ade2d7dSDavid Daney while(p[cmdline_offset] && !isspace(p[cmdline_offset])) 1541ade2d7dSDavid Daney cmdline_offset++; 1551ade2d7dSDavid Daney continue; 1561ade2d7dSDavid Daney } 1571ade2d7dSDavid Daney /* Must be a space character skip over these*/ 1581ade2d7dSDavid Daney while(p[cmdline_offset] && isspace(p[cmdline_offset])) { 1591ade2d7dSDavid Daney p[cmdline_offset] = 0; 1601ade2d7dSDavid Daney cmdline_offset++; 1611ade2d7dSDavid Daney } 1621ade2d7dSDavid Daney } 1631ade2d7dSDavid Daney kvm->arch.argc = argc; 1641ade2d7dSDavid Daney kvm->arch.argv = 0xffffffff80000000ull + argv_start; 1651ade2d7dSDavid Daney } 1661ade2d7dSDavid Daney 1677281a8dbSDavid Daney /* Load at the 1M point. */ 1687281a8dbSDavid Daney #define KERNEL_LOAD_ADDR 0x1000000 169004f7684SAndre Przywara 170004f7684SAndre Przywara static bool load_flat_binary(struct kvm *kvm, int fd_kernel) 1717281a8dbSDavid Daney { 1727281a8dbSDavid Daney void *p; 1737281a8dbSDavid Daney void *k_start; 17412225973SAndre Przywara ssize_t kernel_size; 1757281a8dbSDavid Daney 1767281a8dbSDavid Daney if (lseek(fd_kernel, 0, SEEK_SET) < 0) 1777281a8dbSDavid Daney die_perror("lseek"); 1787281a8dbSDavid Daney 1797281a8dbSDavid Daney p = k_start = guest_flat_to_host(kvm, KERNEL_LOAD_ADDR); 1807281a8dbSDavid Daney 18112225973SAndre Przywara kernel_size = read_file(fd_kernel, p, 18212225973SAndre Przywara kvm->cfg.ram_size - KERNEL_LOAD_ADDR); 18312225973SAndre Przywara if (kernel_size == -1) { 18412225973SAndre Przywara if (errno == ENOMEM) 18512225973SAndre Przywara die("kernel too big for guest memory"); 18612225973SAndre Przywara else 18712225973SAndre Przywara die_perror("kernel read"); 18812225973SAndre Przywara } 1897281a8dbSDavid Daney 1907281a8dbSDavid Daney kvm->arch.is64bit = true; 1917281a8dbSDavid Daney kvm->arch.entry_point = 0xffffffff81000000ull; 1927281a8dbSDavid Daney 19312225973SAndre Przywara pr_info("Loaded kernel to 0x%x (%zd bytes)", KERNEL_LOAD_ADDR, 19412225973SAndre Przywara kernel_size); 1957281a8dbSDavid Daney 1967281a8dbSDavid Daney return true; 1977281a8dbSDavid Daney } 1987281a8dbSDavid Daney 1991ade2d7dSDavid Daney struct kvm__arch_elf_info { 2001ade2d7dSDavid Daney u64 load_addr; 2011ade2d7dSDavid Daney u64 entry_point; 2021ade2d7dSDavid Daney size_t len; 2031ade2d7dSDavid Daney size_t offset; 2041ade2d7dSDavid Daney }; 2051ade2d7dSDavid Daney 2061ade2d7dSDavid Daney static bool kvm__arch_get_elf_64_info(Elf64_Ehdr *ehdr, int fd_kernel, 2071ade2d7dSDavid Daney struct kvm__arch_elf_info *ei) 2081ade2d7dSDavid Daney { 2091ade2d7dSDavid Daney int i; 2101ade2d7dSDavid Daney Elf64_Phdr phdr; 2111ade2d7dSDavid Daney 2121ade2d7dSDavid Daney if (ehdr->e_phentsize != sizeof(phdr)) { 2131ade2d7dSDavid Daney pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize); 2141ade2d7dSDavid Daney return false; 2151ade2d7dSDavid Daney } 2161ade2d7dSDavid Daney 2171ade2d7dSDavid Daney ei->entry_point = ehdr->e_entry; 2181ade2d7dSDavid Daney 2191ade2d7dSDavid Daney if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0) 2201ade2d7dSDavid Daney die_perror("lseek"); 2211ade2d7dSDavid Daney 2221ade2d7dSDavid Daney phdr.p_type = PT_NULL; 2231ade2d7dSDavid Daney for (i = 0; i < ehdr->e_phnum; i++) { 22412225973SAndre Przywara if (read_in_full(fd_kernel, &phdr, sizeof(phdr)) != sizeof(phdr)) { 2251ade2d7dSDavid Daney pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr)); 2261ade2d7dSDavid Daney return false; 2271ade2d7dSDavid Daney } 2281ade2d7dSDavid Daney if (phdr.p_type == PT_LOAD) 2291ade2d7dSDavid Daney break; 2301ade2d7dSDavid Daney } 2311ade2d7dSDavid Daney if (phdr.p_type != PT_LOAD) { 2321ade2d7dSDavid Daney pr_info("No PT_LOAD Program Header found."); 2331ade2d7dSDavid Daney return false; 2341ade2d7dSDavid Daney } 2351ade2d7dSDavid Daney 2361ade2d7dSDavid Daney ei->load_addr = phdr.p_paddr; 2371ade2d7dSDavid Daney 2381ade2d7dSDavid Daney if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull) 2391ade2d7dSDavid Daney ei->load_addr &= 0x1ffffffful; /* Convert KSEG{0,1} to physical. */ 2401ade2d7dSDavid Daney if ((ei->load_addr & 0xc000000000000000ull) == 0x8000000000000000ull) 2411ade2d7dSDavid Daney ei->load_addr &= 0x07ffffffffffffffull; /* Convert XKPHYS to pysical */ 2421ade2d7dSDavid Daney 2431ade2d7dSDavid Daney 2441ade2d7dSDavid Daney ei->len = phdr.p_filesz; 2451ade2d7dSDavid Daney ei->offset = phdr.p_offset; 2461ade2d7dSDavid Daney 2471ade2d7dSDavid Daney return true; 2481ade2d7dSDavid Daney } 2491ade2d7dSDavid Daney 2501ade2d7dSDavid Daney static bool kvm__arch_get_elf_32_info(Elf32_Ehdr *ehdr, int fd_kernel, 2511ade2d7dSDavid Daney struct kvm__arch_elf_info *ei) 2521ade2d7dSDavid Daney { 2531ade2d7dSDavid Daney int i; 2541ade2d7dSDavid Daney Elf32_Phdr phdr; 2551ade2d7dSDavid Daney 2561ade2d7dSDavid Daney if (ehdr->e_phentsize != sizeof(phdr)) { 2571ade2d7dSDavid Daney pr_info("Incompatible ELF PHENTSIZE %d", ehdr->e_phentsize); 2581ade2d7dSDavid Daney return false; 2591ade2d7dSDavid Daney } 2601ade2d7dSDavid Daney 2611ade2d7dSDavid Daney ei->entry_point = (s64)((s32)ehdr->e_entry); 2621ade2d7dSDavid Daney 2631ade2d7dSDavid Daney if (lseek(fd_kernel, ehdr->e_phoff, SEEK_SET) < 0) 2641ade2d7dSDavid Daney die_perror("lseek"); 2651ade2d7dSDavid Daney 2661ade2d7dSDavid Daney phdr.p_type = PT_NULL; 2671ade2d7dSDavid Daney for (i = 0; i < ehdr->e_phnum; i++) { 26812225973SAndre Przywara if (read_in_full(fd_kernel, &phdr, sizeof(phdr)) != sizeof(phdr)) { 2691ade2d7dSDavid Daney pr_info("Couldn't read %d bytes for ELF PHDR.", (int)sizeof(phdr)); 2701ade2d7dSDavid Daney return false; 2711ade2d7dSDavid Daney } 2721ade2d7dSDavid Daney if (phdr.p_type == PT_LOAD) 2731ade2d7dSDavid Daney break; 2741ade2d7dSDavid Daney } 2751ade2d7dSDavid Daney if (phdr.p_type != PT_LOAD) { 2761ade2d7dSDavid Daney pr_info("No PT_LOAD Program Header found."); 2771ade2d7dSDavid Daney return false; 2781ade2d7dSDavid Daney } 2791ade2d7dSDavid Daney 2801ade2d7dSDavid Daney ei->load_addr = (s64)((s32)phdr.p_paddr); 2811ade2d7dSDavid Daney 2821ade2d7dSDavid Daney if ((ei->load_addr & 0xffffffffc0000000ull) == 0xffffffff80000000ull) 2831ade2d7dSDavid Daney ei->load_addr &= 0x1fffffffull; /* Convert KSEG{0,1} to physical. */ 2841ade2d7dSDavid Daney 2851ade2d7dSDavid Daney ei->len = phdr.p_filesz; 2861ade2d7dSDavid Daney ei->offset = phdr.p_offset; 2871ade2d7dSDavid Daney 2881ade2d7dSDavid Daney return true; 2891ade2d7dSDavid Daney } 2901ade2d7dSDavid Daney 291004f7684SAndre Przywara static bool load_elf_binary(struct kvm *kvm, int fd_kernel) 2921ade2d7dSDavid Daney { 2931ade2d7dSDavid Daney union { 2941ade2d7dSDavid Daney Elf64_Ehdr ehdr; 2951ade2d7dSDavid Daney Elf32_Ehdr ehdr32; 2961ade2d7dSDavid Daney } eh; 2971ade2d7dSDavid Daney 2981ade2d7dSDavid Daney size_t nr; 2991ade2d7dSDavid Daney char *p; 3001ade2d7dSDavid Daney struct kvm__arch_elf_info ei; 3011ade2d7dSDavid Daney 3021ade2d7dSDavid Daney nr = read(fd_kernel, &eh, sizeof(eh)); 3031ade2d7dSDavid Daney if (nr != sizeof(eh)) { 3041ade2d7dSDavid Daney pr_info("Couldn't read %d bytes for ELF header.", (int)sizeof(eh)); 3051ade2d7dSDavid Daney return false; 3061ade2d7dSDavid Daney } 3071ade2d7dSDavid Daney 3081ade2d7dSDavid Daney if (eh.ehdr.e_ident[EI_MAG0] != ELFMAG0 || 3091ade2d7dSDavid Daney eh.ehdr.e_ident[EI_MAG1] != ELFMAG1 || 3101ade2d7dSDavid Daney eh.ehdr.e_ident[EI_MAG2] != ELFMAG2 || 3111ade2d7dSDavid Daney eh.ehdr.e_ident[EI_MAG3] != ELFMAG3 || 3121ade2d7dSDavid Daney (eh.ehdr.e_ident[EI_CLASS] != ELFCLASS64 && eh.ehdr.e_ident[EI_CLASS] != ELFCLASS32) || 3131ade2d7dSDavid Daney eh.ehdr.e_ident[EI_VERSION] != EV_CURRENT) { 3141ade2d7dSDavid Daney pr_info("Incompatible ELF header."); 3151ade2d7dSDavid Daney return false; 3161ade2d7dSDavid Daney } 3171ade2d7dSDavid Daney if (eh.ehdr.e_type != ET_EXEC || eh.ehdr.e_machine != EM_MIPS) { 3181ade2d7dSDavid Daney pr_info("Incompatible ELF not MIPS EXEC."); 3191ade2d7dSDavid Daney return false; 3201ade2d7dSDavid Daney } 3211ade2d7dSDavid Daney 3221ade2d7dSDavid Daney if (eh.ehdr.e_ident[EI_CLASS] == ELFCLASS64) { 3231ade2d7dSDavid Daney if (!kvm__arch_get_elf_64_info(&eh.ehdr, fd_kernel, &ei)) 3241ade2d7dSDavid Daney return false; 3251ade2d7dSDavid Daney kvm->arch.is64bit = true; 3261ade2d7dSDavid Daney } else { 3271ade2d7dSDavid Daney if (!kvm__arch_get_elf_32_info(&eh.ehdr32, fd_kernel, &ei)) 3281ade2d7dSDavid Daney return false; 3291ade2d7dSDavid Daney kvm->arch.is64bit = false; 3301ade2d7dSDavid Daney } 3311ade2d7dSDavid Daney 3321ade2d7dSDavid Daney kvm->arch.entry_point = ei.entry_point; 3331ade2d7dSDavid Daney 3341ade2d7dSDavid Daney if (lseek(fd_kernel, ei.offset, SEEK_SET) < 0) 3351ade2d7dSDavid Daney die_perror("lseek"); 3361ade2d7dSDavid Daney 3371ade2d7dSDavid Daney p = guest_flat_to_host(kvm, ei.load_addr); 3381ade2d7dSDavid Daney 3391ade2d7dSDavid Daney pr_info("ELF Loading 0x%lx bytes from 0x%llx to 0x%llx", 34012225973SAndre Przywara (unsigned long)ei.len, (unsigned long long)ei.offset, 34112225973SAndre Przywara (unsigned long long)ei.load_addr); 34212225973SAndre Przywara 34312225973SAndre Przywara if (read_in_full(fd_kernel, p, ei.len) != (ssize_t)ei.len) 3441ade2d7dSDavid Daney die_perror("read"); 3451ade2d7dSDavid Daney 3461ade2d7dSDavid Daney return true; 3471ade2d7dSDavid Daney } 3481ade2d7dSDavid Daney 349004f7684SAndre Przywara bool kvm__arch_load_kernel_image(struct kvm *kvm, int fd_kernel, int fd_initrd, 350004f7684SAndre Przywara const char *kernel_cmdline) 351004f7684SAndre Przywara { 352004f7684SAndre Przywara if (fd_initrd != -1) { 353004f7684SAndre Przywara pr_err("Initrd not supported on MIPS."); 354004f7684SAndre Przywara return false; 355004f7684SAndre Przywara } 356004f7684SAndre Przywara 357004f7684SAndre Przywara if (load_elf_binary(kvm, fd_kernel)) { 358004f7684SAndre Przywara kvm__mips_install_cmdline(kvm); 359004f7684SAndre Przywara return true; 360004f7684SAndre Przywara } 361004f7684SAndre Przywara 362004f7684SAndre Przywara return load_flat_binary(kvm, fd_kernel); 363004f7684SAndre Przywara } 364004f7684SAndre Przywara 3657281a8dbSDavid Daney void ioport__map_irq(u8 *irq) 3667281a8dbSDavid Daney { 3677281a8dbSDavid Daney } 3686f6f384cSAlexandru Elisei 3696f6f384cSAlexandru Elisei void serial8250__inject_sysrq(struct kvm *kvm, char sysrq) 3706f6f384cSAlexandru Elisei { 3716f6f384cSAlexandru Elisei } 372