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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEZdn75s5e6LHDQ+f/rUjsVaLHLAcFAmHW6GcACgkQrUjsVaLH LAcA+Q//bRKyuC2JGn0qN0e4WOcb8zpDUw5zep8WqlWviFiNjxxVjHeroT//cDtr 7apwTCJogDlgnkcH0e88CzD3M0Gh/NJ7JAZ/Z1gBMhBMz7afcahnADqXcTuottMf x0stMIQxKlQhess2IQa502KGb23uitbLfiY2MzaPVnXbxfBbM08YUPAcIhSSl+iP ZXtvweqhrafUoUvaEFXSHkA27QMWEH+vZq4JlRwLSy7y3U3Hd/51nH04Fxp/n4Qh 5XyWO0mPqmiTb6Dz5I/hx7sZLZ5ErMpFI5II22sZYcOqtrrL59f5I9gvYQOYc7im GjyBshD8bB4SVEciMGEJq9QucOw41M6cTFmdiaQR+NCHfMa/A5RPwf0zT+15Xrtg zEkNQCRdWgDhxb/cYqKaAQXERfeposr0xS398qoSUT29GXFvv0N+P2N/WAgQqg+D 2cnhGRMlsdUJEVWUXCJjZ1u/Wwx6gkxJbjvRY48vvvB76eZzr82sOXYEIF8MBrPG co5wa/mzUl3CfgzHO4fESvR+hNTbXiPLbW/FPzSdNNMWxB5GOREP42vcDn9be1Xf IXBcKlpL2MhExPh+J6DjM3BqMdV+8qywcn0iR0f3W7CuAiJS0sF7Pyn8Ur7GpU6U YFwwLWYBQmdPmkXZuH+8fAju2GMjAHxJBCOfpKahhDcWk7TECdE= =Ao1E -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
1b0c9d00aa
2
.mailmap
2
.mailmap
|
@ -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@free-electrons.com>
|
||||
Antonio Ospite <ao2@ao2.it> <ao2@amarulasolutions.com>
|
||||
Anup Patel <anup@brainfault.org> <anup.patel@wdc.com>
|
||||
Archit Taneja <archit@ti.com>
|
||||
Ard Biesheuvel <ardb@kernel.org> <ard.biesheuvel@linaro.org>
|
||||
Arnaud Patard <arnaud.patard@rtp-net.org>
|
||||
Arnd Bergmann <arnd@arndb.de>
|
||||
Atish Patra <atishp@atishpatra.org> <atish.patra@wdc.com>
|
||||
Axel Dyks <xl@xlsigned.net>
|
||||
Axel Lin <axel.lin@gmail.com>
|
||||
Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com>
|
||||
|
|
|
@ -10444,8 +10444,8 @@ F: arch/powerpc/kernel/kvm*
|
|||
F: arch/powerpc/kvm/
|
||||
|
||||
KERNEL VIRTUAL MACHINE FOR RISC-V (KVM/riscv)
|
||||
M: Anup Patel <anup.patel@wdc.com>
|
||||
R: Atish Patra <atish.patra@wdc.com>
|
||||
M: Anup Patel <anup@brainfault.org>
|
||||
R: Atish Patra <atishp@atishpatra.org>
|
||||
L: kvm@vger.kernel.org
|
||||
L: kvm-riscv@lists.infradead.org
|
||||
L: linux-riscv@lists.infradead.org
|
||||
|
|
|
@ -77,13 +77,6 @@ struct kvm_sbi_context {
|
|||
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 {
|
||||
unsigned long sepc;
|
||||
unsigned long scause;
|
||||
|
@ -193,7 +186,7 @@ struct kvm_vcpu_arch {
|
|||
struct kvm_sbi_context sbi_context;
|
||||
|
||||
/* 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 */
|
||||
bool power_off;
|
||||
|
@ -220,12 +213,12 @@ void __kvm_riscv_hfence_gvma_all(void);
|
|||
int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
|
||||
struct kvm_memory_slot *memslot,
|
||||
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);
|
||||
void kvm_riscv_stage2_free_pgd(struct kvm *kvm);
|
||||
void kvm_riscv_stage2_update_hgatp(struct kvm_vcpu *vcpu);
|
||||
void kvm_riscv_stage2_mode_detect(void);
|
||||
unsigned long kvm_riscv_stage2_mode(void);
|
||||
int kvm_riscv_stage2_gpa_bits(void);
|
||||
|
||||
void kvm_riscv_stage2_vmid_detect(void);
|
||||
unsigned long kvm_riscv_stage2_vmid_bits(void);
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
#ifndef _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 */
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* 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 {
|
|||
SBI_EXT_IPI = 0x735049,
|
||||
SBI_EXT_RFENCE = 0x52464E43,
|
||||
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 {
|
||||
|
@ -82,6 +90,7 @@ enum sbi_hsm_hart_status {
|
|||
#define SBI_ERR_INVALID_PARAM -3
|
||||
#define SBI_ERR_DENIED -4
|
||||
#define SBI_ERR_INVALID_ADDRESS -5
|
||||
#define SBI_ERR_ALREADY_AVAILABLE -6
|
||||
|
||||
extern unsigned long sbi_spec_version;
|
||||
struct sbiret {
|
||||
|
|
|
@ -19,4 +19,8 @@ kvm-y += vcpu_exit.o
|
|||
kvm-y += vcpu_fp.o
|
||||
kvm-y += vcpu_switch.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
|
||||
|
|
|
@ -58,6 +58,14 @@ int kvm_arch_hardware_enable(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_HIDELEG, 0);
|
||||
}
|
||||
|
|
|
@ -83,43 +83,6 @@ static int stage2_level_to_page_size(u32 level, unsigned long *out_pgsize)
|
|||
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,
|
||||
pte_t **ptepp, u32 *ptep_level)
|
||||
{
|
||||
|
@ -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,
|
||||
struct kvm_mmu_page_cache *pcache,
|
||||
struct kvm_mmu_memory_cache *pcache,
|
||||
gpa_t addr, const pte_t *new_pte)
|
||||
{
|
||||
u32 current_level = stage2_pgd_levels - 1;
|
||||
|
@ -186,7 +149,9 @@ static int stage2_set_pte(struct kvm *kvm, u32 level,
|
|||
return -EEXIST;
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
*ptep = pfn_pte(PFN_DOWN(__pa(next_ptep)),
|
||||
|
@ -209,7 +174,7 @@ static int stage2_set_pte(struct kvm *kvm, u32 level,
|
|||
}
|
||||
|
||||
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,
|
||||
unsigned long page_size,
|
||||
bool page_rdonly, bool page_exec)
|
||||
|
@ -384,7 +349,10 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
|
|||
int ret = 0;
|
||||
unsigned long pfn;
|
||||
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;
|
||||
pfn = __phys_to_pfn(hpa);
|
||||
|
@ -395,9 +363,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
|
|||
if (!writable)
|
||||
pte = pte_wrprotect(pte);
|
||||
|
||||
ret = stage2_cache_topup(&pcache,
|
||||
stage2_pgd_levels,
|
||||
KVM_MMU_PAGE_CACHE_NR_OBJS);
|
||||
ret = kvm_mmu_topup_memory_cache(&pcache, stage2_pgd_levels);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -411,7 +377,7 @@ static int stage2_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa,
|
|||
}
|
||||
|
||||
out:
|
||||
stage2_cache_flush(&pcache);
|
||||
kvm_mmu_free_memory_cache(&pcache);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -649,7 +615,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
|
|||
gfn_t gfn = gpa >> PAGE_SHIFT;
|
||||
struct vm_area_struct *vma;
|
||||
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 &&
|
||||
!(memslot->flags & KVM_MEM_READONLY)) ? true : false;
|
||||
unsigned long vma_pagesize, mmu_seq;
|
||||
|
@ -684,8 +650,7 @@ int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
|
|||
}
|
||||
|
||||
/* We need minimum second+third level pages */
|
||||
ret = stage2_cache_topup(pcache, stage2_pgd_levels,
|
||||
KVM_MMU_PAGE_CACHE_NR_OBJS);
|
||||
ret = kvm_mmu_topup_memory_cache(pcache, stage2_pgd_levels);
|
||||
if (ret) {
|
||||
kvm_err("Failed to topup stage2 cache\n");
|
||||
return ret;
|
||||
|
@ -734,11 +699,6 @@ out_unlock:
|
|||
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)
|
||||
{
|
||||
struct page *pgd_page;
|
||||
|
@ -809,3 +769,8 @@ unsigned long kvm_riscv_stage2_mode(void)
|
|||
{
|
||||
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)
|
|||
struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr;
|
||||
struct kvm_cpu_context *cntx = &vcpu->arch.guest_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));
|
||||
|
||||
|
@ -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_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)
|
||||
|
@ -77,6 +93,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
|
||||
/* Mark this VCPU never ran */
|
||||
vcpu->arch.ran_atleast_once = false;
|
||||
vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO;
|
||||
|
||||
/* Setup ISA features available to VCPU */
|
||||
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)
|
|||
|
||||
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)
|
||||
|
@ -107,8 +131,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
|||
/* Cleanup VCPU timer */
|
||||
kvm_riscv_vcpu_timer_deinit(vcpu);
|
||||
|
||||
/* Flush the pages pre-allocated for Stage2 page table mappings */
|
||||
kvm_riscv_stage2_flush_cache(vcpu);
|
||||
/* Free unused pages pre-allocated for Stage2 page table mappings */
|
||||
kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
|
||||
}
|
||||
|
||||
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
|
||||
|
|
|
@ -26,7 +26,7 @@ void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu)
|
|||
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_CLEAN;
|
||||
|
|
|
@ -9,15 +9,58 @@
|
|||
#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>
|
||||
|
||||
#define SBI_VERSION_MAJOR 0
|
||||
#define SBI_VERSION_MINOR 1
|
||||
static int kvm_linux_err_map_sbi(int err)
|
||||
{
|
||||
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,
|
||||
struct kvm_run *run)
|
||||
#ifdef CONFIG_RISCV_SBI_V01
|
||||
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;
|
||||
|
||||
|
@ -55,131 +98,73 @@ int kvm_riscv_vcpu_sbi_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RISCV_SBI_V01
|
||||
|
||||
static void kvm_sbi_system_shutdown(struct kvm_vcpu *vcpu,
|
||||
struct kvm_run *run, u32 type)
|
||||
const struct kvm_vcpu_sbi_extension *kvm_vcpu_sbi_find_ext(unsigned long extid)
|
||||
{
|
||||
unsigned long i;
|
||||
struct kvm_vcpu *tmp;
|
||||
int i = 0;
|
||||
|
||||
kvm_for_each_vcpu(i, tmp, vcpu->kvm)
|
||||
tmp->arch.power_off = true;
|
||||
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_SLEEP);
|
||||
for (i = 0; i < ARRAY_SIZE(sbi_ext); i++) {
|
||||
if (sbi_ext[i]->extid_start <= extid &&
|
||||
sbi_ext[i]->extid_end >= extid)
|
||||
return sbi_ext[i];
|
||||
}
|
||||
|
||||
memset(&run->system_event, 0, sizeof(run->system_event));
|
||||
run->system_event.type = type;
|
||||
run->exit_reason = KVM_EXIT_SYSTEM_EVENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int kvm_riscv_vcpu_sbi_ecall(struct kvm_vcpu *vcpu, struct kvm_run *run)
|
||||
{
|
||||
ulong hmask;
|
||||
int i, ret = 1;
|
||||
u64 next_cycle;
|
||||
struct kvm_vcpu *rvcpu;
|
||||
int ret = 1;
|
||||
bool next_sepc = true;
|
||||
struct cpumask cm, hm;
|
||||
struct kvm *kvm = vcpu->kvm;
|
||||
struct kvm_cpu_trap utrap = { 0 };
|
||||
bool userspace_exit = false;
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
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;
|
||||
sbi_ext = kvm_vcpu_sbi_find_ext(cp->a7);
|
||||
if (sbi_ext && sbi_ext->handler) {
|
||||
#ifdef CONFIG_RISCV_SBI_V01
|
||||
if (cp->a7 >= SBI_EXT_0_1_SET_TIMER &&
|
||||
cp->a7 <= SBI_EXT_0_1_SHUTDOWN)
|
||||
ext_is_v01 = true;
|
||||
#endif
|
||||
kvm_riscv_vcpu_timer_next_event(vcpu, next_cycle);
|
||||
break;
|
||||
case SBI_EXT_0_1_CLEAR_IPI:
|
||||
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) {
|
||||
utrap.sepc = cp->sepc;
|
||||
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
|
||||
next_sepc = false;
|
||||
break;
|
||||
}
|
||||
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:
|
||||
kvm_sbi_system_shutdown(vcpu, run, KVM_SYSTEM_EVENT_SHUTDOWN);
|
||||
next_sepc = false;
|
||||
ret = 0;
|
||||
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) {
|
||||
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:
|
||||
ret = sbi_ext->handler(vcpu, run, &out_val, &utrap, &userspace_exit);
|
||||
} else {
|
||||
/* Return error for unsupported SBI calls */
|
||||
cp->a0 = SBI_ERR_NOT_SUPPORTED;
|
||||
break;
|
||||
goto ecall_done;
|
||||
}
|
||||
|
||||
/* Handle special error cases i.e trap, exit or userspace forward */
|
||||
if (utrap.scause) {
|
||||
/* No need to increment sepc or exit ioctl loop */
|
||||
ret = 1;
|
||||
utrap.sepc = cp->sepc;
|
||||
kvm_riscv_vcpu_trap_redirect(vcpu, &utrap);
|
||||
next_sepc = false;
|
||||
goto ecall_done;
|
||||
}
|
||||
|
||||
/* Exit ioctl loop or Propagate the error code the guest */
|
||||
if (userspace_exit) {
|
||||
next_sepc = false;
|
||||
ret = 0;
|
||||
} else {
|
||||
/**
|
||||
* SBI extension handler always returns an Linux error code. Convert
|
||||
* it to the SBI specific error code that can be propagated the SBI
|
||||
* caller.
|
||||
*/
|
||||
ret = kvm_linux_err_map_sbi(ret);
|
||||
cp->a0 = ret;
|
||||
ret = 1;
|
||||
}
|
||||
ecall_done:
|
||||
if (next_sepc)
|
||||
cp->sepc += 4;
|
||||
if (!ext_is_v01)
|
||||
cp->a1 = out_val;
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// 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,
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
// 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,
|
||||
};
|
|
@ -0,0 +1,135 @@
|
|||
// 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,
|
||||
};
|
|
@ -0,0 +1,126 @@
|
|||
// 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)
|
|||
case KVM_CAP_NR_MEMSLOTS:
|
||||
r = KVM_USER_MEM_SLOTS;
|
||||
break;
|
||||
case KVM_CAP_VM_GPA_BITS:
|
||||
r = kvm_riscv_stage2_gpa_bits();
|
||||
break;
|
||||
default:
|
||||
r = 0;
|
||||
break;
|
||||
|
|
|
@ -1131,6 +1131,7 @@ struct kvm_ppc_resize_hpt {
|
|||
#define KVM_CAP_EXIT_ON_EMULATION_FAILURE 204
|
||||
#define KVM_CAP_ARM_MTE 205
|
||||
#define KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM 206
|
||||
#define KVM_CAP_VM_GPA_BITS 207
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
|
|
|
@ -32,11 +32,16 @@ endif
|
|||
ifeq ($(ARCH),s390)
|
||||
UNAME_M := s390x
|
||||
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_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_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/get_msr_index_features
|
||||
|
@ -120,6 +125,13 @@ TEST_GEN_PROGS_s390x += rseq_test
|
|||
TEST_GEN_PROGS_s390x += set_memory_region_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))
|
||||
LIBKVM += $(LIBKVM_$(UNAME_M))
|
||||
|
||||
|
@ -134,7 +146,7 @@ endif
|
|||
CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
|
||||
-fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
|
||||
-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; }' | \
|
||||
$(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie)
|
||||
|
|
|
@ -77,6 +77,16 @@ extern enum vm_guest_mode vm_mode_default;
|
|||
#define MIN_PAGE_SHIFT 12U
|
||||
#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
|
||||
|
||||
#define MIN_PAGE_SIZE (1U << MIN_PAGE_SHIFT)
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/* 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, ®);
|
||||
}
|
||||
|
||||
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, ®);
|
||||
}
|
||||
|
||||
/* 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)
|
|||
guest_mode_append(VM_MODE_P47V64_4K, true, true);
|
||||
}
|
||||
#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)
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* RISC-V code
|
||||
*
|
||||
* Copyright (C) 2021 Western Digital Corporation or its affiliates.
|
||||
*/
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "kvm_util.h"
|
||||
#include "../kvm_util_internal.h"
|
||||
#include "processor.h"
|
||||
|
||||
#define DEFAULT_RISCV_GUEST_STACK_VADDR_MIN 0xac0000
|
||||
|
||||
static uint64_t page_align(struct kvm_vm *vm, uint64_t v)
|
||||
{
|
||||
return (v + vm->page_size) & ~(vm->page_size - 1);
|
||||
}
|
||||
|
||||
static uint64_t pte_addr(struct kvm_vm *vm, uint64_t entry)
|
||||
{
|
||||
return ((entry & PGTBL_PTE_ADDR_MASK) >> PGTBL_PTE_ADDR_SHIFT) <<
|
||||
PGTBL_PAGE_SIZE_SHIFT;
|
||||
}
|
||||
|
||||
static uint64_t ptrs_per_pte(struct kvm_vm *vm)
|
||||
{
|
||||
return PGTBL_PAGE_SIZE / sizeof(uint64_t);
|
||||
}
|
||||
|
||||
static uint64_t pte_index_mask[] = {
|
||||
PGTBL_L0_INDEX_MASK,
|
||||
PGTBL_L1_INDEX_MASK,
|
||||
PGTBL_L2_INDEX_MASK,
|
||||
PGTBL_L3_INDEX_MASK,
|
||||
};
|
||||
|
||||
static uint32_t pte_index_shift[] = {
|
||||
PGTBL_L0_INDEX_SHIFT,
|
||||
PGTBL_L1_INDEX_SHIFT,
|
||||
PGTBL_L2_INDEX_SHIFT,
|
||||
PGTBL_L3_INDEX_SHIFT,
|
||||
};
|
||||
|
||||
static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva, int level)
|
||||
{
|
||||
TEST_ASSERT(level > -1,
|
||||
"Negative page table level (%d) not possible", level);
|
||||
TEST_ASSERT(level < vm->pgtable_levels,
|
||||
"Invalid page table level (%d)", level);
|
||||
|
||||
return (gva & pte_index_mask[level]) >> pte_index_shift[level];
|
||||
}
|
||||
|
||||
void virt_pgd_alloc(struct kvm_vm *vm)
|
||||
{
|
||||
if (!vm->pgd_created) {
|
||||
vm_paddr_t paddr = vm_phy_pages_alloc(vm,
|
||||
page_align(vm, ptrs_per_pte(vm) * 8) / vm->page_size,
|
||||
KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
|
||||
vm->pgd = paddr;
|
||||
vm->pgd_created = true;
|
||||
}
|
||||
}
|
||||
|
||||
void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
|
||||
{
|
||||
uint64_t *ptep, next_ppn;
|
||||
int level = vm->pgtable_levels - 1;
|
||||
|
||||
TEST_ASSERT((vaddr % vm->page_size) == 0,
|
||||
"Virtual address not on page boundary,\n"
|
||||
" vaddr: 0x%lx vm->page_size: 0x%x", vaddr, vm->page_size);
|
||||
TEST_ASSERT(sparsebit_is_set(vm->vpages_valid,
|
||||
(vaddr >> vm->page_shift)),
|
||||
"Invalid virtual address, vaddr: 0x%lx", vaddr);
|
||||
TEST_ASSERT((paddr % vm->page_size) == 0,
|
||||
"Physical address not on page boundary,\n"
|
||||
" paddr: 0x%lx vm->page_size: 0x%x", paddr, vm->page_size);
|
||||
TEST_ASSERT((paddr >> vm->page_shift) <= vm->max_gfn,
|
||||
"Physical address beyond maximum supported,\n"
|
||||
" paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
|
||||
paddr, vm->max_gfn, vm->page_size);
|
||||
|
||||
ptep = addr_gpa2hva(vm, vm->pgd) + pte_index(vm, vaddr, level) * 8;
|
||||
if (!*ptep) {
|
||||
next_ppn = vm_alloc_page_table(vm) >> PGTBL_PAGE_SIZE_SHIFT;
|
||||
*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |
|
||||
PGTBL_PTE_VALID_MASK;
|
||||
}
|
||||
level--;
|
||||
|
||||
while (level > -1) {
|
||||
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +
|
||||
pte_index(vm, vaddr, level) * 8;
|
||||
if (!*ptep && level > 0) {
|
||||
next_ppn = vm_alloc_page_table(vm) >>
|
||||
PGTBL_PAGE_SIZE_SHIFT;
|
||||
*ptep = (next_ppn << PGTBL_PTE_ADDR_SHIFT) |
|
||||
PGTBL_PTE_VALID_MASK;
|
||||
}
|
||||
level--;
|
||||
}
|
||||
|
||||
paddr = paddr >> PGTBL_PAGE_SIZE_SHIFT;
|
||||
*ptep = (paddr << PGTBL_PTE_ADDR_SHIFT) |
|
||||
PGTBL_PTE_PERM_MASK | PGTBL_PTE_VALID_MASK;
|
||||
}
|
||||
|
||||
vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
|
||||
{
|
||||
uint64_t *ptep;
|
||||
int level = vm->pgtable_levels - 1;
|
||||
|
||||
if (!vm->pgd_created)
|
||||
goto unmapped_gva;
|
||||
|
||||
ptep = addr_gpa2hva(vm, vm->pgd) + pte_index(vm, gva, level) * 8;
|
||||
if (!ptep)
|
||||
goto unmapped_gva;
|
||||
level--;
|
||||
|
||||
while (level > -1) {
|
||||
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) +
|
||||
pte_index(vm, gva, level) * 8;
|
||||
if (!ptep)
|
||||
goto unmapped_gva;
|
||||
level--;
|
||||
}
|
||||
|
||||
return pte_addr(vm, *ptep) + (gva & (vm->page_size - 1));
|
||||
|
||||
unmapped_gva:
|
||||
TEST_FAIL("No mapping for vm virtual address gva: 0x%lx level: %d",
|
||||
gva, level);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent,
|
||||
uint64_t page, int level)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
static const char *const type[] = { "pte", "pmd", "pud", "p4d"};
|
||||
uint64_t pte, *ptep;
|
||||
|
||||
if (level < 0)
|
||||
return;
|
||||
|
||||
for (pte = page; pte < page + ptrs_per_pte(vm) * 8; pte += 8) {
|
||||
ptep = addr_gpa2hva(vm, pte);
|
||||
if (!*ptep)
|
||||
continue;
|
||||
fprintf(stream, "%*s%s: %lx: %lx at %p\n", indent, "",
|
||||
type[level], pte, *ptep, ptep);
|
||||
pte_dump(stream, vm, indent + 1,
|
||||
pte_addr(vm, *ptep), level - 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
|
||||
{
|
||||
int level = vm->pgtable_levels - 1;
|
||||
uint64_t pgd, *ptep;
|
||||
|
||||
if (!vm->pgd_created)
|
||||
return;
|
||||
|
||||
for (pgd = vm->pgd; pgd < vm->pgd + ptrs_per_pte(vm) * 8; pgd += 8) {
|
||||
ptep = addr_gpa2hva(vm, pgd);
|
||||
if (!*ptep)
|
||||
continue;
|
||||
fprintf(stream, "%*spgd: %lx: %lx at %p\n", indent, "",
|
||||
pgd, *ptep, ptep);
|
||||
pte_dump(stream, vm, indent + 1,
|
||||
pte_addr(vm, *ptep), level - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void riscv_vcpu_mmu_setup(struct kvm_vm *vm, int vcpuid)
|
||||
{
|
||||
unsigned long satp;
|
||||
|
||||
/*
|
||||
* The RISC-V Sv48 MMU mode supports 56-bit physical address
|
||||
* for 48-bit virtual address with 4KB last level page size.
|
||||
*/
|
||||
switch (vm->mode) {
|
||||
case VM_MODE_P52V48_4K:
|
||||
case VM_MODE_P48V48_4K:
|
||||
case VM_MODE_P40V48_4K:
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
|
||||
}
|
||||
|
||||
satp = (vm->pgd >> PGTBL_PAGE_SIZE_SHIFT) & SATP_PPN;
|
||||
satp |= SATP_MODE_48;
|
||||
|
||||
set_reg(vm, vcpuid, RISCV_CSR_REG(satp), satp);
|
||||
}
|
||||
|
||||
void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent)
|
||||
{
|
||||
struct kvm_riscv_core core;
|
||||
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(mode), &core.mode);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.pc), &core.regs.pc);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.ra), &core.regs.ra);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.sp), &core.regs.sp);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.gp), &core.regs.gp);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.tp), &core.regs.tp);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t0), &core.regs.t0);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t1), &core.regs.t1);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t2), &core.regs.t2);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s0), &core.regs.s0);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s1), &core.regs.s1);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a0), &core.regs.a0);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a1), &core.regs.a1);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a2), &core.regs.a2);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a3), &core.regs.a3);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a4), &core.regs.a4);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a5), &core.regs.a5);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a6), &core.regs.a6);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a7), &core.regs.a7);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s2), &core.regs.s2);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s3), &core.regs.s3);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s4), &core.regs.s4);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s5), &core.regs.s5);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s6), &core.regs.s6);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s7), &core.regs.s7);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s8), &core.regs.s8);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s9), &core.regs.s9);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s10), &core.regs.s10);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s11), &core.regs.s11);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t3), &core.regs.t3);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t4), &core.regs.t4);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t5), &core.regs.t5);
|
||||
get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t6), &core.regs.t6);
|
||||
|
||||
fprintf(stream,
|
||||
" MODE: 0x%lx\n", core.mode);
|
||||
fprintf(stream,
|
||||
" PC: 0x%016lx RA: 0x%016lx SP: 0x%016lx GP: 0x%016lx\n",
|
||||
core.regs.pc, core.regs.ra, core.regs.sp, core.regs.gp);
|
||||
fprintf(stream,
|
||||
" TP: 0x%016lx T0: 0x%016lx T1: 0x%016lx T2: 0x%016lx\n",
|
||||
core.regs.tp, core.regs.t0, core.regs.t1, core.regs.t2);
|
||||
fprintf(stream,
|
||||
" S0: 0x%016lx S1: 0x%016lx A0: 0x%016lx A1: 0x%016lx\n",
|
||||
core.regs.s0, core.regs.s1, core.regs.a0, core.regs.a1);
|
||||
fprintf(stream,
|
||||
" A2: 0x%016lx A3: 0x%016lx A4: 0x%016lx A5: 0x%016lx\n",
|
||||
core.regs.a2, core.regs.a3, core.regs.a4, core.regs.a5);
|
||||
fprintf(stream,
|
||||
" A6: 0x%016lx A7: 0x%016lx S2: 0x%016lx S3: 0x%016lx\n",
|
||||
core.regs.a6, core.regs.a7, core.regs.s2, core.regs.s3);
|
||||
fprintf(stream,
|
||||
" S4: 0x%016lx S5: 0x%016lx S6: 0x%016lx S7: 0x%016lx\n",
|
||||
core.regs.s4, core.regs.s5, core.regs.s6, core.regs.s7);
|
||||
fprintf(stream,
|
||||
" S8: 0x%016lx S9: 0x%016lx S10: 0x%016lx S11: 0x%016lx\n",
|
||||
core.regs.s8, core.regs.s9, core.regs.s10, core.regs.s11);
|
||||
fprintf(stream,
|
||||
" T3: 0x%016lx T4: 0x%016lx T5: 0x%016lx T6: 0x%016lx\n",
|
||||
core.regs.t3, core.regs.t4, core.regs.t5, core.regs.t6);
|
||||
}
|
||||
|
||||
static void guest_hang(void)
|
||||
{
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code)
|
||||
{
|
||||
int r;
|
||||
size_t stack_size = vm->page_size == 4096 ?
|
||||
DEFAULT_STACK_PGS * vm->page_size :
|
||||
vm->page_size;
|
||||
unsigned long stack_vaddr = vm_vaddr_alloc(vm, stack_size,
|
||||
DEFAULT_RISCV_GUEST_STACK_VADDR_MIN);
|
||||
unsigned long current_gp = 0;
|
||||
struct kvm_mp_state mps;
|
||||
|
||||
vm_vcpu_add(vm, vcpuid);
|
||||
riscv_vcpu_mmu_setup(vm, vcpuid);
|
||||
|
||||
/*
|
||||
* With SBI HSM support in KVM RISC-V, all secondary VCPUs are
|
||||
* powered-off by default so we ensure that all secondary VCPUs
|
||||
* are powered-on using KVM_SET_MP_STATE ioctl().
|
||||
*/
|
||||
mps.mp_state = KVM_MP_STATE_RUNNABLE;
|
||||
r = _vcpu_ioctl(vm, vcpuid, KVM_SET_MP_STATE, &mps);
|
||||
TEST_ASSERT(!r, "IOCTL KVM_SET_MP_STATE failed (error %d)", r);
|
||||
|
||||
/* Setup global pointer of guest to be same as the host */
|
||||
asm volatile (
|
||||
"add %0, gp, zero" : "=r" (current_gp) : : "memory");
|
||||
set_reg(vm, vcpuid, RISCV_CORE_REG(regs.gp), current_gp);
|
||||
|
||||
/* Setup stack pointer and program counter of guest */
|
||||
set_reg(vm, vcpuid, RISCV_CORE_REG(regs.sp),
|
||||
stack_vaddr + stack_size);
|
||||
set_reg(vm, vcpuid, RISCV_CORE_REG(regs.pc),
|
||||
(unsigned long)guest_code);
|
||||
|
||||
/* Setup default exception vector of guest */
|
||||
set_reg(vm, vcpuid, RISCV_CSR_REG(stvec),
|
||||
(unsigned long)guest_hang);
|
||||
}
|
||||
|
||||
void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...)
|
||||
{
|
||||
va_list ap;
|
||||
uint64_t id = RISCV_CORE_REG(regs.a0);
|
||||
int i;
|
||||
|
||||
TEST_ASSERT(num >= 1 && num <= 8, "Unsupported number of args,\n"
|
||||
" num: %u\n", num);
|
||||
|
||||
va_start(ap, num);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
switch (i) {
|
||||
case 0:
|
||||
id = RISCV_CORE_REG(regs.a0);
|
||||
break;
|
||||
case 1:
|
||||
id = RISCV_CORE_REG(regs.a1);
|
||||
break;
|
||||
case 2:
|
||||
id = RISCV_CORE_REG(regs.a2);
|
||||
break;
|
||||
case 3:
|
||||
id = RISCV_CORE_REG(regs.a3);
|
||||
break;
|
||||
case 4:
|
||||
id = RISCV_CORE_REG(regs.a4);
|
||||
break;
|
||||
case 5:
|
||||
id = RISCV_CORE_REG(regs.a5);
|
||||
break;
|
||||
case 6:
|
||||
id = RISCV_CORE_REG(regs.a6);
|
||||
break;
|
||||
case 7:
|
||||
id = RISCV_CORE_REG(regs.a7);
|
||||
break;
|
||||
};
|
||||
set_reg(vm, vcpuid, id, va_arg(ap, uint64_t));
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid)
|
||||
{
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
// 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;
|
||||
}
|
Loading…
Reference in New Issue