Path: blob/master/tools/testing/selftests/kvm/lib/riscv/processor.c
49473 views
// SPDX-License-Identifier: GPL-2.01/*2* RISC-V code3*4* Copyright (C) 2021 Western Digital Corporation or its affiliates.5*/67#include <linux/compiler.h>8#include <assert.h>910#include "kvm_util.h"11#include "processor.h"12#include "ucall_common.h"1314#define DEFAULT_RISCV_GUEST_STACK_VADDR_MIN 0xac00001516static vm_vaddr_t exception_handlers;1718bool __vcpu_has_ext(struct kvm_vcpu *vcpu, uint64_t ext)19{20unsigned long value = 0;21int ret;2223ret = __vcpu_get_reg(vcpu, ext, &value);2425return !ret && !!value;26}2728static uint64_t page_align(struct kvm_vm *vm, uint64_t v)29{30return (v + vm->page_size) & ~(vm->page_size - 1);31}3233static uint64_t pte_addr(struct kvm_vm *vm, uint64_t entry)34{35return ((entry & PGTBL_PTE_ADDR_MASK) >> PGTBL_PTE_ADDR_SHIFT) <<36PGTBL_PAGE_SIZE_SHIFT;37}3839static uint64_t ptrs_per_pte(struct kvm_vm *vm)40{41return PGTBL_PAGE_SIZE / sizeof(uint64_t);42}4344static uint64_t pte_index_mask[] = {45PGTBL_L0_INDEX_MASK,46PGTBL_L1_INDEX_MASK,47PGTBL_L2_INDEX_MASK,48PGTBL_L3_INDEX_MASK,49};5051static uint32_t pte_index_shift[] = {52PGTBL_L0_INDEX_SHIFT,53PGTBL_L1_INDEX_SHIFT,54PGTBL_L2_INDEX_SHIFT,55PGTBL_L3_INDEX_SHIFT,56};5758static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva, int level)59{60TEST_ASSERT(level > -1,61"Negative page table level (%d) not possible", level);62TEST_ASSERT(level < vm->pgtable_levels,63"Invalid page table level (%d)", level);6465return (gva & pte_index_mask[level]) >> pte_index_shift[level];66}6768void virt_arch_pgd_alloc(struct kvm_vm *vm)69{70size_t nr_pages = page_align(vm, ptrs_per_pte(vm) * 8) / vm->page_size;7172if (vm->pgd_created)73return;7475vm->pgd = vm_phy_pages_alloc(vm, nr_pages,76KVM_GUEST_PAGE_TABLE_MIN_PADDR,77vm->memslots[MEM_REGION_PT]);78vm->pgd_created = true;79}8081void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)82{83uint64_t *ptep, next_ppn;84int level = vm->pgtable_levels - 1;8586TEST_ASSERT((vaddr % vm->page_size) == 0,87"Virtual address not on page boundary,\n"88" vaddr: 0x%lx vm->page_size: 0x%x", vaddr, vm->page_size);89TEST_ASSERT(sparsebit_is_set(vm->vpages_valid,90(vaddr >> vm->page_shift)),91"Invalid virtual address, vaddr: 0x%lx", vaddr);92TEST_ASSERT((paddr % vm->page_size) == 0,93"Physical address not on page boundary,\n"94" paddr: 0x%lx vm->page_size: 0x%x", paddr, vm->page_size);95TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn,96"Physical address beyond maximum supported,\n"97" paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",98paddr, vm->max_gfn, vm->page_size);99100ptep = addr_gpa2hva(vm, vm->pgd) + pte_index(vm, vaddr, level) * 8;101if (!*ptep) {102next_ppn = vm_alloc_page_table(vm) >> PGTBL_PAGE_SIZE_SHIFT;103*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |104PGTBL_PTE_VALID_MASK;105}106level--;107108while (level > -1) {109ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +110pte_index(vm, vaddr, level) * 8;111if (!*ptep && level > 0) {112next_ppn = vm_alloc_page_table(vm) >>113PGTBL_PAGE_SIZE_SHIFT;114*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |115PGTBL_PTE_VALID_MASK;116}117level--;118}119120paddr = paddr >> PGTBL_PAGE_SIZE_SHIFT;121*ptep = (paddr << PGTBL_PTE_ADDR_SHIFT) |122PGTBL_PTE_PERM_MASK | PGTBL_PTE_VALID_MASK;123}124125vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)126{127uint64_t *ptep;128int level = vm->pgtable_levels - 1;129130if (!vm->pgd_created)131goto unmapped_gva;132133ptep = addr_gpa2hva(vm, vm->pgd) + pte_index(vm, gva, level) * 8;134if (!ptep)135goto unmapped_gva;136level--;137138while (level > -1) {139ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +140pte_index(vm, gva, level) * 8;141if (!ptep)142goto unmapped_gva;143level--;144}145146return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1));147148unmapped_gva:149TEST_FAIL("No mapping for vm virtual address gva: 0x%lx level: %d",150gva, level);151exit(1);152}153154static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent,155uint64_t page, int level)156{157#ifdef DEBUG158static const char *const type[] = { "pte", "pmd", "pud", "p4d"};159uint64_t pte, *ptep;160161if (level < 0)162return;163164for (pte = page; pte < page + ptrs_per_pte(vm) * 8; pte += 8) {165ptep = addr_gpa2hva(vm, pte);166if (!*ptep)167continue;168fprintf(stream, "%*s%s: %lx: %lx at %p\n", indent, "",169type[level], pte, *ptep, ptep);170pte_dump(stream, vm, indent + 1,171pte_addr(vm, *ptep), level - 1);172}173#endif174}175176void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)177{178int level = vm->pgtable_levels - 1;179uint64_t pgd, *ptep;180181if (!vm->pgd_created)182return;183184for (pgd = vm->pgd; pgd < vm->pgd + ptrs_per_pte(vm) * 8; pgd += 8) {185ptep = addr_gpa2hva(vm, pgd);186if (!*ptep)187continue;188fprintf(stream, "%*spgd: %lx: %lx at %p\n", indent, "",189pgd, *ptep, ptep);190pte_dump(stream, vm, indent + 1,191pte_addr(vm, *ptep), level - 1);192}193}194195void riscv_vcpu_mmu_setup(struct kvm_vcpu *vcpu)196{197struct kvm_vm *vm = vcpu->vm;198unsigned long satp;199200/*201* The RISC-V Sv48 MMU mode supports 56-bit physical address202* for 48-bit virtual address with 4KB last level page size.203*/204switch (vm->mode) {205case VM_MODE_P52V48_4K:206case VM_MODE_P48V48_4K:207case VM_MODE_P40V48_4K:208break;209default:210TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);211}212213satp = (vm->pgd >> PGTBL_PAGE_SIZE_SHIFT) & SATP_PPN;214satp |= SATP_MODE_48;215216vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(satp), satp);217}218219void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent)220{221struct kvm_riscv_core core;222223core.mode = vcpu_get_reg(vcpu, RISCV_CORE_REG(mode));224core.regs.pc = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.pc));225core.regs.ra = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.ra));226core.regs.sp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.sp));227core.regs.gp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.gp));228core.regs.tp = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.tp));229core.regs.t0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t0));230core.regs.t1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t1));231core.regs.t2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t2));232core.regs.s0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s0));233core.regs.s1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s1));234core.regs.a0 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a0));235core.regs.a1 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a1));236core.regs.a2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a2));237core.regs.a3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a3));238core.regs.a4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a4));239core.regs.a5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a5));240core.regs.a6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a6));241core.regs.a7 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a7));242core.regs.s2 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s2));243core.regs.s3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s3));244core.regs.s4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s4));245core.regs.s5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s5));246core.regs.s6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s6));247core.regs.s7 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s7));248core.regs.s8 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s8));249core.regs.s9 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s9));250core.regs.s10 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s10));251core.regs.s11 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s11));252core.regs.t3 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t3));253core.regs.t4 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t4));254core.regs.t5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t5));255core.regs.t6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t6));256257fprintf(stream,258" MODE: 0x%lx\n", core.mode);259fprintf(stream,260" PC: 0x%016lx RA: 0x%016lx SP: 0x%016lx GP: 0x%016lx\n",261core.regs.pc, core.regs.ra, core.regs.sp, core.regs.gp);262fprintf(stream,263" TP: 0x%016lx T0: 0x%016lx T1: 0x%016lx T2: 0x%016lx\n",264core.regs.tp, core.regs.t0, core.regs.t1, core.regs.t2);265fprintf(stream,266" S0: 0x%016lx S1: 0x%016lx A0: 0x%016lx A1: 0x%016lx\n",267core.regs.s0, core.regs.s1, core.regs.a0, core.regs.a1);268fprintf(stream,269" A2: 0x%016lx A3: 0x%016lx A4: 0x%016lx A5: 0x%016lx\n",270core.regs.a2, core.regs.a3, core.regs.a4, core.regs.a5);271fprintf(stream,272" A6: 0x%016lx A7: 0x%016lx S2: 0x%016lx S3: 0x%016lx\n",273core.regs.a6, core.regs.a7, core.regs.s2, core.regs.s3);274fprintf(stream,275" S4: 0x%016lx S5: 0x%016lx S6: 0x%016lx S7: 0x%016lx\n",276core.regs.s4, core.regs.s5, core.regs.s6, core.regs.s7);277fprintf(stream,278" S8: 0x%016lx S9: 0x%016lx S10: 0x%016lx S11: 0x%016lx\n",279core.regs.s8, core.regs.s9, core.regs.s10, core.regs.s11);280fprintf(stream,281" T3: 0x%016lx T4: 0x%016lx T5: 0x%016lx T6: 0x%016lx\n",282core.regs.t3, core.regs.t4, core.regs.t5, core.regs.t6);283}284285static void __aligned(16) guest_unexp_trap(void)286{287sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,288KVM_RISCV_SELFTESTS_SBI_UNEXP,2890, 0, 0, 0, 0, 0);290}291292void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)293{294vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.pc), (unsigned long)guest_code);295}296297struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)298{299int r;300size_t stack_size;301unsigned long stack_vaddr;302unsigned long current_gp = 0;303struct kvm_mp_state mps;304struct kvm_vcpu *vcpu;305306stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size :307vm->page_size;308stack_vaddr = __vm_vaddr_alloc(vm, stack_size,309DEFAULT_RISCV_GUEST_STACK_VADDR_MIN,310MEM_REGION_DATA);311312vcpu = __vm_vcpu_add(vm, vcpu_id);313riscv_vcpu_mmu_setup(vcpu);314315/*316* With SBI HSM support in KVM RISC-V, all secondary VCPUs are317* powered-off by default so we ensure that all secondary VCPUs318* are powered-on using KVM_SET_MP_STATE ioctl().319*/320mps.mp_state = KVM_MP_STATE_RUNNABLE;321r = __vcpu_ioctl(vcpu, KVM_SET_MP_STATE, &mps);322TEST_ASSERT(!r, "IOCTL KVM_SET_MP_STATE failed (error %d)", r);323324/* Setup global pointer of guest to be same as the host */325asm volatile (326"add %0, gp, zero" : "=r" (current_gp) : : "memory");327vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.gp), current_gp);328329/* Setup stack pointer and program counter of guest */330vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.sp), stack_vaddr + stack_size);331332/* Setup sscratch for guest_get_vcpuid() */333vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(sscratch), vcpu_id);334335/* Setup default exception vector of guest */336vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(stvec), (unsigned long)guest_unexp_trap);337338return vcpu;339}340341void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...)342{343va_list ap;344uint64_t id = RISCV_CORE_REG(regs.a0);345int i;346347TEST_ASSERT(num >= 1 && num <= 8, "Unsupported number of args,\n"348" num: %u", num);349350va_start(ap, num);351352for (i = 0; i < num; i++) {353switch (i) {354case 0:355id = RISCV_CORE_REG(regs.a0);356break;357case 1:358id = RISCV_CORE_REG(regs.a1);359break;360case 2:361id = RISCV_CORE_REG(regs.a2);362break;363case 3:364id = RISCV_CORE_REG(regs.a3);365break;366case 4:367id = RISCV_CORE_REG(regs.a4);368break;369case 5:370id = RISCV_CORE_REG(regs.a5);371break;372case 6:373id = RISCV_CORE_REG(regs.a6);374break;375case 7:376id = RISCV_CORE_REG(regs.a7);377break;378}379vcpu_set_reg(vcpu, id, va_arg(ap, uint64_t));380}381382va_end(ap);383}384385void kvm_exit_unexpected_exception(int vector, int ec)386{387ucall(UCALL_UNHANDLED, 2, vector, ec);388}389390void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)391{392struct ucall uc;393394if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) {395TEST_FAIL("Unexpected exception (vector:0x%lx, ec:0x%lx)",396uc.args[0], uc.args[1]);397}398}399400struct handlers {401exception_handler_fn exception_handlers[NR_VECTORS][NR_EXCEPTIONS];402};403404void route_exception(struct pt_regs *regs)405{406struct handlers *handlers = (struct handlers *)exception_handlers;407int vector = 0, ec;408409ec = regs->cause & ~CAUSE_IRQ_FLAG;410if (ec >= NR_EXCEPTIONS)411goto unexpected_exception;412413/* Use the same handler for all the interrupts */414if (regs->cause & CAUSE_IRQ_FLAG) {415vector = 1;416ec = 0;417}418419if (handlers && handlers->exception_handlers[vector][ec])420return handlers->exception_handlers[vector][ec](regs);421422unexpected_exception:423return kvm_exit_unexpected_exception(vector, ec);424}425426void vcpu_init_vector_tables(struct kvm_vcpu *vcpu)427{428extern char exception_vectors;429430vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(stvec), (unsigned long)&exception_vectors);431}432433void vm_init_vector_tables(struct kvm_vm *vm)434{435vm->handlers = __vm_vaddr_alloc(vm, sizeof(struct handlers),436vm->page_size, MEM_REGION_DATA);437438*(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers;439}440441void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handler_fn handler)442{443struct handlers *handlers = addr_gva2hva(vm, vm->handlers);444445assert(vector < NR_EXCEPTIONS);446handlers->exception_handlers[0][vector] = handler;447}448449void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler)450{451struct handlers *handlers = addr_gva2hva(vm, vm->handlers);452453handlers->exception_handlers[1][0] = handler;454}455456uint32_t guest_get_vcpuid(void)457{458return csr_read(CSR_SSCRATCH);459}460461struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,462unsigned long arg1, unsigned long arg2,463unsigned long arg3, unsigned long arg4,464unsigned long arg5)465{466register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);467register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);468register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);469register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);470register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);471register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);472register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);473register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);474struct sbiret ret;475476asm volatile (477"ecall"478: "+r" (a0), "+r" (a1)479: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)480: "memory");481ret.error = a0;482ret.value = a1;483484return ret;485}486487bool guest_sbi_probe_extension(int extid, long *out_val)488{489struct sbiret ret;490491ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid,4920, 0, 0, 0, 0);493494__GUEST_ASSERT(!ret.error || ret.error == SBI_ERR_NOT_SUPPORTED,495"ret.error=%ld, ret.value=%ld\n", ret.error, ret.value);496497if (ret.error == SBI_ERR_NOT_SUPPORTED)498return false;499500if (out_val)501*out_val = ret.value;502503return true;504}505506unsigned long get_host_sbi_spec_version(void)507{508struct sbiret ret;509510ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0,5110, 0, 0, 0, 0);512513GUEST_ASSERT(!ret.error);514515return ret.value;516}517518519