162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci// Copyright (C) 2020 Arm Ltd.
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/arm-smccc.h>
562306a36Sopenharmony_ci#include <linux/kvm_host.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <asm/kvm_emulate.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <kvm/arm_hypercalls.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define ARM_SMCCC_TRNG_VERSION_1_0	0x10000UL
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/* Those values are deliberately separate from the generic SMCCC definitions. */
1462306a36Sopenharmony_ci#define TRNG_SUCCESS			0UL
1562306a36Sopenharmony_ci#define TRNG_NOT_SUPPORTED		((unsigned long)-1)
1662306a36Sopenharmony_ci#define TRNG_INVALID_PARAMETER		((unsigned long)-2)
1762306a36Sopenharmony_ci#define TRNG_NO_ENTROPY			((unsigned long)-3)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define TRNG_MAX_BITS64			192
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const uuid_t arm_smc_trng_uuid __aligned(4) = UUID_INIT(
2262306a36Sopenharmony_ci	0x0d21e000, 0x4384, 0x11eb, 0x80, 0x70, 0x52, 0x44, 0x55, 0x4e, 0x5a, 0x4c);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int kvm_trng_do_rnd(struct kvm_vcpu *vcpu, int size)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	DECLARE_BITMAP(bits, TRNG_MAX_BITS64);
2762306a36Sopenharmony_ci	u32 num_bits = smccc_get_arg1(vcpu);
2862306a36Sopenharmony_ci	int i;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (num_bits > 3 * size) {
3162306a36Sopenharmony_ci		smccc_set_retval(vcpu, TRNG_INVALID_PARAMETER, 0, 0, 0);
3262306a36Sopenharmony_ci		return 1;
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* get as many bits as we need to fulfil the request */
3662306a36Sopenharmony_ci	for (i = 0; i < DIV_ROUND_UP(num_bits, BITS_PER_LONG); i++)
3762306a36Sopenharmony_ci		bits[i] = get_random_long();
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	bitmap_clear(bits, num_bits, TRNG_MAX_BITS64 - num_bits);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (size == 32)
4262306a36Sopenharmony_ci		smccc_set_retval(vcpu, TRNG_SUCCESS, lower_32_bits(bits[1]),
4362306a36Sopenharmony_ci				 upper_32_bits(bits[0]), lower_32_bits(bits[0]));
4462306a36Sopenharmony_ci	else
4562306a36Sopenharmony_ci		smccc_set_retval(vcpu, TRNG_SUCCESS, bits[2], bits[1], bits[0]);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	memzero_explicit(bits, sizeof(bits));
4862306a36Sopenharmony_ci	return 1;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint kvm_trng_call(struct kvm_vcpu *vcpu)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	const __le32 *u = (__le32 *)arm_smc_trng_uuid.b;
5462306a36Sopenharmony_ci	u32 func_id = smccc_get_function(vcpu);
5562306a36Sopenharmony_ci	unsigned long val = TRNG_NOT_SUPPORTED;
5662306a36Sopenharmony_ci	int size = 64;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	switch (func_id) {
5962306a36Sopenharmony_ci	case ARM_SMCCC_TRNG_VERSION:
6062306a36Sopenharmony_ci		val = ARM_SMCCC_TRNG_VERSION_1_0;
6162306a36Sopenharmony_ci		break;
6262306a36Sopenharmony_ci	case ARM_SMCCC_TRNG_FEATURES:
6362306a36Sopenharmony_ci		switch (smccc_get_arg1(vcpu)) {
6462306a36Sopenharmony_ci		case ARM_SMCCC_TRNG_VERSION:
6562306a36Sopenharmony_ci		case ARM_SMCCC_TRNG_FEATURES:
6662306a36Sopenharmony_ci		case ARM_SMCCC_TRNG_GET_UUID:
6762306a36Sopenharmony_ci		case ARM_SMCCC_TRNG_RND32:
6862306a36Sopenharmony_ci		case ARM_SMCCC_TRNG_RND64:
6962306a36Sopenharmony_ci			val = TRNG_SUCCESS;
7062306a36Sopenharmony_ci		}
7162306a36Sopenharmony_ci		break;
7262306a36Sopenharmony_ci	case ARM_SMCCC_TRNG_GET_UUID:
7362306a36Sopenharmony_ci		smccc_set_retval(vcpu, le32_to_cpu(u[0]), le32_to_cpu(u[1]),
7462306a36Sopenharmony_ci				 le32_to_cpu(u[2]), le32_to_cpu(u[3]));
7562306a36Sopenharmony_ci		return 1;
7662306a36Sopenharmony_ci	case ARM_SMCCC_TRNG_RND32:
7762306a36Sopenharmony_ci		size = 32;
7862306a36Sopenharmony_ci		fallthrough;
7962306a36Sopenharmony_ci	case ARM_SMCCC_TRNG_RND64:
8062306a36Sopenharmony_ci		return kvm_trng_do_rnd(vcpu, size);
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	smccc_set_retval(vcpu, val, 0, 0, 0);
8462306a36Sopenharmony_ci	return 1;
8562306a36Sopenharmony_ci}
86