blob: e178b5a0f691752ffde8f3cb619f000e0b26ca06 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/arm-smccc.h>
#include <linux/module.h>
#include <linux/gunyah.h>
#include <linux/uuid.h>
/* {c1d58fcd-a453-5fdb-9265-ce36673d5f14} */
static const uuid_t GUNYAH_UUID =
UUID_INIT(0xc1d58fcd, 0xa453, 0x5fdb, 0x92, 0x65, 0xce, 0x36, 0x67, 0x3d, 0x5f, 0x14);
bool arch_is_gh_guest(void)
{
struct arm_smccc_res res;
uuid_t uuid;
u32 *up;
arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res);
up = (u32 *)&uuid.b[0];
up[0] = lower_32_bits(res.a0);
up[1] = lower_32_bits(res.a1);
up[2] = lower_32_bits(res.a2);
up[3] = lower_32_bits(res.a3);
return uuid_equal(&uuid, &GUNYAH_UUID);
}
EXPORT_SYMBOL_GPL(arch_is_gh_guest);
#define GH_HYPERCALL(fn) ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
ARM_SMCCC_OWNER_VENDOR_HYP, \
fn)
#define GH_HYPERCALL_HYP_IDENTIFY GH_HYPERCALL(0x8000)
#define GH_HYPERCALL_BELL_SEND GH_HYPERCALL(0x8012)
#define GH_HYPERCALL_BELL_SET_MASK GH_HYPERCALL(0x8015)
#define GH_HYPERCALL_MSGQ_SEND GH_HYPERCALL(0x801B)
#define GH_HYPERCALL_MSGQ_RECV GH_HYPERCALL(0x801C)
#define GH_HYPERCALL_VCPU_RUN GH_HYPERCALL(0x8065)
/**
* gh_hypercall_hyp_identify() - Returns build information and feature flags
* supported by Gunyah.
* @hyp_identity: filled by the hypercall with the API info and feature flags.
*/
void gh_hypercall_hyp_identify(struct gh_hypercall_hyp_identify_resp *hyp_identity)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GH_HYPERCALL_HYP_IDENTIFY, &res);
hyp_identity->api_info = res.a0;
hyp_identity->flags[0] = res.a1;
hyp_identity->flags[1] = res.a2;
hyp_identity->flags[2] = res.a3;
}
EXPORT_SYMBOL_GPL(gh_hypercall_hyp_identify);
enum gh_error gh_hypercall_bell_send(u64 capid, u64 new_flags, u64 *old_flags)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GH_HYPERCALL_BELL_SEND, capid, new_flags, 0, &res);
if (res.a0 == GH_ERROR_OK && old_flags)
*old_flags = res.a1;
return res.a0;
}
EXPORT_SYMBOL_GPL(gh_hypercall_bell_send);
enum gh_error gh_hypercall_bell_set_mask(u64 capid, u64 enable_mask, u64 ack_mask)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GH_HYPERCALL_BELL_SET_MASK, capid, enable_mask, ack_mask, 0, &res);
return res.a0;
}
EXPORT_SYMBOL_GPL(gh_hypercall_bell_set_mask);
enum gh_error gh_hypercall_msgq_send(u64 capid, size_t size, void *buff, u64 tx_flags, bool *ready)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GH_HYPERCALL_MSGQ_SEND, capid, size, (uintptr_t)buff, tx_flags, 0, &res);
if (res.a0 == GH_ERROR_OK)
*ready = !!res.a1;
return res.a0;
}
EXPORT_SYMBOL_GPL(gh_hypercall_msgq_send);
enum gh_error gh_hypercall_msgq_recv(u64 capid, void *buff, size_t size, size_t *recv_size,
bool *ready)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GH_HYPERCALL_MSGQ_RECV, capid, (uintptr_t)buff, size, 0, &res);
if (res.a0 == GH_ERROR_OK) {
*recv_size = res.a1;
*ready = !!res.a2;
}
return res.a0;
}
EXPORT_SYMBOL_GPL(gh_hypercall_msgq_recv);
enum gh_error gh_hypercall_vcpu_run(u64 capid, u64 *resume_data,
struct gh_hypercall_vcpu_run_resp *resp)
{
struct arm_smccc_1_2_regs args = {
.a0 = GH_HYPERCALL_VCPU_RUN,
.a1 = capid,
.a2 = resume_data[0],
.a3 = resume_data[1],
.a4 = resume_data[2],
/* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
.a5 = 0,
};
struct arm_smccc_1_2_regs res;
arm_smccc_1_2_hvc(&args, &res);
if (res.a0 == GH_ERROR_OK) {
resp->sized_state = res.a1;
resp->state_data[0] = res.a2;
resp->state_data[1] = res.a3;
resp->state_data[2] = res.a4;
}
return res.a0;
}
EXPORT_SYMBOL_GPL(gh_hypercall_vcpu_run);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gunyah Hypervisor Hypercalls");