Contributors: 14
Author Tokens Token Proportion Commits Commit Proportion
Anup Patel 386 44.11% 24 44.44%
Atish Patra 142 16.23% 6 11.11%
Samuel Holland 83 9.49% 2 3.70%
Clément Leger 64 7.31% 4 7.41%
Quan Zhou 56 6.40% 5 9.26%
Andrew Jones 55 6.29% 2 3.70%
Yong-Xuan Wang 25 2.86% 1 1.85%
Mayuresh Chitale 18 2.06% 2 3.70%
Lukas Gerlach 11 1.26% 2 3.70%
Pincheng Wang 10 1.14% 1 1.85%
Vincent Chen 9 1.03% 2 3.70%
Xu Lu 8 0.91% 1 1.85%
Andy Chiu 7 0.80% 1 1.85%
Chao Du 1 0.11% 1 1.85%
Total 875 54


// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (c) 2026 Qualcomm Technologies, Inc.
 */

#include <linux/cpufeature.h>
#include <linux/errno.h>
#include <linux/kvm_host.h>
#include <linux/nospec.h>
#include <linux/pgtable.h>
#include <asm/kvm_isa.h>
#include <asm/vector.h>

#define KVM_ISA_EXT_ARR(ext)		\
[KVM_RISCV_ISA_EXT_##ext] = RISCV_ISA_EXT_##ext

/* Mapping between KVM ISA Extension ID & guest ISA extension ID */
static const unsigned long kvm_isa_ext_arr[] = {
	/* Single letter extensions (alphabetically sorted) */
	[KVM_RISCV_ISA_EXT_A] = RISCV_ISA_EXT_a,
	[KVM_RISCV_ISA_EXT_C] = RISCV_ISA_EXT_c,
	[KVM_RISCV_ISA_EXT_D] = RISCV_ISA_EXT_d,
	[KVM_RISCV_ISA_EXT_F] = RISCV_ISA_EXT_f,
	[KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h,
	[KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i,
	[KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m,
	[KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v,
	/* Multi letter extensions (alphabetically sorted) */
	KVM_ISA_EXT_ARR(SMNPM),
	KVM_ISA_EXT_ARR(SMSTATEEN),
	KVM_ISA_EXT_ARR(SSAIA),
	KVM_ISA_EXT_ARR(SSCOFPMF),
	KVM_ISA_EXT_ARR(SSNPM),
	KVM_ISA_EXT_ARR(SSTC),
	KVM_ISA_EXT_ARR(SVADE),
	KVM_ISA_EXT_ARR(SVADU),
	KVM_ISA_EXT_ARR(SVINVAL),
	KVM_ISA_EXT_ARR(SVNAPOT),
	KVM_ISA_EXT_ARR(SVPBMT),
	KVM_ISA_EXT_ARR(SVVPTC),
	KVM_ISA_EXT_ARR(ZAAMO),
	KVM_ISA_EXT_ARR(ZABHA),
	KVM_ISA_EXT_ARR(ZACAS),
	KVM_ISA_EXT_ARR(ZALASR),
	KVM_ISA_EXT_ARR(ZALRSC),
	KVM_ISA_EXT_ARR(ZAWRS),
	KVM_ISA_EXT_ARR(ZBA),
	KVM_ISA_EXT_ARR(ZBB),
	KVM_ISA_EXT_ARR(ZBC),
	KVM_ISA_EXT_ARR(ZBKB),
	KVM_ISA_EXT_ARR(ZBKC),
	KVM_ISA_EXT_ARR(ZBKX),
	KVM_ISA_EXT_ARR(ZBS),
	KVM_ISA_EXT_ARR(ZCA),
	KVM_ISA_EXT_ARR(ZCB),
	KVM_ISA_EXT_ARR(ZCD),
	KVM_ISA_EXT_ARR(ZCF),
	KVM_ISA_EXT_ARR(ZCLSD),
	KVM_ISA_EXT_ARR(ZCMOP),
	KVM_ISA_EXT_ARR(ZFA),
	KVM_ISA_EXT_ARR(ZFBFMIN),
	KVM_ISA_EXT_ARR(ZFH),
	KVM_ISA_EXT_ARR(ZFHMIN),
	KVM_ISA_EXT_ARR(ZICBOM),
	KVM_ISA_EXT_ARR(ZICBOP),
	KVM_ISA_EXT_ARR(ZICBOZ),
	KVM_ISA_EXT_ARR(ZICCRSE),
	KVM_ISA_EXT_ARR(ZICNTR),
	KVM_ISA_EXT_ARR(ZICOND),
	KVM_ISA_EXT_ARR(ZICSR),
	KVM_ISA_EXT_ARR(ZIFENCEI),
	KVM_ISA_EXT_ARR(ZIHINTNTL),
	KVM_ISA_EXT_ARR(ZIHINTPAUSE),
	KVM_ISA_EXT_ARR(ZIHPM),
	KVM_ISA_EXT_ARR(ZILSD),
	KVM_ISA_EXT_ARR(ZIMOP),
	KVM_ISA_EXT_ARR(ZKND),
	KVM_ISA_EXT_ARR(ZKNE),
	KVM_ISA_EXT_ARR(ZKNH),
	KVM_ISA_EXT_ARR(ZKR),
	KVM_ISA_EXT_ARR(ZKSED),
	KVM_ISA_EXT_ARR(ZKSH),
	KVM_ISA_EXT_ARR(ZKT),
	KVM_ISA_EXT_ARR(ZTSO),
	KVM_ISA_EXT_ARR(ZVBB),
	KVM_ISA_EXT_ARR(ZVBC),
	KVM_ISA_EXT_ARR(ZVFBFMIN),
	KVM_ISA_EXT_ARR(ZVFBFWMA),
	KVM_ISA_EXT_ARR(ZVFH),
	KVM_ISA_EXT_ARR(ZVFHMIN),
	KVM_ISA_EXT_ARR(ZVKB),
	KVM_ISA_EXT_ARR(ZVKG),
	KVM_ISA_EXT_ARR(ZVKNED),
	KVM_ISA_EXT_ARR(ZVKNHA),
	KVM_ISA_EXT_ARR(ZVKNHB),
	KVM_ISA_EXT_ARR(ZVKSED),
	KVM_ISA_EXT_ARR(ZVKSH),
	KVM_ISA_EXT_ARR(ZVKT),
};

unsigned long kvm_riscv_base2isa_ext(unsigned long base_ext)
{
	unsigned long i;

	for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) {
		if (kvm_isa_ext_arr[i] == base_ext)
			return i;
	}

	return KVM_RISCV_ISA_EXT_MAX;
}

int __kvm_riscv_isa_check_host(unsigned long kvm_ext, unsigned long *base_ext)
{
	unsigned long host_ext;

	if (kvm_ext >= KVM_RISCV_ISA_EXT_MAX ||
	    kvm_ext >= ARRAY_SIZE(kvm_isa_ext_arr))
		return -ENOENT;

	kvm_ext = array_index_nospec(kvm_ext, ARRAY_SIZE(kvm_isa_ext_arr));
	switch (kvm_isa_ext_arr[kvm_ext]) {
	case RISCV_ISA_EXT_SMNPM:
		/*
		 * Pointer masking effective in (H)S-mode is provided by the
		 * Smnpm extension, so that extension is reported to the guest,
		 * even though the CSR bits for configuring VS-mode pointer
		 * masking on the host side are part of the Ssnpm extension.
		 */
		host_ext = RISCV_ISA_EXT_SSNPM;
		break;
	default:
		host_ext = kvm_isa_ext_arr[kvm_ext];
		break;
	}

	if (!__riscv_isa_extension_available(NULL, host_ext))
		return -ENOENT;

	if (base_ext)
		*base_ext = kvm_isa_ext_arr[kvm_ext];

	return 0;
}

bool kvm_riscv_isa_enable_allowed(unsigned long ext)
{
	switch (ext) {
	case KVM_RISCV_ISA_EXT_H:
		return false;
	case KVM_RISCV_ISA_EXT_SSCOFPMF:
		/* Sscofpmf depends on interrupt filtering defined in ssaia */
		return !kvm_riscv_isa_check_host(SSAIA);
	case KVM_RISCV_ISA_EXT_SVADU:
		/*
		 * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero.
		 * Guest OS can use Svadu only when host OS enable Svadu.
		 */
		return arch_has_hw_pte_young();
	case KVM_RISCV_ISA_EXT_V:
		return riscv_v_vstate_ctrl_user_allowed();
	default:
		break;
	}

	return true;
}

bool kvm_riscv_isa_disable_allowed(unsigned long ext)
{
	switch (ext) {
	/* Extensions which don't have any mechanism to disable */
	case KVM_RISCV_ISA_EXT_A:
	case KVM_RISCV_ISA_EXT_C:
	case KVM_RISCV_ISA_EXT_I:
	case KVM_RISCV_ISA_EXT_M:
	/* There is not architectural config bit to disable sscofpmf completely */
	case KVM_RISCV_ISA_EXT_SSCOFPMF:
	case KVM_RISCV_ISA_EXT_SSNPM:
	case KVM_RISCV_ISA_EXT_SSTC:
	case KVM_RISCV_ISA_EXT_SVINVAL:
	case KVM_RISCV_ISA_EXT_SVNAPOT:
	case KVM_RISCV_ISA_EXT_SVVPTC:
	case KVM_RISCV_ISA_EXT_ZAAMO:
	case KVM_RISCV_ISA_EXT_ZABHA:
	case KVM_RISCV_ISA_EXT_ZACAS:
	case KVM_RISCV_ISA_EXT_ZALASR:
	case KVM_RISCV_ISA_EXT_ZALRSC:
	case KVM_RISCV_ISA_EXT_ZAWRS:
	case KVM_RISCV_ISA_EXT_ZBA:
	case KVM_RISCV_ISA_EXT_ZBB:
	case KVM_RISCV_ISA_EXT_ZBC:
	case KVM_RISCV_ISA_EXT_ZBKB:
	case KVM_RISCV_ISA_EXT_ZBKC:
	case KVM_RISCV_ISA_EXT_ZBKX:
	case KVM_RISCV_ISA_EXT_ZBS:
	case KVM_RISCV_ISA_EXT_ZCA:
	case KVM_RISCV_ISA_EXT_ZCB:
	case KVM_RISCV_ISA_EXT_ZCD:
	case KVM_RISCV_ISA_EXT_ZCF:
	case KVM_RISCV_ISA_EXT_ZCMOP:
	case KVM_RISCV_ISA_EXT_ZFA:
	case KVM_RISCV_ISA_EXT_ZFBFMIN:
	case KVM_RISCV_ISA_EXT_ZFH:
	case KVM_RISCV_ISA_EXT_ZFHMIN:
	case KVM_RISCV_ISA_EXT_ZICBOP:
	case KVM_RISCV_ISA_EXT_ZICCRSE:
	case KVM_RISCV_ISA_EXT_ZICNTR:
	case KVM_RISCV_ISA_EXT_ZICOND:
	case KVM_RISCV_ISA_EXT_ZICSR:
	case KVM_RISCV_ISA_EXT_ZIFENCEI:
	case KVM_RISCV_ISA_EXT_ZIHINTNTL:
	case KVM_RISCV_ISA_EXT_ZIHINTPAUSE:
	case KVM_RISCV_ISA_EXT_ZIHPM:
	case KVM_RISCV_ISA_EXT_ZIMOP:
	case KVM_RISCV_ISA_EXT_ZKND:
	case KVM_RISCV_ISA_EXT_ZKNE:
	case KVM_RISCV_ISA_EXT_ZKNH:
	case KVM_RISCV_ISA_EXT_ZKR:
	case KVM_RISCV_ISA_EXT_ZKSED:
	case KVM_RISCV_ISA_EXT_ZKSH:
	case KVM_RISCV_ISA_EXT_ZKT:
	case KVM_RISCV_ISA_EXT_ZTSO:
	case KVM_RISCV_ISA_EXT_ZVBB:
	case KVM_RISCV_ISA_EXT_ZVBC:
	case KVM_RISCV_ISA_EXT_ZVFBFMIN:
	case KVM_RISCV_ISA_EXT_ZVFBFWMA:
	case KVM_RISCV_ISA_EXT_ZVFH:
	case KVM_RISCV_ISA_EXT_ZVFHMIN:
	case KVM_RISCV_ISA_EXT_ZVKB:
	case KVM_RISCV_ISA_EXT_ZVKG:
	case KVM_RISCV_ISA_EXT_ZVKNED:
	case KVM_RISCV_ISA_EXT_ZVKNHA:
	case KVM_RISCV_ISA_EXT_ZVKNHB:
	case KVM_RISCV_ISA_EXT_ZVKSED:
	case KVM_RISCV_ISA_EXT_ZVKSH:
	case KVM_RISCV_ISA_EXT_ZVKT:
		return false;
	/* Extensions which can be disabled using Smstateen */
	case KVM_RISCV_ISA_EXT_SSAIA:
		return riscv_has_extension_unlikely(RISCV_ISA_EXT_SMSTATEEN);
	case KVM_RISCV_ISA_EXT_SVADE:
		/*
		 * The henvcfg.ADUE is read-only zero if menvcfg.ADUE is zero.
		 * Svade can't be disabled unless we support Svadu.
		 */
		return arch_has_hw_pte_young();
	default:
		break;
	}

	return true;
}