162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* hypercalls: Check the ARM64's psuedo-firmware bitmap register interface.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * The test validates the basic hypercall functionalities that are exposed
662306a36Sopenharmony_ci * via the psuedo-firmware bitmap register. This includes the registers'
762306a36Sopenharmony_ci * read/write behavior before and after the VM has started, and if the
862306a36Sopenharmony_ci * hypercalls are properly masked or unmasked to the guest when disabled or
962306a36Sopenharmony_ci * enabled from the KVM userspace, respectively.
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <errno.h>
1262306a36Sopenharmony_ci#include <linux/arm-smccc.h>
1362306a36Sopenharmony_ci#include <asm/kvm.h>
1462306a36Sopenharmony_ci#include <kvm_util.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "processor.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define FW_REG_ULIMIT_VAL(max_feat_bit) (GENMASK(max_feat_bit, 0))
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Last valid bits of the bitmapped firmware registers */
2162306a36Sopenharmony_ci#define KVM_REG_ARM_STD_BMAP_BIT_MAX		0
2262306a36Sopenharmony_ci#define KVM_REG_ARM_STD_HYP_BMAP_BIT_MAX	0
2362306a36Sopenharmony_ci#define KVM_REG_ARM_VENDOR_HYP_BMAP_BIT_MAX	1
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistruct kvm_fw_reg_info {
2662306a36Sopenharmony_ci	uint64_t reg;		/* Register definition */
2762306a36Sopenharmony_ci	uint64_t max_feat_bit;	/* Bit that represents the upper limit of the feature-map */
2862306a36Sopenharmony_ci};
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define FW_REG_INFO(r)			\
3162306a36Sopenharmony_ci	{					\
3262306a36Sopenharmony_ci		.reg = r,			\
3362306a36Sopenharmony_ci		.max_feat_bit = r##_BIT_MAX,	\
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic const struct kvm_fw_reg_info fw_reg_info[] = {
3762306a36Sopenharmony_ci	FW_REG_INFO(KVM_REG_ARM_STD_BMAP),
3862306a36Sopenharmony_ci	FW_REG_INFO(KVM_REG_ARM_STD_HYP_BMAP),
3962306a36Sopenharmony_ci	FW_REG_INFO(KVM_REG_ARM_VENDOR_HYP_BMAP),
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cienum test_stage {
4362306a36Sopenharmony_ci	TEST_STAGE_REG_IFACE,
4462306a36Sopenharmony_ci	TEST_STAGE_HVC_IFACE_FEAT_DISABLED,
4562306a36Sopenharmony_ci	TEST_STAGE_HVC_IFACE_FEAT_ENABLED,
4662306a36Sopenharmony_ci	TEST_STAGE_HVC_IFACE_FALSE_INFO,
4762306a36Sopenharmony_ci	TEST_STAGE_END,
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int stage = TEST_STAGE_REG_IFACE;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistruct test_hvc_info {
5362306a36Sopenharmony_ci	uint32_t func_id;
5462306a36Sopenharmony_ci	uint64_t arg1;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define TEST_HVC_INFO(f, a1)	\
5862306a36Sopenharmony_ci	{			\
5962306a36Sopenharmony_ci		.func_id = f,	\
6062306a36Sopenharmony_ci		.arg1 = a1,	\
6162306a36Sopenharmony_ci	}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct test_hvc_info hvc_info[] = {
6462306a36Sopenharmony_ci	/* KVM_REG_ARM_STD_BMAP */
6562306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_VERSION, 0),
6662306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_TRNG_RND64),
6762306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_GET_UUID, 0),
6862306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND32, 0),
6962306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_RND64, 0),
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* KVM_REG_ARM_STD_HYP_BMAP */
7262306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_HV_PV_TIME_FEATURES),
7362306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_HV_PV_TIME_ST),
7462306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_ST, 0),
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* KVM_REG_ARM_VENDOR_HYP_BMAP */
7762306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID,
7862306a36Sopenharmony_ci			ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
7962306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, 0),
8062306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID, KVM_PTP_VIRT_COUNTER),
8162306a36Sopenharmony_ci};
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/* Feed false hypercall info to test the KVM behavior */
8462306a36Sopenharmony_cistatic const struct test_hvc_info false_hvc_info[] = {
8562306a36Sopenharmony_ci	/* Feature support check against a different family of hypercalls */
8662306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_TRNG_FEATURES, ARM_SMCCC_VENDOR_HYP_KVM_PTP_FUNC_ID),
8762306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_ARCH_FEATURES_FUNC_ID, ARM_SMCCC_TRNG_RND64),
8862306a36Sopenharmony_ci	TEST_HVC_INFO(ARM_SMCCC_HV_PV_TIME_FEATURES, ARM_SMCCC_TRNG_RND64),
8962306a36Sopenharmony_ci};
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void guest_test_hvc(const struct test_hvc_info *hc_info)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	unsigned int i;
9462306a36Sopenharmony_ci	struct arm_smccc_res res;
9562306a36Sopenharmony_ci	unsigned int hvc_info_arr_sz;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	hvc_info_arr_sz =
9862306a36Sopenharmony_ci	hc_info == hvc_info ? ARRAY_SIZE(hvc_info) : ARRAY_SIZE(false_hvc_info);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	for (i = 0; i < hvc_info_arr_sz; i++, hc_info++) {
10162306a36Sopenharmony_ci		memset(&res, 0, sizeof(res));
10262306a36Sopenharmony_ci		smccc_hvc(hc_info->func_id, hc_info->arg1, 0, 0, 0, 0, 0, 0, &res);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci		switch (stage) {
10562306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
10662306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
10762306a36Sopenharmony_ci			__GUEST_ASSERT(res.a0 == SMCCC_RET_NOT_SUPPORTED,
10862306a36Sopenharmony_ci				       "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u",
10962306a36Sopenharmony_ci					res.a0, hc_info->func_id, hc_info->arg1, stage);
11062306a36Sopenharmony_ci			break;
11162306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
11262306a36Sopenharmony_ci			__GUEST_ASSERT(res.a0 != SMCCC_RET_NOT_SUPPORTED,
11362306a36Sopenharmony_ci				       "a0 = 0x%lx, func_id = 0x%x, arg1 = 0x%llx, stage = %u",
11462306a36Sopenharmony_ci					res.a0, hc_info->func_id, hc_info->arg1, stage);
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci		default:
11762306a36Sopenharmony_ci			GUEST_FAIL("Unexpected stage = %u", stage);
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void guest_code(void)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	while (stage != TEST_STAGE_END) {
12562306a36Sopenharmony_ci		switch (stage) {
12662306a36Sopenharmony_ci		case TEST_STAGE_REG_IFACE:
12762306a36Sopenharmony_ci			break;
12862306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
12962306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
13062306a36Sopenharmony_ci			guest_test_hvc(hvc_info);
13162306a36Sopenharmony_ci			break;
13262306a36Sopenharmony_ci		case TEST_STAGE_HVC_IFACE_FALSE_INFO:
13362306a36Sopenharmony_ci			guest_test_hvc(false_hvc_info);
13462306a36Sopenharmony_ci			break;
13562306a36Sopenharmony_ci		default:
13662306a36Sopenharmony_ci			GUEST_FAIL("Unexpected stage = %u", stage);
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci		GUEST_SYNC(stage);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	GUEST_DONE();
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistruct st_time {
14662306a36Sopenharmony_ci	uint32_t rev;
14762306a36Sopenharmony_ci	uint32_t attr;
14862306a36Sopenharmony_ci	uint64_t st_time;
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#define STEAL_TIME_SIZE		((sizeof(struct st_time) + 63) & ~63)
15262306a36Sopenharmony_ci#define ST_GPA_BASE		(1 << 30)
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void steal_time_init(struct kvm_vcpu *vcpu)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	uint64_t st_ipa = (ulong)ST_GPA_BASE;
15762306a36Sopenharmony_ci	unsigned int gpages;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE);
16062306a36Sopenharmony_ci	vm_userspace_mem_region_add(vcpu->vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PVTIME_CTRL,
16362306a36Sopenharmony_ci			     KVM_ARM_VCPU_PVTIME_IPA, &st_ipa);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	uint64_t val;
16962306a36Sopenharmony_ci	unsigned int i;
17062306a36Sopenharmony_ci	int ret;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
17362306a36Sopenharmony_ci		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		/* First 'read' should be an upper limit of the features supported */
17662306a36Sopenharmony_ci		vcpu_get_reg(vcpu, reg_info->reg, &val);
17762306a36Sopenharmony_ci		TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit),
17862306a36Sopenharmony_ci			"Expected all the features to be set for reg: 0x%lx; expected: 0x%lx; read: 0x%lx\n",
17962306a36Sopenharmony_ci			reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), val);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci		/* Test a 'write' by disabling all the features of the register map */
18262306a36Sopenharmony_ci		ret = __vcpu_set_reg(vcpu, reg_info->reg, 0);
18362306a36Sopenharmony_ci		TEST_ASSERT(ret == 0,
18462306a36Sopenharmony_ci			"Failed to clear all the features of reg: 0x%lx; ret: %d\n",
18562306a36Sopenharmony_ci			reg_info->reg, errno);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		vcpu_get_reg(vcpu, reg_info->reg, &val);
18862306a36Sopenharmony_ci		TEST_ASSERT(val == 0,
18962306a36Sopenharmony_ci			"Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		/*
19262306a36Sopenharmony_ci		 * Test enabling a feature that's not supported.
19362306a36Sopenharmony_ci		 * Avoid this check if all the bits are occupied.
19462306a36Sopenharmony_ci		 */
19562306a36Sopenharmony_ci		if (reg_info->max_feat_bit < 63) {
19662306a36Sopenharmony_ci			ret = __vcpu_set_reg(vcpu, reg_info->reg, BIT(reg_info->max_feat_bit + 1));
19762306a36Sopenharmony_ci			TEST_ASSERT(ret != 0 && errno == EINVAL,
19862306a36Sopenharmony_ci			"Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n",
19962306a36Sopenharmony_ci			errno, reg_info->reg);
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void test_fw_regs_after_vm_start(struct kvm_vcpu *vcpu)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	uint64_t val;
20762306a36Sopenharmony_ci	unsigned int i;
20862306a36Sopenharmony_ci	int ret;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(fw_reg_info); i++) {
21162306a36Sopenharmony_ci		const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i];
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		/*
21462306a36Sopenharmony_ci		 * Before starting the VM, the test clears all the bits.
21562306a36Sopenharmony_ci		 * Check if that's still the case.
21662306a36Sopenharmony_ci		 */
21762306a36Sopenharmony_ci		vcpu_get_reg(vcpu, reg_info->reg, &val);
21862306a36Sopenharmony_ci		TEST_ASSERT(val == 0,
21962306a36Sopenharmony_ci			"Expected all the features to be cleared for reg: 0x%lx\n",
22062306a36Sopenharmony_ci			reg_info->reg);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci		/*
22362306a36Sopenharmony_ci		 * Since the VM has run at least once, KVM shouldn't allow modification of
22462306a36Sopenharmony_ci		 * the registers and should return EBUSY. Set the registers and check for
22562306a36Sopenharmony_ci		 * the expected errno.
22662306a36Sopenharmony_ci		 */
22762306a36Sopenharmony_ci		ret = __vcpu_set_reg(vcpu, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit));
22862306a36Sopenharmony_ci		TEST_ASSERT(ret != 0 && errno == EBUSY,
22962306a36Sopenharmony_ci		"Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n",
23062306a36Sopenharmony_ci		errno, reg_info->reg);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci}
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic struct kvm_vm *test_vm_create(struct kvm_vcpu **vcpu)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct kvm_vm *vm;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	vm = vm_create_with_one_vcpu(vcpu, guest_code);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	steal_time_init(*vcpu);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return vm;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void test_guest_stage(struct kvm_vm **vm, struct kvm_vcpu **vcpu)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	int prev_stage = stage;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	pr_debug("Stage: %d\n", prev_stage);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Sync the stage early, the VM might be freed below. */
25262306a36Sopenharmony_ci	stage++;
25362306a36Sopenharmony_ci	sync_global_to_guest(*vm, stage);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	switch (prev_stage) {
25662306a36Sopenharmony_ci	case TEST_STAGE_REG_IFACE:
25762306a36Sopenharmony_ci		test_fw_regs_after_vm_start(*vcpu);
25862306a36Sopenharmony_ci		break;
25962306a36Sopenharmony_ci	case TEST_STAGE_HVC_IFACE_FEAT_DISABLED:
26062306a36Sopenharmony_ci		/* Start a new VM so that all the features are now enabled by default */
26162306a36Sopenharmony_ci		kvm_vm_free(*vm);
26262306a36Sopenharmony_ci		*vm = test_vm_create(vcpu);
26362306a36Sopenharmony_ci		break;
26462306a36Sopenharmony_ci	case TEST_STAGE_HVC_IFACE_FEAT_ENABLED:
26562306a36Sopenharmony_ci	case TEST_STAGE_HVC_IFACE_FALSE_INFO:
26662306a36Sopenharmony_ci		break;
26762306a36Sopenharmony_ci	default:
26862306a36Sopenharmony_ci		TEST_FAIL("Unknown test stage: %d\n", prev_stage);
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void test_run(void)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct kvm_vcpu *vcpu;
27562306a36Sopenharmony_ci	struct kvm_vm *vm;
27662306a36Sopenharmony_ci	struct ucall uc;
27762306a36Sopenharmony_ci	bool guest_done = false;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	vm = test_vm_create(&vcpu);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	test_fw_regs_before_vm_start(vcpu);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	while (!guest_done) {
28462306a36Sopenharmony_ci		vcpu_run(vcpu);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci		switch (get_ucall(vcpu, &uc)) {
28762306a36Sopenharmony_ci		case UCALL_SYNC:
28862306a36Sopenharmony_ci			test_guest_stage(&vm, &vcpu);
28962306a36Sopenharmony_ci			break;
29062306a36Sopenharmony_ci		case UCALL_DONE:
29162306a36Sopenharmony_ci			guest_done = true;
29262306a36Sopenharmony_ci			break;
29362306a36Sopenharmony_ci		case UCALL_ABORT:
29462306a36Sopenharmony_ci			REPORT_GUEST_ASSERT(uc);
29562306a36Sopenharmony_ci			break;
29662306a36Sopenharmony_ci		default:
29762306a36Sopenharmony_ci			TEST_FAIL("Unexpected guest exit\n");
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	kvm_vm_free(vm);
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ciint main(void)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	test_run();
30762306a36Sopenharmony_ci	return 0;
30862306a36Sopenharmony_ci}
309