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

KVM/riscv changes for 7.4

- Lazy V-extension enablement for KVM selftests
- Avoid redundant interrupt check for pending timers
- Implement KVM_PRE_FAULT_MEMORY ioctl() and enable corresponding KVM selftests
- Show sepc/scause/stval CSRs in KVM selftests vcpu dump
- Reject vCPU creation after AIA initialization
- Fix build with vector support disabled
- Fix IPI delivery for out-of-order vcpu_id
- Replace imsic switch-case macros with range checks
- Use a raw lock for IMSIC wakeup state
- Reject vCPU creation after AIA initialization
- Updated next branch name in KVM/riscv MAINTAINERS entry
- Allow Zicclsm, Ziccamoa, Ziccif, Za64rs for KVM guest
This commit is contained in:
Paolo Bonzini 2026-10-22 16:48:12 +02:00
commit 7e60162b16
17 changed files with 251 additions and 141 deletions

View File

@ -14361,7 +14361,7 @@ L: kvm@vger.kernel.org
L: kvm-riscv@lists.infradead.org
L: linux-riscv@lists.infradead.org
S: Maintained
T: git https://github.com/kvm-riscv/linux.git
T: git https://github.com/kvm-riscv/linux.git riscv_kvm_next
F: arch/riscv/include/asm/kvm*
F: arch/riscv/include/uapi/asm/kvm*
F: arch/riscv/kvm/

View File

@ -11,9 +11,9 @@
#define __KVM_VCPU_RISCV_VECTOR_H
#include <linux/types.h>
#include <asm/vector.h>
#ifdef CONFIG_RISCV_ISA_V
#include <asm/vector.h>
#include <asm/kvm_host.h>
static __always_inline void __kvm_riscv_vector_save(struct kvm_cpu_context *context)
@ -55,6 +55,10 @@ static inline void kvm_riscv_v_exit(void)
struct kvm_cpu_context;
static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context)
{
}
static inline void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
{
}

View File

@ -206,6 +206,10 @@ enum KVM_RISCV_ISA_EXT_ID {
KVM_RISCV_ISA_EXT_ZALASR,
KVM_RISCV_ISA_EXT_ZICFILP,
KVM_RISCV_ISA_EXT_ZICFISS,
KVM_RISCV_ISA_EXT_ZICCLSM,
KVM_RISCV_ISA_EXT_ZICCAMOA,
KVM_RISCV_ISA_EXT_ZICCIF,
KVM_RISCV_ISA_EXT_ZA64RS,
KVM_RISCV_ISA_EXT_MAX,
};

View File

@ -26,6 +26,7 @@ config KVM
select KVM_COMMON
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_GENERIC_HARDWARE_ENABLING
select KVM_GENERIC_PRE_FAULT_MEMORY
select VIRT_XFER_TO_GUEST_WORK
select SCHED_INFO
select GUEST_PERF_EVENTS if PERF_EVENTS

View File

@ -48,6 +48,8 @@ struct imsic {
/* IMSIC VS-file */
rwlock_t vsfile_lock;
/* Writers hold both locks; scheduler-out only takes vsfile_hgei_lock. */
raw_spinlock_t vsfile_hgei_lock;
int vsfile_cpu;
int vsfile_hgei;
void __iomem *vsfile_va;
@ -67,36 +69,17 @@ struct imsic {
__r; \
})
#define imsic_read_switchcase(__ireg) \
case __ireg: \
return imsic_vs_csr_read(__ireg);
#define imsic_read_switchcase_2(__ireg) \
imsic_read_switchcase(__ireg + 0) \
imsic_read_switchcase(__ireg + 1)
#define imsic_read_switchcase_4(__ireg) \
imsic_read_switchcase_2(__ireg + 0) \
imsic_read_switchcase_2(__ireg + 2)
#define imsic_read_switchcase_8(__ireg) \
imsic_read_switchcase_4(__ireg + 0) \
imsic_read_switchcase_4(__ireg + 4)
#define imsic_read_switchcase_16(__ireg) \
imsic_read_switchcase_8(__ireg + 0) \
imsic_read_switchcase_8(__ireg + 8)
#define imsic_read_switchcase_32(__ireg) \
imsic_read_switchcase_16(__ireg + 0) \
imsic_read_switchcase_16(__ireg + 16)
#define imsic_read_switchcase_64(__ireg) \
imsic_read_switchcase_32(__ireg + 0) \
imsic_read_switchcase_32(__ireg + 32)
static inline bool imsic_eix_ireg_is_valid(int ireg)
{
return (ireg >= IMSIC_EIP0 && ireg <= IMSIC_EIP63) ||
(ireg >= IMSIC_EIE0 && ireg <= IMSIC_EIE63);
}
static unsigned long imsic_eix_read(int ireg)
{
switch (ireg) {
imsic_read_switchcase_64(IMSIC_EIP0)
imsic_read_switchcase_64(IMSIC_EIE0)
}
return 0;
if (!imsic_eix_ireg_is_valid(ireg))
return 0;
return imsic_vs_csr_read(ireg);
}
#define imsic_vs_csr_swap(__c, __v) \
@ -107,36 +90,11 @@ static unsigned long imsic_eix_read(int ireg)
__r; \
})
#define imsic_swap_switchcase(__ireg, __v) \
case __ireg: \
return imsic_vs_csr_swap(__ireg, __v);
#define imsic_swap_switchcase_2(__ireg, __v) \
imsic_swap_switchcase(__ireg + 0, __v) \
imsic_swap_switchcase(__ireg + 1, __v)
#define imsic_swap_switchcase_4(__ireg, __v) \
imsic_swap_switchcase_2(__ireg + 0, __v) \
imsic_swap_switchcase_2(__ireg + 2, __v)
#define imsic_swap_switchcase_8(__ireg, __v) \
imsic_swap_switchcase_4(__ireg + 0, __v) \
imsic_swap_switchcase_4(__ireg + 4, __v)
#define imsic_swap_switchcase_16(__ireg, __v) \
imsic_swap_switchcase_8(__ireg + 0, __v) \
imsic_swap_switchcase_8(__ireg + 8, __v)
#define imsic_swap_switchcase_32(__ireg, __v) \
imsic_swap_switchcase_16(__ireg + 0, __v) \
imsic_swap_switchcase_16(__ireg + 16, __v)
#define imsic_swap_switchcase_64(__ireg, __v) \
imsic_swap_switchcase_32(__ireg + 0, __v) \
imsic_swap_switchcase_32(__ireg + 32, __v)
static unsigned long imsic_eix_swap(int ireg, unsigned long val)
{
switch (ireg) {
imsic_swap_switchcase_64(IMSIC_EIP0, val)
imsic_swap_switchcase_64(IMSIC_EIE0, val)
}
return 0;
if (!imsic_eix_ireg_is_valid(ireg))
return 0;
return imsic_vs_csr_swap(ireg, val);
}
#define imsic_vs_csr_write(__c, __v) \
@ -145,35 +103,10 @@ do { \
csr_write(CSR_VSIREG, __v); \
} while (0)
#define imsic_write_switchcase(__ireg, __v) \
case __ireg: \
imsic_vs_csr_write(__ireg, __v); \
break;
#define imsic_write_switchcase_2(__ireg, __v) \
imsic_write_switchcase(__ireg + 0, __v) \
imsic_write_switchcase(__ireg + 1, __v)
#define imsic_write_switchcase_4(__ireg, __v) \
imsic_write_switchcase_2(__ireg + 0, __v) \
imsic_write_switchcase_2(__ireg + 2, __v)
#define imsic_write_switchcase_8(__ireg, __v) \
imsic_write_switchcase_4(__ireg + 0, __v) \
imsic_write_switchcase_4(__ireg + 4, __v)
#define imsic_write_switchcase_16(__ireg, __v) \
imsic_write_switchcase_8(__ireg + 0, __v) \
imsic_write_switchcase_8(__ireg + 8, __v)
#define imsic_write_switchcase_32(__ireg, __v) \
imsic_write_switchcase_16(__ireg + 0, __v) \
imsic_write_switchcase_16(__ireg + 16, __v)
#define imsic_write_switchcase_64(__ireg, __v) \
imsic_write_switchcase_32(__ireg + 0, __v) \
imsic_write_switchcase_32(__ireg + 32, __v)
static void imsic_eix_write(int ireg, unsigned long val)
{
switch (ireg) {
imsic_write_switchcase_64(IMSIC_EIP0, val)
imsic_write_switchcase_64(IMSIC_EIE0, val)
}
if (imsic_eix_ireg_is_valid(ireg))
imsic_vs_csr_write(ireg, val);
}
#define imsic_vs_csr_set(__c, __v) \
@ -182,35 +115,10 @@ do { \
csr_set(CSR_VSIREG, __v); \
} while (0)
#define imsic_set_switchcase(__ireg, __v) \
case __ireg: \
imsic_vs_csr_set(__ireg, __v); \
break;
#define imsic_set_switchcase_2(__ireg, __v) \
imsic_set_switchcase(__ireg + 0, __v) \
imsic_set_switchcase(__ireg + 1, __v)
#define imsic_set_switchcase_4(__ireg, __v) \
imsic_set_switchcase_2(__ireg + 0, __v) \
imsic_set_switchcase_2(__ireg + 2, __v)
#define imsic_set_switchcase_8(__ireg, __v) \
imsic_set_switchcase_4(__ireg + 0, __v) \
imsic_set_switchcase_4(__ireg + 4, __v)
#define imsic_set_switchcase_16(__ireg, __v) \
imsic_set_switchcase_8(__ireg + 0, __v) \
imsic_set_switchcase_8(__ireg + 8, __v)
#define imsic_set_switchcase_32(__ireg, __v) \
imsic_set_switchcase_16(__ireg + 0, __v) \
imsic_set_switchcase_16(__ireg + 16, __v)
#define imsic_set_switchcase_64(__ireg, __v) \
imsic_set_switchcase_32(__ireg + 0, __v) \
imsic_set_switchcase_32(__ireg + 32, __v)
static void imsic_eix_set(int ireg, unsigned long val)
{
switch (ireg) {
imsic_set_switchcase_64(IMSIC_EIP0, val)
imsic_set_switchcase_64(IMSIC_EIE0, val)
}
if (imsic_eix_ireg_is_valid(ireg))
imsic_vs_csr_set(ireg, val);
}
static unsigned long imsic_mrif_atomic_rmw(struct imsic_mrif *mrif,
@ -582,6 +490,17 @@ static void imsic_vsfile_local_update(int vsfile_hgei, u32 nr_eix,
csr_write(CSR_VSISELECT, old_vsiselect);
}
static void imsic_vsfile_set_hgei(struct imsic *imsic, int cpu, int hgei)
{
unsigned long flags;
/* Serialize the CPU/HGEI pair with scheduler-out's wakeup setup. */
raw_spin_lock_irqsave(&imsic->vsfile_hgei_lock, flags);
imsic->vsfile_cpu = cpu;
imsic->vsfile_hgei = hgei;
raw_spin_unlock_irqrestore(&imsic->vsfile_hgei_lock, flags);
}
static void imsic_vsfile_cleanup(struct imsic *imsic)
{
int old_vsfile_hgei, old_vsfile_cpu;
@ -596,7 +515,7 @@ static void imsic_vsfile_cleanup(struct imsic *imsic)
write_lock_irqsave(&imsic->vsfile_lock, flags);
old_vsfile_hgei = imsic->vsfile_hgei;
old_vsfile_cpu = imsic->vsfile_cpu;
imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
imsic_vsfile_set_hgei(imsic, -1, -1);
imsic->vsfile_va = NULL;
imsic->vsfile_pa = 0;
write_unlock_irqrestore(&imsic->vsfile_lock, flags);
@ -727,10 +646,10 @@ void kvm_riscv_vcpu_aia_imsic_put(struct kvm_vcpu *vcpu)
if (!kvm_vcpu_is_blocking(vcpu))
return;
read_lock_irqsave(&imsic->vsfile_lock, flags);
raw_spin_lock_irqsave(&imsic->vsfile_hgei_lock, flags);
if (imsic->vsfile_cpu > -1)
csr_set(CSR_HGEIE, BIT(imsic->vsfile_hgei));
read_unlock_irqrestore(&imsic->vsfile_lock, flags);
raw_spin_unlock_irqrestore(&imsic->vsfile_hgei_lock, flags);
}
void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
@ -747,7 +666,7 @@ void kvm_riscv_vcpu_aia_imsic_release(struct kvm_vcpu *vcpu)
write_lock_irqsave(&imsic->vsfile_lock, flags);
old_vsfile_hgei = imsic->vsfile_hgei;
old_vsfile_cpu = imsic->vsfile_cpu;
imsic->vsfile_cpu = imsic->vsfile_hgei = -1;
imsic_vsfile_set_hgei(imsic, -1, -1);
imsic->vsfile_va = NULL;
imsic->vsfile_pa = 0;
write_unlock_irqrestore(&imsic->vsfile_lock, flags);
@ -861,8 +780,7 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu)
/* Update new IMSIC VS-file details in IMSIC context */
write_lock_irqsave(&imsic->vsfile_lock, flags);
imsic->vsfile_hgei = new_vsfile_hgei;
imsic->vsfile_cpu = vcpu->cpu;
imsic_vsfile_set_hgei(imsic, vcpu->cpu, new_vsfile_hgei);
imsic->vsfile_va = new_vsfile_va;
imsic->vsfile_pa = new_vsfile_pa;
write_unlock_irqrestore(&imsic->vsfile_lock, flags);
@ -1119,6 +1037,7 @@ int kvm_riscv_vcpu_aia_imsic_init(struct kvm_vcpu *vcpu)
/* Setup IMSIC context */
imsic->nr_msis = kvm->arch.aia.nr_ids + 1;
rwlock_init(&imsic->vsfile_lock);
raw_spin_lock_init(&imsic->vsfile_hgei_lock);
imsic->nr_eix = BITS_TO_U64(imsic->nr_msis);
imsic->nr_hw_eix = BITS_TO_U64(kvm_riscv_aia_max_ids);
imsic->vsfile_hgei = imsic->vsfile_cpu = -1;

View File

@ -216,12 +216,10 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
pte_t *ptep;
int ret;
out_map->addr = gpa;
out_map->level = 0;
ret = gstage_page_size_to_level(gstage, page_size, &out_map->level);
if (ret)
return ret;
out_map->addr = ALIGN_DOWN(gpa, page_size);
/*
* A RISC-V implementation can choose to either:
@ -280,6 +278,8 @@ int kvm_riscv_gstage_map_page(struct kvm_gstage *gstage,
out_map->level, true);
} else if (ALIGN_DOWN(PFN_PHYS(pte_pfn(ptep_get(ptep))), page_size) == hpa) {
kvm_riscv_gstage_update_pte_prot(gstage, ptep_level, gpa, ptep, prot);
out_map->level = ptep_level;
out_map->pte = ptep_get(ptep);
return 0;
}
}

View File

@ -38,6 +38,7 @@ static const unsigned long kvm_isa_ext_arr[] = {
KVM_ISA_EXT_ARR(SVNAPOT),
KVM_ISA_EXT_ARR(SVPBMT),
KVM_ISA_EXT_ARR(SVVPTC),
KVM_ISA_EXT_ARR(ZA64RS),
KVM_ISA_EXT_ARR(ZAAMO),
KVM_ISA_EXT_ARR(ZABHA),
KVM_ISA_EXT_ARR(ZACAS),
@ -64,6 +65,9 @@ static const unsigned long kvm_isa_ext_arr[] = {
KVM_ISA_EXT_ARR(ZICBOM),
KVM_ISA_EXT_ARR(ZICBOP),
KVM_ISA_EXT_ARR(ZICBOZ),
KVM_ISA_EXT_ARR(ZICCAMOA),
KVM_ISA_EXT_ARR(ZICCIF),
KVM_ISA_EXT_ARR(ZICCLSM),
KVM_ISA_EXT_ARR(ZICCRSE),
KVM_ISA_EXT_ARR(ZICFILP),
KVM_ISA_EXT_ARR(ZICFISS),
@ -183,6 +187,7 @@ bool kvm_riscv_isa_disable_allowed(unsigned long ext)
case KVM_RISCV_ISA_EXT_SVINVAL:
case KVM_RISCV_ISA_EXT_SVNAPOT:
case KVM_RISCV_ISA_EXT_SVVPTC:
case KVM_RISCV_ISA_EXT_ZA64RS:
case KVM_RISCV_ISA_EXT_ZAAMO:
case KVM_RISCV_ISA_EXT_ZABHA:
case KVM_RISCV_ISA_EXT_ZACAS:
@ -206,6 +211,9 @@ bool kvm_riscv_isa_disable_allowed(unsigned long ext)
case KVM_RISCV_ISA_EXT_ZFH:
case KVM_RISCV_ISA_EXT_ZFHMIN:
case KVM_RISCV_ISA_EXT_ZICBOP:
case KVM_RISCV_ISA_EXT_ZICCAMOA:
case KVM_RISCV_ISA_EXT_ZICCIF:
case KVM_RISCV_ISA_EXT_ZICCLSM:
case KVM_RISCV_ISA_EXT_ZICCRSE:
case KVM_RISCV_ISA_EXT_ZICNTR:
case KVM_RISCV_ISA_EXT_ZICOND:

View File

@ -754,6 +754,53 @@ int kvm_riscv_mmu_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot,
return ret;
}
int kvm_arch_pre_fault_allowed(struct kvm_vcpu *vcpu)
{
return 0;
}
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu,
struct kvm_pre_fault_memory *range)
{
struct kvm_gstage_mapping out_map = { 0 };
struct kvm_memory_slot *memslot;
unsigned long map_size;
unsigned long hva;
gpa_t end;
gfn_t gfn;
int ret;
gfn = gpa_to_gfn(range->gpa);
memslot = kvm_vcpu_gfn_to_memslot(vcpu, gfn);
if (!memslot)
return -ENOENT;
hva = gfn_to_hva_memslot_prot(memslot, gfn, NULL);
if (kvm_is_error_hva(hva))
return -ENOENT;
for (;;) {
if (signal_pending(current))
return -EINTR;
if (kvm_test_request(KVM_REQ_VM_DEAD, vcpu))
return -EIO;
cond_resched();
ret = kvm_riscv_mmu_map(vcpu, memslot, range->gpa, hva, false, &out_map);
if (ret)
return ret;
if (pte_val(out_map.pte))
break;
}
map_size = PAGE_SIZE << (out_map.level * kvm_riscv_gstage_index_bits);
end = ALIGN_DOWN(range->gpa, map_size) + map_size;
return min_t(u64, range->size, end - range->gpa);
}
int kvm_riscv_mmu_alloc_pgd(struct kvm *kvm)
{
struct page *pgd_page;

View File

@ -138,6 +138,9 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
{
if (kvm_riscv_aia_initialized(kvm))
return -EBUSY;
return 0;
}

View File

@ -647,7 +647,7 @@ int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
vcpu->arch.mmio_decode.return_handled = 1;
insn = vcpu->arch.mmio_decode.insn;
if (run->mmio.is_write)
if (run->mmio.is_write || !((insn >> SH_RD) & MASK_RX))
goto done;
len = vcpu->arch.mmio_decode.len;

View File

@ -64,8 +64,17 @@ static int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
if (tmp->vcpu_id < hbase)
continue;
hart_bit = tmp->vcpu_id - hbase;
/*
* kvm_for_each_vcpu() walks kvm->vcpus[] by
* vcpu_idx, i.e. the creation order, which has
* nothing to do with the vcpu_id (hart id) space
* that the SBI IPI operates on. vcpu_ids need not
* increase along the iteration, so harts outside
* the hart_mask window must be skipped instead of
* aborting the loop.
*/
if (hart_bit >= __riscv_xlen)
goto done;
continue;
if (!(hmask & (1UL << hart_bit)))
continue;
}
@ -76,7 +85,6 @@ static int kvm_sbi_ext_ipi_handler(struct kvm_vcpu *vcpu, struct kvm_run *run,
kvm_riscv_vcpu_pmu_incr_fw(tmp, SBI_PMU_FW_IPI_RCVD);
}
done:
if (hbase != -1UL && (hmask ^ sentmask))
retdata->err_val = SBI_ERR_INVALID_PARAM;

View File

@ -135,11 +135,7 @@ bool kvm_riscv_vcpu_timer_pending(struct kvm_vcpu *vcpu)
struct kvm_vcpu_timer *t = &vcpu->arch.timer;
struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer;
if (!kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t) ||
kvm_riscv_vcpu_has_interrupts(vcpu, 1UL << IRQ_VS_TIMER))
return true;
else
return false;
return !kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t);
}
static void kvm_riscv_vcpu_timer_blocking(struct kvm_vcpu *vcpu)

View File

@ -187,6 +187,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_MP_STATE:
case KVM_CAP_IMMEDIATE_EXIT:
case KVM_CAP_SET_GUEST_DEBUG:
case KVM_CAP_PRE_FAULT_MEMORY:
r = 1;
break;
case KVM_CAP_NR_VCPUS:

View File

@ -242,6 +242,7 @@ TEST_GEN_PROGS_riscv += coalesced_io_test
TEST_GEN_PROGS_riscv += dirty_log_perf_test
TEST_GEN_PROGS_riscv += get-reg-list
TEST_GEN_PROGS_riscv += mmu_stress_test
TEST_GEN_PROGS_riscv += pre_fault_memory_test
TEST_GEN_PROGS_riscv += rseq_test
TEST_GEN_PROGS_riscv += steal_time

View File

@ -12,6 +12,9 @@
#include <asm/vdso/processor.h>
#include "kvm_util.h"
#define PAGE_SHIFT 12
#define PAGE_SIZE BIT_ULL(PAGE_SHIFT)
#define INSN_OPCODE_MASK 0x007c
#define INSN_OPCODE_SHIFT 2
#define INSN_OPCODE_SYSTEM 28
@ -25,6 +28,19 @@
#define GET_RM(insn) (((insn) & INSN_MASK_FUNCT3) >> INSN_SHIFT_FUNCT3)
#define GET_CSR_NUM(insn) (((insn) & INSN_CSR_MASK) >> INSN_CSR_SHIFT)
/* Vector (V) instruction decoding, matching arch/riscv/include/asm/insn.h */
#define RV_INSN_OPCODE_MASK 0x7f
#define RVG_OPCODE_SYSTEM 0x73
#define RVV_OPCODE_VECTOR 0x57
#define RVV_OPCODE_VL 0x07
#define RVV_OPCODE_VS 0x27
#define RVV_VL_VS_WIDTH_8 0
#define RVV_VL_VS_WIDTH_16 5
#define RVV_VL_VS_WIDTH_32 6
#define RVV_VL_VS_WIDTH_64 7
#define RVV_EXTRACT_VL_VS_WIDTH(insn) (((insn) >> 12) & 0x7)
#define RVG_EXTRACT_SYSTEM_CSR(insn) (((insn) >> 20) & 0xfff)
static inline u64 __kvm_reg_id(u64 type, u64 subtype, u64 idx, u64 size)
{
return KVM_REG_RISCV | type | subtype | idx | size;
@ -127,6 +143,9 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, exception_handl
void vm_install_interrupt_handler(struct kvm_vm *vm, exception_handler_fn handler);
/* L4 index Bit[56:48] */
#define PGTBL_L4_INDEX_MASK 0x01FF000000000000ULL
#define PGTBL_L4_INDEX_SHIFT 48
/* L3 index Bit[47:39] */
#define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL
#define PGTBL_L3_INDEX_SHIFT 39

View File

@ -17,6 +17,11 @@
static gva_t exception_handlers;
struct handlers {
exception_handler_fn exception_handlers[NR_VECTORS][NR_EXCEPTIONS];
bool v_available;
};
bool __vcpu_has_ext(struct kvm_vcpu *vcpu, u64 ext)
{
unsigned long value = 0;
@ -43,6 +48,7 @@ static u64 pte_index_mask[] = {
PGTBL_L1_INDEX_MASK,
PGTBL_L2_INDEX_MASK,
PGTBL_L3_INDEX_MASK,
PGTBL_L4_INDEX_MASK,
};
static u32 pte_index_shift[] = {
@ -50,6 +56,7 @@ static u32 pte_index_shift[] = {
PGTBL_L1_INDEX_SHIFT,
PGTBL_L2_INDEX_SHIFT,
PGTBL_L3_INDEX_SHIFT,
PGTBL_L4_INDEX_SHIFT,
};
static u64 pte_index(struct kvm_vm *vm, gva_t gva, int level)
@ -233,6 +240,7 @@ void riscv_vcpu_mmu_setup(struct kvm_vcpu *vcpu)
void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
{
struct kvm_riscv_core core;
struct kvm_riscv_csr general_csr;
core.mode = vcpu_get_reg(vcpu, RISCV_CORE_REG(mode));
core.regs.pc = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.pc));
@ -268,6 +276,10 @@ void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
core.regs.t5 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t5));
core.regs.t6 = vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t6));
general_csr.sepc = vcpu_get_reg(vcpu, RISCV_GENERAL_CSR_REG(sepc));
general_csr.scause = vcpu_get_reg(vcpu, RISCV_GENERAL_CSR_REG(scause));
general_csr.stval = vcpu_get_reg(vcpu, RISCV_GENERAL_CSR_REG(stval));
fprintf(stream,
" MODE: 0x%lx\n", core.mode);
fprintf(stream,
@ -294,13 +306,9 @@ void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, u8 indent)
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 __aligned(16) guest_unexp_trap(void)
{
sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,
KVM_RISCV_SELFTESTS_SBI_UNEXP,
0, 0, 0, 0, 0, 0);
fprintf(stream,
"SEPC: 0x%016lx SCAUSE: 0x%016lx STVAL: 0x%016lx\n",
general_csr.sepc, general_csr.scause, general_csr.stval);
}
void vcpu_arch_set_entry_point(struct kvm_vcpu *vcpu, void *guest_code)
@ -346,8 +354,26 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
/* Setup sscratch for guest_get_vcpuid() */
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(sscratch), vcpu_id);
/* Setup default exception vector of guest */
vcpu_set_reg(vcpu, RISCV_GENERAL_CSR_REG(stvec), (unsigned long)guest_unexp_trap);
/*
* Advertise V to KVM so -O2 auto-vectorization in guest code is valid;
* ignore errors since the tests work without V too. Use the full
* exception vector table (which lazily enables V in route_exception())
* as the default handler; vm_init_vector_tables() is idempotent.
*/
__vcpu_set_reg(vcpu, RISCV_ISA_EXT_REG(KVM_RISCV_ISA_EXT_V), 1);
vm_init_vector_tables(vm);
vcpu_init_vector_tables(vcpu);
/*
* Record V availability for route_exception(), which runs in guest
* context. V is enabled uniformly for every vCPU, so this is a
* VM-wide property.
*/
{
struct handlers *h = addr_gva2hva(vm, vm->handlers);
h->v_available = __vcpu_has_isa_ext(vcpu, KVM_RISCV_ISA_EXT_V);
}
return vcpu;
}
@ -406,19 +432,43 @@ void assert_on_unhandled_exception(struct kvm_vcpu *vcpu)
struct ucall uc;
if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) {
vcpu_dump(stderr, vcpu, 2);
TEST_FAIL("Unexpected exception (vector:0x%lx, ec:0x%lx)",
uc.args[0], uc.args[1]);
}
}
struct handlers {
exception_handler_fn exception_handlers[NR_VECTORS][NR_EXCEPTIONS];
};
static bool insn_is_vector(u32 insn)
{
u32 opcode = insn & RV_INSN_OPCODE_MASK;
u32 width, csr;
/* All V-related instructions are 4-byte, i.e. not compressed. */
if ((insn & 0x3) != 0x3)
return false;
switch (opcode) {
case RVV_OPCODE_VECTOR:
return true;
case RVV_OPCODE_VL:
case RVV_OPCODE_VS:
width = RVV_EXTRACT_VL_VS_WIDTH(insn);
return width == RVV_VL_VS_WIDTH_8 || width == RVV_VL_VS_WIDTH_16 ||
width == RVV_VL_VS_WIDTH_32 || width == RVV_VL_VS_WIDTH_64;
case RVG_OPCODE_SYSTEM:
csr = RVG_EXTRACT_SYSTEM_CSR(insn);
return (csr >= CSR_VSTART && csr <= CSR_VCSR) ||
(csr >= CSR_VL && csr <= CSR_VLENB);
}
return false;
}
void route_exception(struct pt_regs *regs)
{
struct handlers *handlers = (struct handlers *)exception_handlers;
int vector = 0, ec;
int vector = 0;
unsigned long ec;
ec = regs->cause & ~CAUSE_IRQ_FLAG;
if (ec >= NR_EXCEPTIONS)
@ -430,6 +480,36 @@ void route_exception(struct pt_regs *regs)
ec = 0;
}
/*
* Lazily enable V on the first vector instruction: if the faulting
* instruction decodes as vector while VS is off, set VS to Initial
* and re-execute it, like the kernel's riscv_v_first_use_handler().
* Genuinely illegal instructions continue to the unexpected-exception
* path.
*/
if (!(regs->cause & CAUSE_IRQ_FLAG) && ec == EXC_INST_ILLEGAL &&
handlers && handlers->v_available && !(regs->status & SR_VS)) {
u32 insn = (u32)regs->badaddr;
/*
* stval is not guaranteed to hold the faulting instruction.
* Vector instructions are always 32-bit, so check the first
* 16-bit halfword and, for a 32-bit instruction, assemble it
* from two halfwords to avoid an unaligned 32-bit access.
*/
if (!insn) {
u16 half = *(u16 *)regs->epc;
if ((half & 0x3) == 0x3)
insn = (u32)half | ((u32)*(u16 *)(regs->epc + 2) << 16);
}
if (insn_is_vector(insn)) {
regs->status |= SR_VS_INITIAL;
return;
}
}
if (handlers && handlers->exception_handlers[vector][ec])
return handlers->exception_handlers[vector][ec](regs);
@ -446,6 +526,9 @@ void vcpu_init_vector_tables(struct kvm_vcpu *vcpu)
void vm_init_vector_tables(struct kvm_vm *vm)
{
if (vm->handlers)
return;
vm->handlers = __vm_alloc(vm, sizeof(struct handlers), vm->page_size,
MEM_REGION_DATA);

View File

@ -63,6 +63,7 @@ bool filter_reg(__u64 reg)
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_SVNAPOT:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_SVPBMT:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_SVVPTC:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZA64RS:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZAAMO:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZABHA:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZACAS:
@ -89,6 +90,9 @@ bool filter_reg(__u64 reg)
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICBOM:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICBOP:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICBOZ:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICCAMOA:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICCIF:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICCLSM:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICCRSE:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICFILP:
case KVM_REG_RISCV_ISA_EXT | KVM_REG_RISCV_ISA_SINGLE | KVM_RISCV_ISA_EXT_ZICFISS:
@ -576,6 +580,7 @@ static const char *isa_ext_single_id_to_str(__u64 reg_off)
KVM_ISA_EXT_ARR(SVNAPOT),
KVM_ISA_EXT_ARR(SVPBMT),
KVM_ISA_EXT_ARR(SVVPTC),
KVM_ISA_EXT_ARR(ZA64RS),
KVM_ISA_EXT_ARR(ZAAMO),
KVM_ISA_EXT_ARR(ZABHA),
KVM_ISA_EXT_ARR(ZACAS),
@ -602,6 +607,9 @@ static const char *isa_ext_single_id_to_str(__u64 reg_off)
KVM_ISA_EXT_ARR(ZICBOM),
KVM_ISA_EXT_ARR(ZICBOP),
KVM_ISA_EXT_ARR(ZICBOZ),
KVM_ISA_EXT_ARR(ZICCAMOA),
KVM_ISA_EXT_ARR(ZICCIF),
KVM_ISA_EXT_ARR(ZICCLSM),
KVM_ISA_EXT_ARR(ZICCRSE),
KVM_ISA_EXT_ARR(ZICFILP),
KVM_ISA_EXT_ARR(ZICFISS),
@ -1238,6 +1246,7 @@ KVM_ISA_EXT_SIMPLE_CONFIG(svinval, SVINVAL);
KVM_ISA_EXT_SIMPLE_CONFIG(svnapot, SVNAPOT);
KVM_ISA_EXT_SIMPLE_CONFIG(svpbmt, SVPBMT);
KVM_ISA_EXT_SIMPLE_CONFIG(svvptc, SVVPTC);
KVM_ISA_EXT_SIMPLE_CONFIG(za64rs, ZA64RS);
KVM_ISA_EXT_SIMPLE_CONFIG(zaamo, ZAAMO);
KVM_ISA_EXT_SIMPLE_CONFIG(zabha, ZABHA);
KVM_ISA_EXT_SIMPLE_CONFIG(zacas, ZACAS);
@ -1264,6 +1273,9 @@ KVM_ISA_EXT_SIMPLE_CONFIG(zfhmin, ZFHMIN);
KVM_ISA_EXT_SUBLIST_CONFIG(zicbom, ZICBOM);
KVM_ISA_EXT_SUBLIST_CONFIG(zicbop, ZICBOP);
KVM_ISA_EXT_SUBLIST_CONFIG(zicboz, ZICBOZ);
KVM_ISA_EXT_SIMPLE_CONFIG(ziccamoa, ZICCAMOA);
KVM_ISA_EXT_SIMPLE_CONFIG(ziccif, ZICCIF);
KVM_ISA_EXT_SIMPLE_CONFIG(zicclsm, ZICCLSM);
KVM_ISA_EXT_SIMPLE_CONFIG(ziccrse, ZICCRSE);
KVM_ISA_EXT_SIMPLE_CONFIG(zicfilp, ZICFILP);
KVM_ISA_EXT_SUBLIST_CONFIG(zicfiss, ZICFISS);
@ -1372,6 +1384,7 @@ struct vcpu_reg_list *vcpu_configs[] = {
&config_svnapot,
&config_svpbmt,
&config_svvptc,
&config_za64rs,
&config_zaamo,
&config_zabha,
&config_zacas,
@ -1398,6 +1411,9 @@ struct vcpu_reg_list *vcpu_configs[] = {
&config_zicbom,
&config_zicbop,
&config_zicboz,
&config_ziccamoa,
&config_ziccif,
&config_zicclsm,
&config_ziccrse,
&config_zicfilp,
&config_zicfiss,