Files
android_kernel_samsung_sm8750/arch/arm64/gunyah/gunyah_hypercall.c
2025-08-12 22:16:57 +02:00

280 lines
8.7 KiB
C
Executable File

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022-2024 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_gunyah_guest(void)
{
struct arm_smccc_res res;
uuid_t uuid;
u32 *up;
arm_smccc_1_1_invoke(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_gunyah_guest);
#define GUNYAH_HYPERCALL(fn) \
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
ARM_SMCCC_OWNER_VENDOR_HYP, fn)
/* clang-format off */
#define GUNYAH_HYPERCALL_HYP_IDENTIFY GUNYAH_HYPERCALL(0x8000)
#define GUNYAH_HYPERCALL_BELL_SEND GUNYAH_HYPERCALL(0x8012)
#define GUNYAH_HYPERCALL_BELL_SET_MASK GUNYAH_HYPERCALL(0x8015)
#define GUNYAH_HYPERCALL_MSGQ_SEND GUNYAH_HYPERCALL(0x801B)
#define GUNYAH_HYPERCALL_MSGQ_RECV GUNYAH_HYPERCALL(0x801C)
#define GUNYAH_HYPERCALL_ADDRSPACE_MAP GUNYAH_HYPERCALL(0x802B)
#define GUNYAH_HYPERCALL_ADDRSPACE_UNMAP GUNYAH_HYPERCALL(0x802C)
#define GUNYAH_HYPERCALL_MEMEXTENT_DONATE GUNYAH_HYPERCALL(0x8061)
#define GUNYAH_HYPERCALL_VCPU_RUN GUNYAH_HYPERCALL(0x8065)
/* clang-format on */
/**
* gunyah_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 gunyah_hypercall_hyp_identify(
struct gunyah_hypercall_hyp_identify_resp *hyp_identity)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GUNYAH_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(gunyah_hypercall_hyp_identify);
/**
* gunyah_hypercall_bell_send() - Assert a gunyah doorbell
* @capid: capability ID of the doorbell
* @new_flags: bits to set on the doorbell
* @old_flags: Filled with the bits set before the send call if return value is GUNYAH_ERROR_OK
*/
enum gunyah_error gunyah_hypercall_bell_send(u64 capid, u64 new_flags, u64 *old_flags)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_BELL_SEND, capid, new_flags, 0, &res);
if (res.a0 == GUNYAH_ERROR_OK && old_flags)
*old_flags = res.a1;
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_bell_send);
/**
* gunyah_hypercall_bell_set_mask() - Set masks on a Gunyah doorbell
* @capid: capability ID of the doorbell
* @enable_mask: which bits trigger the receiver interrupt
* @ack_mask: which bits are automatically acknowledged when the receiver
* interrupt is ack'd
*/
enum gunyah_error gunyah_hypercall_bell_set_mask(u64 capid, u64 enable_mask, u64 ack_mask)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_BELL_SET_MASK, capid, enable_mask, ack_mask, 0, &res);
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_bell_set_mask);
/**
* gunyah_hypercall_msgq_send() - Send a buffer on a message queue
* @capid: capability ID of the message queue to add message
* @size: Size of @buff
* @buff: Address of buffer to send
* @tx_flags: See GUNYAH_HYPERCALL_MSGQ_TX_FLAGS_*
* @ready: If the send was successful, ready is filled with true if more
* messages can be sent on the queue. If false, then the tx IRQ will
* be raised in future when send can succeed.
*/
enum gunyah_error gunyah_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(GUNYAH_HYPERCALL_MSGQ_SEND, capid, size,
(uintptr_t)buff, tx_flags, 0, &res);
if (res.a0 == GUNYAH_ERROR_OK)
*ready = !!res.a1;
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_msgq_send);
/**
* gunyah_hypercall_msgq_recv() - Send a buffer on a message queue
* @capid: capability ID of the message queue to add message
* @buff: Address of buffer to copy received data into
* @size: Size of @buff
* @recv_size: If the receive was successful, recv_size is filled with the
* size of data received. Will be <= size.
* @ready: If the receive was successful, ready is filled with true if more
* messages are ready to be received on the queue. If false, then the
* rx IRQ will be raised in future when recv can succeed.
*/
enum gunyah_error gunyah_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(GUNYAH_HYPERCALL_MSGQ_RECV, capid, (uintptr_t)buff,
size, 0, &res);
if (res.a0 == GUNYAH_ERROR_OK) {
*recv_size = res.a1;
*ready = !!res.a2;
}
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_msgq_recv);
/**
* gunyah_hypercall_addrspace_map() - Add memory to an address space from a memory extent
* @capid: Address space capability ID
* @extent_capid: Memory extent capability ID
* @vbase: location in address space
* @extent_attrs: Attributes for the memory
* @flags: Flags for address space mapping
* @offset: Offset into memory extent (physical address of memory)
* @size: Size of memory to map; must be page-aligned
*/
enum gunyah_error gunyah_hypercall_addrspace_map(u64 capid, u64 extent_capid, u64 vbase,
u32 extent_attrs, u32 flags, u64 offset, u64 size)
{
struct arm_smccc_1_2_regs args = {
.a0 = GUNYAH_HYPERCALL_ADDRSPACE_MAP,
.a1 = capid,
.a2 = extent_capid,
.a3 = vbase,
.a4 = extent_attrs,
.a5 = flags,
.a6 = offset,
.a7 = size,
/* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
.a8 = 0,
};
struct arm_smccc_1_2_regs res;
arm_smccc_1_2_hvc(&args, &res);
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspace_map);
/**
* gunyah_hypercall_addrspace_unmap() - Remove memory from an address space
* @capid: Address space capability ID
* @extent_capid: Memory extent capability ID
* @vbase: location in address space
* @flags: Flags for address space mapping
* @offset: Offset into memory extent (physical address of memory)
* @size: Size of memory to map; must be page-aligned
*/
enum gunyah_error gunyah_hypercall_addrspace_unmap(u64 capid, u64 extent_capid, u64 vbase,
u32 flags, u64 offset, u64 size)
{
struct arm_smccc_1_2_regs args = {
.a0 = GUNYAH_HYPERCALL_ADDRSPACE_UNMAP,
.a1 = capid,
.a2 = extent_capid,
.a3 = vbase,
.a4 = flags,
.a5 = offset,
.a6 = size,
/* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
.a7 = 0,
};
struct arm_smccc_1_2_regs res;
arm_smccc_1_2_hvc(&args, &res);
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspace_unmap);
/**
* gunyah_hypercall_memextent_donate() - Donate memory from one memory extent to another
* @options: donate options
* @from_capid: Memory extent capability ID to donate from
* @to_capid: Memory extent capability ID to donate to
* @offset: Offset into memory extent (physical address of memory)
* @size: Size of memory to donate; must be page-aligned
*/
enum gunyah_error gunyah_hypercall_memextent_donate(u32 options, u64 from_capid, u64 to_capid,
u64 offset, u64 size)
{
struct arm_smccc_res res;
arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_MEMEXTENT_DONATE, options, from_capid, to_capid,
offset, size, 0, &res);
return res.a0;
}
EXPORT_SYMBOL_GPL(gunyah_hypercall_memextent_donate);
/**
* gunyah_hypercall_vcpu_run() - Donate CPU time to a vcpu
* @capid: capability ID of the vCPU to run
* @resume_data: Array of 3 state-specific resume data
* @resp: Filled reason why vCPU exited when return value is GUNYAH_ERROR_OK
*
* See also:
* https://github.com/quic/gunyah-hypervisor/blob/develop/docs/api/gunyah_api.md#run-a-proxy-scheduled-vcpu-thread
*/
enum gunyah_error
gunyah_hypercall_vcpu_run(u64 capid, unsigned long *resume_data,
struct gunyah_hypercall_vcpu_run_resp *resp)
{
struct arm_smccc_1_2_regs args = {
.a0 = GUNYAH_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 == GUNYAH_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(gunyah_hypercall_vcpu_run);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Gunyah Hypervisor Hypercalls");