@@ -353,10 +353,7 @@ int kvm_smccc_call_handler(struct kvm_vcpu *vcpu)
val[0] = gpa;
break;
case ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID:
- val[0] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0;
- val[1] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1;
- val[2] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2;
- val[3] = ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3;
+ UUID_TO_SMCCC_RES(ARM_SMCCC_VENDOR_HYP_UID_KVM, val);
break;
case ARM_SMCCC_VENDOR_HYP_KVM_FEATURES_FUNC_ID:
val[0] = smccc_feat->vendor_hyp_bmap;
@@ -14,17 +14,11 @@ static DECLARE_BITMAP(__kvm_arm_hyp_services, ARM_SMCCC_KVM_NUM_FUNCS) __ro_afte
void __init kvm_init_hyp_services(void)
{
+ uuid_t kvm_uuid = ARM_SMCCC_VENDOR_HYP_UID_KVM;
struct arm_smccc_res res;
u32 val[4];
- if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_HVC)
- return;
-
- arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res);
- if (res.a0 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 ||
- res.a1 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 ||
- res.a2 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 ||
- res.a3 != ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3)
+ if (!arm_smccc_hyp_present(&kvm_uuid))
return;
memset(&res, 0, sizeof(res));
@@ -67,6 +67,25 @@ s32 arm_smccc_get_soc_id_revision(void)
}
EXPORT_SYMBOL_GPL(arm_smccc_get_soc_id_revision);
+bool arm_smccc_hyp_present(const uuid_t *hyp_uuid)
+{
+ struct arm_smccc_res res = {};
+
+ if (arm_smccc_1_1_get_conduit() != SMCCC_CONDUIT_HVC)
+ return false;
+ arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res);
+ if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
+ return false;
+
+ return ({
+ const uuid_t uuid = SMCCC_RES_TO_UUID(res.a0, res.a1, res.a2, res.a3);
+ const bool present = uuid_equal(&uuid, hyp_uuid);
+
+ present;
+ });
+}
+EXPORT_SYMBOL_GPL(arm_smccc_hyp_present);
+
static int __init smccc_devices_init(void)
{
struct platform_device *pdev;
@@ -7,6 +7,11 @@
#include <linux/args.h>
#include <linux/init.h>
+
+#ifndef __ASSEMBLER__
+#include <linux/uuid.h>
+#endif
+
#include <uapi/linux/const.h>
/*
@@ -107,10 +112,10 @@
ARM_SMCCC_FUNC_QUERY_CALL_UID)
/* KVM UID value: 28b46fb6-2ec5-11e9-a9ca-4b564d003a74 */
-#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_0 0xb66fb428U
-#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_1 0xe911c52eU
-#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_2 0x564bcaa9U
-#define ARM_SMCCC_VENDOR_HYP_UID_KVM_REG_3 0x743a004dU
+#define ARM_SMCCC_VENDOR_HYP_UID_KVM UUID_INIT(\
+ 0xb66fb428, 0xc52e, 0xe911, \
+ 0xa9, 0xca, 0x4b, 0x56, \
+ 0x4d, 0x00, 0x3a, 0x74)
/* KVM "vendor specific" services */
#define ARM_SMCCC_KVM_FUNC_FEATURES 0
@@ -333,6 +338,48 @@ s32 arm_smccc_get_soc_id_version(void);
*/
s32 arm_smccc_get_soc_id_revision(void);
+#ifndef __ASSEMBLER__
+
+/**
+ * arm_smccc_hyp_present(const uuid_t *hyp_uuid)
+ *
+ * Returns `true` if the hypervisor advertises its presence via SMCCC.
+ *
+ * When the function returns `false`, the caller shall not assume that
+ * there is no hypervisor running. Instead, the caller must fall back to
+ * other approaches if any are available.
+ */
+bool arm_smccc_hyp_present(const uuid_t *hyp_uuid);
+
+#define SMCCC_RES_TO_UUID(r0, r1, r2, r3) \
+ UUID_INIT( \
+ cpu_to_le32(lower_32_bits(r0)), \
+ cpu_to_le32(lower_32_bits(r1)) & 0xffff, \
+ cpu_to_le32(lower_32_bits(r1)) >> 16, \
+ cpu_to_le32(lower_32_bits(r2)) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r2)) >> 8) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r2)) >> 16) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r2)) >> 24) & 0xff, \
+ cpu_to_le32(lower_32_bits(r3)) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r3)) >> 8) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r3)) >> 16) & 0xff, \
+ (cpu_to_le32(lower_32_bits(r3)) >> 24) & 0xff \
+ )
+
+#define UUID_TO_SMCCC_RES(uuid_init, regs) do { \
+ const uuid_t uuid = uuid_init; \
+ (regs)[0] = le32_to_cpu((u32)uuid.b[0] | (uuid.b[1] << 8) | \
+ ((uuid.b[2]) << 16) | ((uuid.b[3]) << 24)); \
+ (regs)[1] = le32_to_cpu((u32)uuid.b[4] | (uuid.b[5] << 8) | \
+ ((uuid.b[6]) << 16) | ((uuid.b[7]) << 24)); \
+ (regs)[2] = le32_to_cpu((u32)uuid.b[8] | (uuid.b[9] << 8) | \
+ ((uuid.b[10]) << 16) | ((uuid.b[11]) << 24)); \
+ (regs)[3] = le32_to_cpu((u32)uuid.b[12] | (uuid.b[13] << 8) | \
+ ((uuid.b[14]) << 16) | ((uuid.b[15]) << 24)); \
+ } while (0)
+
+#endif /* !__ASSEMBLER__ */
+
/**
* struct arm_smccc_res - Result from SMC/HVC call
* @a0-a3 result values from registers 0 to 3
The KVM/arm64 uses SMCCC to detect hypervisor presence. That code is private, and it follows the SMCCC specification. Other existing and emerging hypervisor guest implementations can and should use that standard approach as well. Factor out a common infrastructure that the guests can use, update KVM to employ the new API. The cenrtal notion of the SMCCC method is the UUID of the hypervisor, and the API follows that. No functional changes. Validated with a KVM/arm64 guest. Signed-off-by: Roman Kisel <romank@linux.microsoft.com> --- arch/arm64/kvm/hypercalls.c | 5 +-- drivers/firmware/smccc/kvm_guest.c | 10 ++---- drivers/firmware/smccc/smccc.c | 19 +++++++++++ include/linux/arm-smccc.h | 55 +++++++++++++++++++++++++++--- 4 files changed, 73 insertions(+), 16 deletions(-)