// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include #include /* {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");