Commit 1b0c9d00 authored by Paolo Bonzini's avatar Paolo Bonzini

Merge tag 'kvm-riscv-5.17-1' of https://github.com/kvm-riscv/linux into HEAD

KVM/riscv changes for 5.17, take #1

- Use common KVM implementation of MMU memory caches
- SBI v0.2 support for Guest
- Initial KVM selftests support
- Fix to avoid spurious virtual interrupts after clearing hideleg CSR
- Update email address for Anup and Atish
parents 7fd55a02 497685f2
...@@ -46,10 +46,12 @@ Andy Adamson <andros@citi.umich.edu> ...@@ -46,10 +46,12 @@ Andy Adamson <andros@citi.umich.edu>
Antoine Tenart <atenart@kernel.org> <antoine.tenart@bootlin.com> Antoine Tenart <atenart@kernel.org> <antoine.tenart@bootlin.com>
Antoine Tenart <atenart@kernel.org> <antoine.tenart@free-electrons.com> Antoine Tenart <atenart@kernel.org> <antoine.tenart@free-electrons.com>
Antonio Ospite <ao2@ao2.it> <ao2@amarulasolutions.com> Antonio Ospite <ao2@ao2.it> <ao2@amarulasolutions.com>
Anup Patel <anup@brainfault.org> <anup.patel@wdc.com>
Archit Taneja <archit@ti.com> Archit Taneja <archit@ti.com>
Ard Biesheuvel <ardb@kernel.org> <ard.biesheuvel@linaro.org> Ard Biesheuvel <ardb@kernel.org> <ard.biesheuvel@linaro.org>
Arnaud Patard <arnaud.patard@rtp-net.org> Arnaud Patard <arnaud.patard@rtp-net.org>
Arnd Bergmann <arnd@arndb.de> Arnd Bergmann <arnd@arndb.de>
Atish Patra <atishp@atishpatra.org> <atish.patra@wdc.com>
Axel Dyks <xl@xlsigned.net> Axel Dyks <xl@xlsigned.net>
Axel Lin <axel.lin@gmail.com> Axel Lin <axel.lin@gmail.com>
Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com> Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com>
......
...@@ -10444,8 +10444,8 @@ F: arch/powerpc/kernel/kvm* ...@@ -10444,8 +10444,8 @@ F: arch/powerpc/kernel/kvm*
F: arch/powerpc/kvm/ F: arch/powerpc/kvm/
KERNEL VIRTUAL MACHINE FOR RISC-V (KVM/riscv) KERNEL VIRTUAL MACHINE FOR RISC-V (KVM/riscv)
M: Anup Patel <anup.patel@wdc.com> M: Anup Patel <anup@brainfault.org>
R: Atish Patra <atish.patra@wdc.com> R: Atish Patra <atishp@atishpatra.org>
L: kvm@vger.kernel.org L: kvm@vger.kernel.org
L: kvm-riscv@lists.infradead.org L: kvm-riscv@lists.infradead.org
L: linux-riscv@lists.infradead.org L: linux-riscv@lists.infradead.org
......
...@@ -77,13 +77,6 @@ struct kvm_sbi_context { ...@@ -77,13 +77,6 @@ struct kvm_sbi_context {
int return_handled; int return_handled;
}; };
#define KVM_MMU_PAGE_CACHE_NR_OBJS 32
struct kvm_mmu_page_cache {
int nobjs;
void *objects[KVM_MMU_PAGE_CACHE_NR_OBJS];
};
struct kvm_cpu_trap { struct kvm_cpu_trap {
unsigned long sepc; unsigned long sepc;
unsigned long scause; unsigned long scause;
...@@ -193,7 +186,7 @@ struct kvm_vcpu_arch { ...@@ -193,7 +186,7 @@ struct kvm_vcpu_arch {
struct kvm_sbi_context sbi_context; struct kvm_sbi_context sbi_context;
/* Cache pages needed to program page tables with spinlock held */ /* Cache pages needed to program page tables with spinlock held */
struct kvm_mmu_page_cache mmu_page_cache; struct kvm_mmu_memory_cache mmu_page_cache;
/* VCPU power-off state */ /* VCPU power-off state */
bool power_off; bool power_off;
...@@ -220,12 +213,12 @@ void __kvm_riscv_hfence_gvma_all(void); ...@@ -220,12 +213,12 @@ void __kvm_riscv_hfence_gvma_all(void);
int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu, int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
struct kvm_memory_slot *memslot, struct kvm_memory_slot *memslot,
gpa_t gpa, unsigned long hva, bool is_write); gpa_t gpa, unsigned long hva, bool is_write);
void kvm_riscv_stage2_flush_cache(struct kvm_vcpu *vcpu);
int kvm_riscv_stage2_alloc_pgd(struct kvm *kvm); int kvm_riscv_stage2_alloc_pgd(struct kvm *kvm);
void kvm_riscv_stage2_free_pgd(struct kvm *kvm); void kvm_riscv_stage2_free_pgd(struct kvm *kvm);
void kvm_riscv_stage2_update_hgatp(struct kvm_vcpu *vcpu); void kvm_riscv_stage2_update_hgatp(struct kvm_vcpu *vcpu);
void kvm_riscv_stage2_mode_detect(void); void kvm_riscv_stage2_mode_detect(void);
unsigned long kvm_riscv_stage2_mode(void); unsigned long kvm_riscv_stage2_mode(void);
int kvm_riscv_stage2_gpa_bits(void);
void kvm_riscv_stage2_vmid_detect(void); void kvm_riscv_stage2_vmid_detect(void);
unsigned long kvm_riscv_stage2_vmid_bits(void); unsigned long kvm_riscv_stage2_vmid_bits(void);
......
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
#ifndef _ASM_RISCV_KVM_TYPES_H #ifndef _ASM_RISCV_KVM_TYPES_H
#define _ASM_RISCV_KVM_TYPES_H #define _ASM_RISCV_KVM_TYPES_H
#define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 40 #define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 32
#endif /* _ASM_RISCV_KVM_TYPES_H */ #endif /* _ASM_RISCV_KVM_TYPES_H */
/* SPDX-License-Identifier: GPL-2.0-only */
/**
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#ifndef __RISCV_KVM_VCPU_SBI_H__
#define __RISCV_KVM_VCPU_SBI_H__
#define KVM_SBI_IMPID 3
#define KVM_SBI_VERSION_MAJOR 0
#define KVM_SBI_VERSION_MINOR 2
struct kvm_vcpu_sbi_extension {
unsigned long extid_start;
unsigned long extid_end;
/**
* SBI extension handler. It can be defined for a given extension or group of
* extension. But it should always return linux error codes rather than SBI
* specific error codes.
*/
int (*handler)(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val, struct kvm_cpu_trap *utrap,
bool *exit);
};
void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run);
const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid);
#endif /* __RISCV_KVM_VCPU_SBI_H__ */
...@@ -27,6 +27,14 @@ enum sbi_ext_id { ...@@ -27,6 +27,14 @@ enum sbi_ext_id {
SBI_EXT_IPI = 0x735049, SBI_EXT_IPI = 0x735049,
SBI_EXT_RFENCE = 0x52464E43, SBI_EXT_RFENCE = 0x52464E43,
SBI_EXT_HSM = 0x48534D, SBI_EXT_HSM = 0x48534D,
/* Experimentals extensions must lie within this range */
SBI_EXT_EXPERIMENTAL_START = 0x08000000,
SBI_EXT_EXPERIMENTAL_END = 0x08FFFFFF,
/* Vendor extensions must lie within this range */
SBI_EXT_VENDOR_START = 0x09000000,
SBI_EXT_VENDOR_END = 0x09FFFFFF,
}; };
enum sbi_ext_base_fid { enum sbi_ext_base_fid {
...@@ -82,6 +90,7 @@ enum sbi_hsm_hart_status { ...@@ -82,6 +90,7 @@ enum sbi_hsm_hart_status {
#define SBI_ERR_INVALID_PARAM -3 #define SBI_ERR_INVALID_PARAM -3
#define SBI_ERR_DENIED -4 #define SBI_ERR_DENIED -4
#define SBI_ERR_INVALID_ADDRESS -5 #define SBI_ERR_INVALID_ADDRESS -5
#define SBI_ERR_ALREADY_AVAILABLE -6
extern unsigned long sbi_spec_version; extern unsigned long sbi_spec_version;
struct sbiret { struct sbiret {
......
...@@ -19,4 +19,8 @@ kvm-y += vcpu_exit.o ...@@ -19,4 +19,8 @@ kvm-y += vcpu_exit.o
kvm-y += vcpu_fp.o kvm-y += vcpu_fp.o
kvm-y += vcpu_switch.o kvm-y += vcpu_switch.o
kvm-y += vcpu_sbi.o kvm-y += vcpu_sbi.o
kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o
kvm-y += vcpu_sbi_base.o
kvm-y += vcpu_sbi_replace.o
kvm-y += vcpu_sbi_hsm.o
kvm-y += vcpu_timer.o kvm-y += vcpu_timer.o
...@@ -58,6 +58,14 @@ int kvm_arch_hardware_enable(void) ...@@ -58,6 +58,14 @@ int kvm_arch_hardware_enable(void)
void kvm_arch_hardware_disable(void) void kvm_arch_hardware_disable(void)
{ {
/*
* After clearing the hideleg CSR, the host kernel will receive
* spurious interrupts if hvip CSR has pending interrupts and the
* corresponding enable bits in vsie CSR are asserted. To avoid it,
* hvip CSR and vsie CSR must be cleared before clearing hideleg CSR.
*/
csr_write(CSR_VSIE, 0);
csr_write(CSR_HVIP, 0);
csr_write(CSR_HEDELEG, 0); csr_write(CSR_HEDELEG, 0);
csr_write(CSR_HIDELEG, 0); csr_write(CSR_HIDELEG, 0);
} }
......
...@@ -83,43 +83,6 @@ static int stage2_level_to_page_size(u32 level, unsigned long *out_pgsize) ...@@ -83,43 +83,6 @@ static int stage2_level_to_page_size(u32 level, unsigned long *out_pgsize)
return 0; return 0;
} }
static int stage2_cache_topup(struct kvm_mmu_page_cache *pcache,
int min, int max)
{
void *page;
BUG_ON(max > KVM_MMU_PAGE_CACHE_NR_OBJS);
if (pcache->nobjs >= min)
return 0;
while (pcache->nobjs < max) {
page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
return -ENOMEM;
pcache->objects[pcache->nobjs++] = page;
}
return 0;
}
static void stage2_cache_flush(struct kvm_mmu_page_cache *pcache)
{
while (pcache && pcache->nobjs)
free_page((unsigned long)pcache->objects[--pcache->nobjs]);
}
static void *stage2_cache_alloc(struct kvm_mmu_page_cache *pcache)
{
void *p;
if (!pcache)
return NULL;
BUG_ON(!pcache->nobjs);
p = pcache->objects[--pcache->nobjs];
return p;
}
static bool stage2_get_leaf_entry(struct kvm *kvm, gpa_t addr, static bool stage2_get_leaf_entry(struct kvm *kvm, gpa_t addr,
pte_t **ptepp, u32 *ptep_level) pte_t **ptepp, u32 *ptep_level)
{ {
...@@ -171,7 +134,7 @@ static void stage2_remote_tlb_flush(struct kvm *kvm, u32 level, gpa_t addr) ...@@ -171,7 +134,7 @@ static void stage2_remote_tlb_flush(struct kvm *kvm, u32 level, gpa_t addr)
} }
static int stage2_set_pte(struct kvm *kvm, u32 level, static int stage2_set_pte(struct kvm *kvm, u32 level,
struct kvm_mmu_page_cache *pcache, struct kvm_mmu_memory_cache *pcache,
gpa_t addr, const pte_t *new_pte) gpa_t addr, const pte_t *new_pte)
{ {
u32 current_level = stage2_pgd_levels - 1; u32 current_level = stage2_pgd_levels - 1;
...@@ -186,7 +149,9 @@ static int stage2_set_pte(struct kvm *kvm, u32 level, ...@@ -186,7 +149,9 @@ static int stage2_set_pte(struct kvm *kvm, u32 level,
return -EEXIST; return -EEXIST;
if (!pte_val(*ptep)) { if (!pte_val(*ptep)) {
next_ptep = stage2_cache_alloc(pcache); if (!pcache)
return -ENOMEM;
next_ptep = kvm_mmu_memory_cache_alloc(pcache);
if (!next_ptep) if (!next_ptep)
return -ENOMEM; return -ENOMEM;
*ptep = pfn_pte(PFN_DOWN(__pa(next_ptep)), *ptep = pfn_pte(PFN_DOWN(__pa(next_ptep)),
...@@ -209,7 +174,7 @@ static int stage2_set_pte(struct kvm *kvm, u32 level, ...@@ -209,7 +174,7 @@ static int stage2_set_pte(struct kvm *kvm, u32 level,
} }
static int stage2_map_page(struct kvm *kvm, static int stage2_map_page(struct kvm *kvm,
struct kvm_mmu_page_cache *pcache, struct kvm_mmu_memory_cache *pcache,
gpa_t gpa, phys_addr_t hpa, gpa_t gpa, phys_addr_t hpa,
unsigned long page_size, unsigned long page_size,
bool page_rdonly, bool page_exec) bool page_rdonly, bool page_exec)
...@@ -384,7 +349,10 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, ...@@ -384,7 +349,10 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
int ret = 0; int ret = 0;
unsigned long pfn; unsigned long pfn;
phys_addr_t addr, end; phys_addr_t addr, end;
struct kvm_mmu_page_cache pcache = { 0, }; struct kvm_mmu_memory_cache pcache;
memset(&pcache, 0, sizeof(pcache));
pcache.gfp_zero = __GFP_ZERO;
end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK; end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK;
pfn = __phys_to_pfn(hpa); pfn = __phys_to_pfn(hpa);
...@@ -395,9 +363,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, ...@@ -395,9 +363,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
if (!writable) if (!writable)
pte = pte_wrprotect(pte); pte = pte_wrprotect(pte);
ret = stage2_cache_topup(&pcache, ret = kvm_mmu_topup_memory_cache(&pcache, stage2_pgd_levels);
stage2_pgd_levels,
KVM_MMU_PAGE_CACHE_NR_OBJS);
if (ret) if (ret)
goto out; goto out;
...@@ -411,7 +377,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, ...@@ -411,7 +377,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
} }
out: out:
stage2_cache_flush(&pcache); kvm_mmu_free_memory_cache(&pcache);
return ret; return ret;
} }
...@@ -649,7 +615,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu, ...@@ -649,7 +615,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
gfn_t gfn = gpa >> PAGE_SHIFT; gfn_t gfn = gpa >> PAGE_SHIFT;
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct kvm *kvm = vcpu->kvm; struct kvm *kvm = vcpu->kvm;
struct kvm_mmu_page_cache *pcache = &vcpu->arch.mmu_page_cache; struct kvm_mmu_memory_cache *pcache = &vcpu->arch.mmu_page_cache;
bool logging = (memslot->dirty_bitmap && bool logging = (memslot->dirty_bitmap &&
!(memslot->flags & KVM_MEM_READONLY)) ? true : false; !(memslot->flags & KVM_MEM_READONLY)) ? true : false;
unsigned long vma_pagesize, mmu_seq; unsigned long vma_pagesize, mmu_seq;
...@@ -684,8 +650,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu, ...@@ -684,8 +650,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
} }
/* We need minimum second+third level pages */ /* We need minimum second+third level pages */
ret = stage2_cache_topup(pcache, stage2_pgd_levels, ret = kvm_mmu_topup_memory_cache(pcache, stage2_pgd_levels);
KVM_MMU_PAGE_CACHE_NR_OBJS);
if (ret) { if (ret) {
kvm_err("Failed to topup stage2 cache\n"); kvm_err("Failed to topup stage2 cache\n");
return ret; return ret;
...@@ -734,11 +699,6 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu, ...@@ -734,11 +699,6 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
return ret; return ret;
} }
void kvm_riscv_stage2_flush_cache(struct kvm_vcpu *vcpu)
{
stage2_cache_flush(&vcpu->arch.mmu_page_cache);
}
int kvm_riscv_stage2_alloc_pgd(struct kvm *kvm) int kvm_riscv_stage2_alloc_pgd(struct kvm *kvm)
{ {
struct page *pgd_page; struct page *pgd_page;
...@@ -809,3 +769,8 @@ unsigned long kvm_riscv_stage2_mode(void) ...@@ -809,3 +769,8 @@ unsigned long kvm_riscv_stage2_mode(void)
{ {
return stage2_mode >> HGATP_MODE_SHIFT; return stage2_mode >> HGATP_MODE_SHIFT;
} }
int kvm_riscv_stage2_gpa_bits(void)
{
return stage2_gpa_bits;
}
...@@ -53,6 +53,17 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu) ...@@ -53,6 +53,17 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)
struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr; struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;
struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
struct kvm_cpu_context *reset_cntx = &vcpu->arch.guest_reset_context; struct kvm_cpu_context *reset_cntx = &vcpu->arch.guest_reset_context;
bool loaded;
/**
* The preemption should be disabled here because it races with
* kvm_sched_out/kvm_sched_in(called from preempt notifiers) which
* also calls vcpu_load/put.
*/
get_cpu();
loaded = (vcpu->cpu != -1);
if (loaded)
kvm_arch_vcpu_put(vcpu);
memcpy(csr, reset_csr, sizeof(*csr)); memcpy(csr, reset_csr, sizeof(*csr));
...@@ -64,6 +75,11 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu) ...@@ -64,6 +75,11 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)
WRITE_ONCE(vcpu->arch.irqs_pending, 0); WRITE_ONCE(vcpu->arch.irqs_pending, 0);
WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0); WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0);
/* Reset the guest CSRs for hotplug usecase */
if (loaded)
kvm_arch_vcpu_load(vcpu, smp_processor_id());
put_cpu();
} }
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
...@@ -77,6 +93,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) ...@@ -77,6 +93,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
/* Mark this VCPU never ran */ /* Mark this VCPU never ran */
vcpu->arch.ran_atleast_once = false; vcpu->arch.ran_atleast_once = false;
vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
/* Setup ISA features available to VCPU */ /* Setup ISA features available to VCPU */
vcpu->arch.isa = riscv_isa_extension_base(NULL) & KVM_RISCV_ISA_ALLOWED; vcpu->arch.isa = riscv_isa_extension_base(NULL) & KVM_RISCV_ISA_ALLOWED;
...@@ -100,6 +117,13 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) ...@@ -100,6 +117,13 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
{ {
/**
* vcpu with id 0 is the designated boot cpu.
* Keep all vcpus with non-zero id in power-off state so that
* they can be brought up using SBI HSM extension.
*/
if (vcpu->vcpu_idx != 0)
kvm_riscv_vcpu_power_off(vcpu);
} }
void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
...@@ -107,8 +131,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) ...@@ -107,8 +131,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
/* Cleanup VCPU timer */ /* Cleanup VCPU timer */
kvm_riscv_vcpu_timer_deinit(vcpu); kvm_riscv_vcpu_timer_deinit(vcpu);
/* Flush the pages pre-allocated for Stage2 page table mappings */ /* Free unused pages pre-allocated for Stage2 page table mappings */
kvm_riscv_stage2_flush_cache(vcpu); kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
} }
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
......
...@@ -26,7 +26,7 @@ void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu) ...@@ -26,7 +26,7 @@ void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu)
cntx->sstatus |= SR_FS_OFF; cntx->sstatus |= SR_FS_OFF;
} }
void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context *cntx) static void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context *cntx)
{ {
cntx->sstatus &= ~SR_FS; cntx->sstatus &= ~SR_FS;
cntx->sstatus |= SR_FS_CLEAN; cntx->sstatus |= SR_FS_CLEAN;
......
...@@ -9,15 +9,58 @@ ...@@ -9,15 +9,58 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <asm/csr.h>
#include <asm/sbi.h> #include <asm/sbi.h>
#include <asm/kvm_vcpu_timer.h> #include <asm/kvm_vcpu_sbi.h>
#define SBI_VERSION_MAJOR 0 static int kvm_linux_err_map_sbi(int err)
#define SBI_VERSION_MINOR 1 {
switch (err) {
case 0:
return SBI_SUCCESS;
case -EPERM:
return SBI_ERR_DENIED;
case -EINVAL:
return SBI_ERR_INVALID_PARAM;
case -EFAULT:
return SBI_ERR_INVALID_ADDRESS;
case -EOPNOTSUPP:
return SBI_ERR_NOT_SUPPORTED;
case -EALREADY:
return SBI_ERR_ALREADY_AVAILABLE;
default:
return SBI_ERR_FAILURE;
};
}
static void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, #ifdef CONFIG_RISCV_SBI_V01
struct kvm_run *run) extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01;
#else
static const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
.extid_start = -1UL,
.extid_end = -1UL,
.handler = NULL,
};
#endif
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental;
extern const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor;
static const struct kvm_vcpu_sbi_extension *sbi_ext[] = {
&vcpu_sbi_ext_v01,
&vcpu_sbi_ext_base,
&vcpu_sbi_ext_time,
&vcpu_sbi_ext_ipi,
&vcpu_sbi_ext_rfence,
&vcpu_sbi_ext_hsm,
&vcpu_sbi_ext_experimental,
&vcpu_sbi_ext_vendor,
};
void kvm_riscv_vcpu_sbi_forward(struct kvm_vcpu *vcpu, struct kvm_run *run)
{ {
struct kvm_cpu_context *cp = &vcpu->arch.guest_context; struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
...@@ -55,131 +98,73 @@ int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run) ...@@ -55,131 +98,73 @@ int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
return 0; return 0;
} }
#ifdef CONFIG_RISCV_SBI_V01 const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
static void kvm_sbi_system_shutdown(struct kvm_vcpu *vcpu,
struct kvm_run *run, u32 type)
{ {
unsigned long i; int i = 0;
struct kvm_vcpu *tmp;
kvm_for_each_vcpu(i, tmp, vcpu->kvm) for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
tmp->arch.power_off = true; if (sbi_ext[i]->extid_start <= extid &&
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP); sbi_ext[i]->extid_end >= extid)
return sbi_ext[i];
}
memset(&run->system_event, 0, sizeof(run->system_event)); return NULL;
run->system_event.type = type;
run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
} }
int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run) int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
{ {
ulong hmask; int ret = 1;
int i, ret = 1;
u64 next_cycle;
struct kvm_vcpu *rvcpu;
bool next_sepc = true; bool next_sepc = true;
struct cpumask cm, hm; bool userspace_exit = false;
struct kvm *kvm = vcpu->kvm;
struct kvm_cpu_trap utrap = { 0 };
struct kvm_cpu_context *cp = &vcpu->arch.guest_context; struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
const struct kvm_vcpu_sbi_extension *sbi_ext;
struct kvm_cpu_trap utrap = { 0 };
unsigned long out_val = 0;
bool ext_is_v01 = false;
if (!cp) sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
return -EINVAL; if (sbi_ext && sbi_ext->handler) {
#ifdef CONFIG_RISCV_SBI_V01
switch (cp->a7) { if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
case SBI_EXT_0_1_CONSOLE_GETCHAR: cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
case SBI_EXT_0_1_CONSOLE_PUTCHAR: ext_is_v01 = true;
/*
* The CONSOLE_GETCHAR/CONSOLE_PUTCHAR SBI calls cannot be
* handled in kernel so we forward these to user-space
*/
kvm_riscv_vcpu_sbi_forward(vcpu, run);
next_sepc = false;
ret = 0;
break;
case SBI_EXT_0_1_SET_TIMER:
#if __riscv_xlen == 32
next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0;
#else
next_cycle = (u64)cp->a0;
#endif #endif
kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle); ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
break; } else {
case SBI_EXT_0_1_CLEAR_IPI: /* Return error for unsupported SBI calls */
kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_SOFT); cp->a0 = SBI_ERR_NOT_SUPPORTED;
break; goto ecall_done;
case SBI_EXT_0_1_SEND_IPI: }
if (cp->a0)
hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, /* Handle special error cases i.e trap, exit or userspace forward */
&utrap);
else
hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1;
if (utrap.scause) { if (utrap.scause) {
/* No need to increment sepc or exit ioctl loop */
ret = 1;
utrap.sepc = cp->sepc; utrap.sepc = cp->sepc;
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
next_sepc = false; next_sepc = false;
break; goto ecall_done;
}
for_each_set_bit(i, &hmask, BITS_PER_LONG) {
rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i);
kvm_riscv_vcpu_set_interrupt(rvcpu, IRQ_VS_SOFT);
} }
break;
case SBI_EXT_0_1_SHUTDOWN: /* Exit ioctl loop or Propagate the error code the guest */
kvm_sbi_system_shutdown(vcpu, run, KVM_SYSTEM_EVENT_SHUTDOWN); if (userspace_exit) {
next_sepc = false; next_sepc = false;
ret = 0; ret = 0;
break; } else {
case SBI_EXT_0_1_REMOTE_FENCE_I: /**
case SBI_EXT_0_1_REMOTE_SFENCE_VMA: * SBI extension handler always returns an Linux error code. Convert
case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID: * it to the SBI specific error code that can be propagated the SBI
if (cp->a0) * caller.
hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0, */
&utrap); ret = kvm_linux_err_map_sbi(ret);
else cp->a0 = ret;
hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1; ret = 1;
if (utrap.scause) {
utrap.sepc = cp->sepc;
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
next_sepc = false;
break;
}
cpumask_clear(&cm);
for_each_set_bit(i, &hmask, BITS_PER_LONG) {
rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i);
if (rvcpu->cpu < 0)
continue;
cpumask_set_cpu(rvcpu->cpu, &cm);
}
riscv_cpuid_to_hartid_mask(&cm, &hm);
if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I)
sbi_remote_fence_i(cpumask_bits(&hm));
else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA)
sbi_remote_hfence_vvma(cpumask_bits(&hm),
cp->a1, cp->a2);
else
sbi_remote_hfence_vvma_asid(cpumask_bits(&hm),
cp->a1, cp->a2, cp->a3);
break;
default:
/* Return error for unsupported SBI calls */
cp->a0 = SBI_ERR_NOT_SUPPORTED;
break;
} }
ecall_done:
if (next_sepc) if (next_sepc)
cp->sepc += 4; cp->sepc += 4;
if (!ext_is_v01)
cp->a1 = out_val;
return ret; return ret;
} }
#else
int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
{
kvm_riscv_vcpu_sbi_forward(vcpu, run);
return 0;
}
#endif
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <asm/csr.h>
#include <asm/sbi.h>
#include <asm/kvm_vcpu_timer.h>
#include <asm/kvm_vcpu_sbi.h>
static int kvm_sbi_ext_base_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *trap, bool *exit)
{
int ret = 0;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
struct sbiret ecall_ret;
switch (cp->a6) {
case SBI_EXT_BASE_GET_SPEC_VERSION:
*out_val = (KVM_SBI_VERSION_MAJOR <<
SBI_SPEC_VERSION_MAJOR_SHIFT) |
KVM_SBI_VERSION_MINOR;
break;
case SBI_EXT_BASE_GET_IMP_ID:
*out_val = KVM_SBI_IMPID;
break;
case SBI_EXT_BASE_GET_IMP_VERSION:
*out_val = 0;
break;
case SBI_EXT_BASE_PROBE_EXT:
if ((cp->a0 >= SBI_EXT_EXPERIMENTAL_START &&
cp->a0 <= SBI_EXT_EXPERIMENTAL_END) ||
(cp->a0 >= SBI_EXT_VENDOR_START &&
cp->a0 <= SBI_EXT_VENDOR_END)) {
/*
* For experimental/vendor extensions
* forward it to the userspace
*/
kvm_riscv_vcpu_sbi_forward(vcpu, run);
*exit = true;
} else
*out_val = kvm_vcpu_sbi_find_ext(cp->a0) ? 1 : 0;
break;
case SBI_EXT_BASE_GET_MVENDORID:
case SBI_EXT_BASE_GET_MARCHID:
case SBI_EXT_BASE_GET_MIMPID:
ecall_ret = sbi_ecall(SBI_EXT_BASE, cp->a6, 0, 0, 0, 0, 0, 0);
if (!ecall_ret.error)
*out_val = ecall_ret.value;
/*TODO: We are unnecessarily converting the error twice */
ret = sbi_err_map_linux_errno(ecall_ret.error);
break;
default:
ret = -EOPNOTSUPP;
break;
}
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_base = {
.extid_start = SBI_EXT_BASE,
.extid_end = SBI_EXT_BASE,
.handler = kvm_sbi_ext_base_handler,
};
static int kvm_sbi_ext_forward_handler(struct kvm_vcpu *vcpu,
struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap,
bool *exit)
{
/*
* Both SBI experimental and vendor extensions are
* unconditionally forwarded to userspace.
*/
kvm_riscv_vcpu_sbi_forward(vcpu, run);
*exit = true;
return 0;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_experimental = {
.extid_start = SBI_EXT_EXPERIMENTAL_START,
.extid_end = SBI_EXT_EXPERIMENTAL_END,
.handler = kvm_sbi_ext_forward_handler,
};
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_vendor = {
.extid_start = SBI_EXT_VENDOR_START,
.extid_end = SBI_EXT_VENDOR_END,
.handler = kvm_sbi_ext_forward_handler,
};
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <asm/csr.h>
#include <asm/sbi.h>
#include <asm/kvm_vcpu_sbi.h>
static int kvm_sbi_hsm_vcpu_start(struct kvm_vcpu *vcpu)
{
struct kvm_cpu_context *reset_cntx;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
struct kvm_vcpu *target_vcpu;
unsigned long target_vcpuid = cp->a0;
target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid);
if (!target_vcpu)
return -EINVAL;
if (!target_vcpu->arch.power_off)
return -EALREADY;
reset_cntx = &target_vcpu->arch.guest_reset_context;
/* start address */
reset_cntx->sepc = cp->a1;
/* target vcpu id to start */
reset_cntx->a0 = target_vcpuid;
/* private data passed from kernel */
reset_cntx->a1 = cp->a2;
kvm_make_request(KVM_REQ_VCPU_RESET, target_vcpu);
kvm_riscv_vcpu_power_on(target_vcpu);
return 0;
}
static int kvm_sbi_hsm_vcpu_stop(struct kvm_vcpu *vcpu)
{
if (vcpu->arch.power_off)
return -EINVAL;
kvm_riscv_vcpu_power_off(vcpu);
return 0;
}
static int kvm_sbi_hsm_vcpu_get_status(struct kvm_vcpu *vcpu)
{
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
unsigned long target_vcpuid = cp->a0;
struct kvm_vcpu *target_vcpu;
target_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, target_vcpuid);
if (!target_vcpu)
return -EINVAL;
if (!target_vcpu->arch.power_off)
return SBI_HSM_HART_STATUS_STARTED;
else
return SBI_HSM_HART_STATUS_STOPPED;
}
static int kvm_sbi_ext_hsm_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap,
bool *exit)
{
int ret = 0;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
struct kvm *kvm = vcpu->kvm;
unsigned long funcid = cp->a6;
switch (funcid) {
case SBI_EXT_HSM_HART_START:
mutex_lock(&kvm->lock);
ret = kvm_sbi_hsm_vcpu_start(vcpu);
mutex_unlock(&kvm->lock);
break;
case SBI_EXT_HSM_HART_STOP:
ret = kvm_sbi_hsm_vcpu_stop(vcpu);
break;
case SBI_EXT_HSM_HART_STATUS:
ret = kvm_sbi_hsm_vcpu_get_status(vcpu);
if (ret >= 0) {
*out_val = ret;
ret = 0;
}
break;
default:
ret = -EOPNOTSUPP;
}
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_hsm = {
.extid_start = SBI_EXT_HSM,
.extid_end = SBI_EXT_HSM,
.handler = kvm_sbi_ext_hsm_handler,
};
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <asm/csr.h>
#include <asm/sbi.h>
#include <asm/kvm_vcpu_timer.h>
#include <asm/kvm_vcpu_sbi.h>
static int kvm_sbi_ext_time_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap, bool *exit)
{
int ret = 0;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
u64 next_cycle;
if (cp->a6 != SBI_EXT_TIME_SET_TIMER)
return -EINVAL;
#if __riscv_xlen == 32
next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0;
#else
next_cycle = (u64)cp->a0;
#endif
kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle);
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_time = {
.extid_start = SBI_EXT_TIME,
.extid_end = SBI_EXT_TIME,
.handler = kvm_sbi_ext_time_handler,
};
static int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap, bool *exit)
{
int ret = 0;
unsigned long i;
struct kvm_vcpu *tmp;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
unsigned long hmask = cp->a0;
unsigned long hbase = cp->a1;
if (cp->a6 != SBI_EXT_IPI_SEND_IPI)
return -EINVAL;
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
if (hbase != -1UL) {
if (tmp->vcpu_id < hbase)
continue;
if (!(hmask & (1UL << (tmp->vcpu_id - hbase))))
continue;
}
ret = kvm_riscv_vcpu_set_interrupt(tmp, IRQ_VS_SOFT);
if (ret < 0)
break;
}
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_ipi = {
.extid_start = SBI_EXT_IPI,
.extid_end = SBI_EXT_IPI,
.handler = kvm_sbi_ext_ipi_handler,
};
static int kvm_sbi_ext_rfence_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap, bool *exit)
{
int ret = 0;
unsigned long i;
struct cpumask cm, hm;
struct kvm_vcpu *tmp;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
unsigned long hmask = cp->a0;
unsigned long hbase = cp->a1;
unsigned long funcid = cp->a6;
cpumask_clear(&cm);
cpumask_clear(&hm);
kvm_for_each_vcpu(i, tmp, vcpu->kvm) {
if (hbase != -1UL) {
if (tmp->vcpu_id < hbase)
continue;
if (!(hmask & (1UL << (tmp->vcpu_id - hbase))))
continue;
}
if (tmp->cpu < 0)
continue;
cpumask_set_cpu(tmp->cpu, &cm);
}
riscv_cpuid_to_hartid_mask(&cm, &hm);
switch (funcid) {
case SBI_EXT_RFENCE_REMOTE_FENCE_I:
ret = sbi_remote_fence_i(cpumask_bits(&hm));
break;
case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA:
ret = sbi_remote_hfence_vvma(cpumask_bits(&hm), cp->a2, cp->a3);
break;
case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID:
ret = sbi_remote_hfence_vvma_asid(cpumask_bits(&hm), cp->a2,
cp->a3, cp->a4);
break;
case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA:
case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID:
case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA:
case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID:
/* TODO: implement for nested hypervisor case */
default:
ret = -EOPNOTSUPP;
}
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_rfence = {
.extid_start = SBI_EXT_RFENCE,
.extid_end = SBI_EXT_RFENCE,
.handler = kvm_sbi_ext_rfence_handler,
};
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
*
* Authors:
* Atish Patra <atish.patra@wdc.com>
*/
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/kvm_host.h>
#include <asm/csr.h>
#include <asm/sbi.h>
#include <asm/kvm_vcpu_timer.h>
#include <asm/kvm_vcpu_sbi.h>
static void kvm_sbi_system_shutdown(struct kvm_vcpu *vcpu,
struct kvm_run *run, u32 type)
{
unsigned long i;
struct kvm_vcpu *tmp;
kvm_for_each_vcpu(i, tmp, vcpu->kvm)
tmp->arch.power_off = true;
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
memset(&run->system_event, 0, sizeof(run->system_event));
run->system_event.type = type;
run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
}
static int kvm_sbi_ext_v01_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
unsigned long *out_val,
struct kvm_cpu_trap *utrap,
bool *exit)
{
ulong hmask;
int i, ret = 0;
u64 next_cycle;
struct kvm_vcpu *rvcpu;
struct cpumask cm, hm;
struct kvm *kvm = vcpu->kvm;
struct kvm_cpu_context *cp = &vcpu->arch.guest_context;
switch (cp->a7) {
case SBI_EXT_0_1_CONSOLE_GETCHAR:
case SBI_EXT_0_1_CONSOLE_PUTCHAR:
/*
* The CONSOLE_GETCHAR/CONSOLE_PUTCHAR SBI calls cannot be
* handled in kernel so we forward these to user-space
*/
kvm_riscv_vcpu_sbi_forward(vcpu, run);
*exit = true;
break;
case SBI_EXT_0_1_SET_TIMER:
#if __riscv_xlen == 32
next_cycle = ((u64)cp->a1 << 32) | (u64)cp->a0;
#else
next_cycle = (u64)cp->a0;
#endif
ret = kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle);
break;
case SBI_EXT_0_1_CLEAR_IPI:
ret = kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_SOFT);
break;
case SBI_EXT_0_1_SEND_IPI:
if (cp->a0)
hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0,
utrap);
else
hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1;
if (utrap->scause)
break;
for_each_set_bit(i, &hmask, BITS_PER_LONG) {
rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i);
ret = kvm_riscv_vcpu_set_interrupt(rvcpu, IRQ_VS_SOFT);
if (ret < 0)
break;
}
break;
case SBI_EXT_0_1_SHUTDOWN:
kvm_sbi_system_shutdown(vcpu, run, KVM_SYSTEM_EVENT_SHUTDOWN);
*exit = true;
break;
case SBI_EXT_0_1_REMOTE_FENCE_I:
case SBI_EXT_0_1_REMOTE_SFENCE_VMA:
case SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID:
if (cp->a0)
hmask = kvm_riscv_vcpu_unpriv_read(vcpu, false, cp->a0,
utrap);
else
hmask = (1UL << atomic_read(&kvm->online_vcpus)) - 1;
if (utrap->scause)
break;
cpumask_clear(&cm);
for_each_set_bit(i, &hmask, BITS_PER_LONG) {
rvcpu = kvm_get_vcpu_by_id(vcpu->kvm, i);
if (rvcpu->cpu < 0)
continue;
cpumask_set_cpu(rvcpu->cpu, &cm);
}
riscv_cpuid_to_hartid_mask(&cm, &hm);
if (cp->a7 == SBI_EXT_0_1_REMOTE_FENCE_I)
ret = sbi_remote_fence_i(cpumask_bits(&hm));
else if (cp->a7 == SBI_EXT_0_1_REMOTE_SFENCE_VMA)
ret = sbi_remote_hfence_vvma(cpumask_bits(&hm),
cp->a1, cp->a2);
else
ret = sbi_remote_hfence_vvma_asid(cpumask_bits(&hm),
cp->a1, cp->a2, cp->a3);
break;
default:
ret = -EINVAL;
break;
};
return ret;
}
const struct kvm_vcpu_sbi_extension vcpu_sbi_ext_v01 = {
.extid_start = SBI_EXT_0_1_SET_TIMER,
.extid_end = SBI_EXT_0_1_SHUTDOWN,
.handler = kvm_sbi_ext_v01_handler,
};
...@@ -74,6 +74,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) ...@@ -74,6 +74,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_NR_MEMSLOTS: case KVM_CAP_NR_MEMSLOTS:
r = KVM_USER_MEM_SLOTS; r = KVM_USER_MEM_SLOTS;
break; break;
case KVM_CAP_VM_GPA_BITS:
r = kvm_riscv_stage2_gpa_bits();
break;
default: default:
r = 0; r = 0;
break; break;
......
...@@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt { ...@@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204 #define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
#define KVM_CAP_ARM_MTE 205 #define KVM_CAP_ARM_MTE 205
#define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206 #define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
#define KVM_CAP_VM_GPA_BITS 207
#ifdef KVM_CAP_IRQ_ROUTING #ifdef KVM_CAP_IRQ_ROUTING
......
...@@ -32,11 +32,16 @@ endif ...@@ -32,11 +32,16 @@ endif
ifeq ($(ARCH),s390) ifeq ($(ARCH),s390)
UNAME_M := s390x UNAME_M := s390x
endif endif
# Set UNAME_M riscv compile/install to work
ifeq ($(ARCH),riscv)
UNAME_M := riscv
endif
LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c
LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S LIBKVM_x86_64 = lib/x86_64/apic.c lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S
LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c lib/aarch64/vgic.c LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c lib/aarch64/vgic.c
LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
LIBKVM_riscv = lib/riscv/processor.c lib/riscv/ucall.c
TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test
TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features
...@@ -120,6 +125,13 @@ TEST_GEN_PROGS_s390x += rseq_test ...@@ -120,6 +125,13 @@ TEST_GEN_PROGS_s390x += rseq_test
TEST_GEN_PROGS_s390x += set_memory_region_test TEST_GEN_PROGS_s390x += set_memory_region_test
TEST_GEN_PROGS_s390x += kvm_binary_stats_test TEST_GEN_PROGS_s390x += kvm_binary_stats_test
TEST_GEN_PROGS_riscv += demand_paging_test
TEST_GEN_PROGS_riscv += dirty_log_test
TEST_GEN_PROGS_riscv += kvm_create_max_vcpus
TEST_GEN_PROGS_riscv += kvm_page_table_test
TEST_GEN_PROGS_riscv += set_memory_region_test
TEST_GEN_PROGS_riscv += kvm_binary_stats_test
TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M))
LIBKVM += $(LIBKVM_$(UNAME_M)) LIBKVM += $(LIBKVM_$(UNAME_M))
...@@ -134,7 +146,7 @@ endif ...@@ -134,7 +146,7 @@ endif
CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
-fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \ -fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
-I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \ -I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
-I$(<D) -Iinclude/$(UNAME_M) -I.. -I$(<D) -Iinclude/$(UNAME_M) -I.. $(EXTRA_CFLAGS)
no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
$(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie) $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie)
......
...@@ -77,6 +77,16 @@ extern enum vm_guest_mode vm_mode_default; ...@@ -77,6 +77,16 @@ extern enum vm_guest_mode vm_mode_default;
#define MIN_PAGE_SHIFT 12U #define MIN_PAGE_SHIFT 12U
#define ptes_per_page(page_size) ((page_size) / 16) #define ptes_per_page(page_size) ((page_size) / 16)
#elif defined(__riscv)
#if __riscv_xlen == 32
#error "RISC-V 32-bit kvm selftests not supported"
#endif
#define VM_MODE_DEFAULT VM_MODE_P40V48_4K
#define MIN_PAGE_SHIFT 12U
#define ptes_per_page(page_size) ((page_size) / 8)
#endif #endif
#define MIN_PAGE_SIZE (1U << MIN_PAGE_SHIFT) #define MIN_PAGE_SIZE (1U << MIN_PAGE_SHIFT)
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* RISC-V processor specific defines
*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*/
#ifndef SELFTEST_KVM_PROCESSOR_H
#define SELFTEST_KVM_PROCESSOR_H
#include "kvm_util.h"
#include <linux/stringify.h>
static inline uint64_t __kvm_reg_id(uint64_t type, uint64_t idx,
uint64_t size)
{
return KVM_REG_RISCV | type | idx | size;
}
#if __riscv_xlen == 64
#define KVM_REG_SIZE_ULONG KVM_REG_SIZE_U64
#else
#define KVM_REG_SIZE_ULONG KVM_REG_SIZE_U32
#endif
#define RISCV_CONFIG_REG(name) __kvm_reg_id(KVM_REG_RISCV_CONFIG, \
KVM_REG_RISCV_CONFIG_REG(name), \
KVM_REG_SIZE_ULONG)
#define RISCV_CORE_REG(name) __kvm_reg_id(KVM_REG_RISCV_CORE, \
KVM_REG_RISCV_CORE_REG(name), \
KVM_REG_SIZE_ULONG)
#define RISCV_CSR_REG(name) __kvm_reg_id(KVM_REG_RISCV_CSR, \
KVM_REG_RISCV_CSR_REG(name), \
KVM_REG_SIZE_ULONG)
#define RISCV_TIMER_REG(name) __kvm_reg_id(KVM_REG_RISCV_TIMER, \
KVM_REG_RISCV_TIMER_REG(name), \
KVM_REG_SIZE_U64)
static inline void get_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id,
unsigned long *addr)
{
struct kvm_one_reg reg;
reg.id = id;
reg.addr = (unsigned long)addr;
vcpu_get_reg(vm, vcpuid, &reg);
}
static inline void set_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id,
unsigned long val)
{
struct kvm_one_reg reg;
reg.id = id;
reg.addr = (unsigned long)&val;
vcpu_set_reg(vm, vcpuid, &reg);
}
/* L3 index Bit[47:39] */
#define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL
#define PGTBL_L3_INDEX_SHIFT 39
#define PGTBL_L3_BLOCK_SHIFT 39
#define PGTBL_L3_BLOCK_SIZE 0x0000008000000000ULL
#define PGTBL_L3_MAP_MASK (~(PGTBL_L3_BLOCK_SIZE - 1))
/* L2 index Bit[38:30] */
#define PGTBL_L2_INDEX_MASK 0x0000007FC0000000ULL
#define PGTBL_L2_INDEX_SHIFT 30
#define PGTBL_L2_BLOCK_SHIFT 30
#define PGTBL_L2_BLOCK_SIZE 0x0000000040000000ULL
#define PGTBL_L2_MAP_MASK (~(PGTBL_L2_BLOCK_SIZE - 1))
/* L1 index Bit[29:21] */
#define PGTBL_L1_INDEX_MASK 0x000000003FE00000ULL
#define PGTBL_L1_INDEX_SHIFT 21
#define PGTBL_L1_BLOCK_SHIFT 21
#define PGTBL_L1_BLOCK_SIZE 0x0000000000200000ULL
#define PGTBL_L1_MAP_MASK (~(PGTBL_L1_BLOCK_SIZE - 1))
/* L0 index Bit[20:12] */
#define PGTBL_L0_INDEX_MASK 0x00000000001FF000ULL
#define PGTBL_L0_INDEX_SHIFT 12
#define PGTBL_L0_BLOCK_SHIFT 12
#define PGTBL_L0_BLOCK_SIZE 0x0000000000001000ULL
#define PGTBL_L0_MAP_MASK (~(PGTBL_L0_BLOCK_SIZE - 1))
#define PGTBL_PTE_ADDR_MASK 0x003FFFFFFFFFFC00ULL
#define PGTBL_PTE_ADDR_SHIFT 10
#define PGTBL_PTE_RSW_MASK 0x0000000000000300ULL
#define PGTBL_PTE_RSW_SHIFT 8
#define PGTBL_PTE_DIRTY_MASK 0x0000000000000080ULL
#define PGTBL_PTE_DIRTY_SHIFT 7
#define PGTBL_PTE_ACCESSED_MASK 0x0000000000000040ULL
#define PGTBL_PTE_ACCESSED_SHIFT 6
#define PGTBL_PTE_GLOBAL_MASK 0x0000000000000020ULL
#define PGTBL_PTE_GLOBAL_SHIFT 5
#define PGTBL_PTE_USER_MASK 0x0000000000000010ULL
#define PGTBL_PTE_USER_SHIFT 4
#define PGTBL_PTE_EXECUTE_MASK 0x0000000000000008ULL
#define PGTBL_PTE_EXECUTE_SHIFT 3
#define PGTBL_PTE_WRITE_MASK 0x0000000000000004ULL
#define PGTBL_PTE_WRITE_SHIFT 2
#define PGTBL_PTE_READ_MASK 0x0000000000000002ULL
#define PGTBL_PTE_READ_SHIFT 1
#define PGTBL_PTE_PERM_MASK (PGTBL_PTE_EXECUTE_MASK | \
PGTBL_PTE_WRITE_MASK | \
PGTBL_PTE_READ_MASK)
#define PGTBL_PTE_VALID_MASK 0x0000000000000001ULL
#define PGTBL_PTE_VALID_SHIFT 0
#define PGTBL_PAGE_SIZE PGTBL_L0_BLOCK_SIZE
#define PGTBL_PAGE_SIZE_SHIFT PGTBL_L0_BLOCK_SHIFT
#define SATP_PPN _AC(0x00000FFFFFFFFFFF, UL)
#define SATP_MODE_39 _AC(0x8000000000000000, UL)
#define SATP_MODE_48 _AC(0x9000000000000000, UL)
#define SATP_ASID_BITS 16
#define SATP_ASID_SHIFT 44
#define SATP_ASID_MASK _AC(0xFFFF, UL)
#define SBI_EXT_EXPERIMENTAL_START 0x08000000
#define SBI_EXT_EXPERIMENTAL_END 0x08FFFFFF
#define KVM_RISCV_SELFTESTS_SBI_EXT SBI_EXT_EXPERIMENTAL_END
struct sbiret {
long error;
long value;
};
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5);
#endif /* SELFTEST_KVM_PROCESSOR_H */
...@@ -75,6 +75,16 @@ void guest_modes_append_default(void) ...@@ -75,6 +75,16 @@ void guest_modes_append_default(void)
guest_mode_append(VM_MODE_P47V64_4K, true, true); guest_mode_append(VM_MODE_P47V64_4K, true, true);
} }
#endif #endif
#ifdef __riscv
{
unsigned int sz = kvm_check_cap(KVM_CAP_VM_GPA_BITS);
if (sz >= 52)
guest_mode_append(VM_MODE_P52V48_4K, true, true);
if (sz >= 48)
guest_mode_append(VM_MODE_P48V48_4K, true, true);
}
#endif
} }
void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg) void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* ucall support. A ucall is a "hypercall to userspace".
*
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
*/
#include <linux/kvm.h>
#include "kvm_util.h"
#include "../kvm_util_internal.h"
#include "processor.h"
void ucall_init(struct kvm_vm *vm, void *arg)
{
}
void ucall_uninit(struct kvm_vm *vm)
{
}
struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
unsigned long arg1, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5)
{
register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
struct sbiret ret;
asm volatile (
"ecall"
: "+r" (a0), "+r" (a1)
: "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
: "memory");
ret.error = a0;
ret.value = a1;
return ret;
}
void ucall(uint64_t cmd, int nargs, ...)
{
struct ucall uc = {
.cmd = cmd,
};
va_list va;
int i;
nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS;
va_start(va, nargs);
for (i = 0; i < nargs; ++i)
uc.args[i] = va_arg(va, uint64_t);
va_end(va);
sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT, 0, (vm_vaddr_t)&uc,
0, 0, 0, 0, 0);
}
uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc)
{
struct kvm_run *run = vcpu_state(vm, vcpu_id);
struct ucall ucall = {};
if (uc)
memset(uc, 0, sizeof(*uc));
if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT &&
run->riscv_sbi.function_id == 0) {
memcpy(&ucall, addr_gva2hva(vm, run->riscv_sbi.args[0]),
sizeof(ucall));
vcpu_run_complete_io(vm, vcpu_id);
if (uc)
memcpy(uc, &ucall, sizeof(ucall));
}
return ucall.cmd;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment