1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4 */
5
6 #include <linux/arm-smccc.h>
7 #include <linux/module.h>
8 #include <linux/gunyah.h>
9 #include <linux/uuid.h>
10
11 #define GUNYAH_VCPU_RUN_STATE_PSCI_SYSTEM_RESET 256
12 /* {c1d58fcd-a453-5fdb-9265-ce36673d5f14} */
13 static const uuid_t GUNYAH_UUID = UUID_INIT(0xc1d58fcd, 0xa453, 0x5fdb, 0x92,
14 0x65, 0xce, 0x36, 0x67, 0x3d, 0x5f,
15 0x14);
16
arch_is_gunyah_guest(void)17 bool arch_is_gunyah_guest(void)
18 {
19 struct arm_smccc_res res;
20 uuid_t uuid;
21 u32 *up;
22
23 arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_CALL_UID_FUNC_ID, &res);
24
25 up = (u32 *)&uuid.b[0];
26 up[0] = lower_32_bits(res.a0);
27 up[1] = lower_32_bits(res.a1);
28 up[2] = lower_32_bits(res.a2);
29 up[3] = lower_32_bits(res.a3);
30
31 return uuid_equal(&uuid, &GUNYAH_UUID);
32 }
33 EXPORT_SYMBOL_GPL(arch_is_gunyah_guest);
34
35 #define GUNYAH_HYPERCALL(fn) \
36 ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64, \
37 ARM_SMCCC_OWNER_VENDOR_HYP, fn)
38
39 /* clang-format off */
40 #define GUNYAH_HYPERCALL_HYP_IDENTIFY GUNYAH_HYPERCALL(0x8000)
41 #define GUNYAH_HYPERCALL_BELL_SEND GUNYAH_HYPERCALL(0x8012)
42 #define GUNYAH_HYPERCALL_BELL_SET_MASK GUNYAH_HYPERCALL(0x8015)
43 #define GUNYAH_HYPERCALL_MSGQ_SEND GUNYAH_HYPERCALL(0x801B)
44 #define GUNYAH_HYPERCALL_MSGQ_RECV GUNYAH_HYPERCALL(0x801C)
45 #define GUNYAH_HYPERCALL_ADDRSPACE_MAP GUNYAH_HYPERCALL(0x802B)
46 #define GUNYAH_HYPERCALL_ADDRSPACE_UNMAP GUNYAH_HYPERCALL(0x802C)
47 #define GUNYAH_HYPERCALL_ADDRSPACE_CONFIG_VMMIO_RANGE GUNYAH_HYPERCALL(0x8060)
48 #define GUNYAH_HYPERCALL_MEMEXTENT_DONATE GUNYAH_HYPERCALL(0x8061)
49 #define GUNYAH_HYPERCALL_VCPU_RUN GUNYAH_HYPERCALL(0x8065)
50 #define GUNYAH_HYPERCALL_ADDRSPC_MODIFY_PAGES GUNYAH_HYPERCALL(0x8069)
51 #define GUNYAH_HYPERCALL_ADDRSPACE_FIND_INFO_AREA GUNYAH_HYPERCALL(0x806a)
52 /* clang-format on */
53
54 /**
55 * gunyah_hypercall_hyp_identify() - Returns build information and feature flags
56 * supported by Gunyah.
57 * @hyp_identity: filled by the hypercall with the API info and feature flags.
58 */
gunyah_hypercall_hyp_identify(struct gunyah_hypercall_hyp_identify_resp * hyp_identity)59 void gunyah_hypercall_hyp_identify(
60 struct gunyah_hypercall_hyp_identify_resp *hyp_identity)
61 {
62 struct arm_smccc_res res;
63
64 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_HYP_IDENTIFY, &res);
65
66 hyp_identity->api_info = res.a0;
67 hyp_identity->flags[0] = res.a1;
68 hyp_identity->flags[1] = res.a2;
69 hyp_identity->flags[2] = res.a3;
70 }
71 EXPORT_SYMBOL_GPL(gunyah_hypercall_hyp_identify);
72
gunyah_hypercall_addrspc_modify_pages(u64 capid,u64 addr,u64 size,u64 flags)73 enum gunyah_error gunyah_hypercall_addrspc_modify_pages(u64 capid, u64 addr,
74 u64 size, u64 flags)
75 {
76 struct arm_smccc_res res;
77
78 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_ADDRSPC_MODIFY_PAGES, capid,
79 addr, size, flags, &res);
80
81 return res.a0;
82 }
83 EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspc_modify_pages);
84
85 /**
86 * gunyah_hypercall_addrspc_configure_vmmio_range() - Configure virtual MMIO device regions for
87 * the address space.
88 * @capid: Address space capability ID
89 * @base: Base guest address of MMIO region
90 * @size: Size of the MMIO region
91 * @op: Map or Unmap
92 */
gunyah_hypercall_addrspc_configure_vmmio_range(u64 capid,u64 base,u64 size,u64 op)93 enum gunyah_error gunyah_hypercall_addrspc_configure_vmmio_range(u64 capid, u64 base,
94 u64 size, u64 op)
95 {
96 struct arm_smccc_1_2_regs args = {
97 .a0 = GUNYAH_HYPERCALL_ADDRSPACE_CONFIG_VMMIO_RANGE,
98 .a1 = capid,
99 .a2 = base,
100 .a3 = size,
101 .a4 = op,
102 /* Reserved. Must be 0 */
103 .a5 = 0,
104 };
105 struct arm_smccc_1_2_regs res;
106
107 arm_smccc_1_2_hvc(&args, &res);
108
109 return res.a0;
110 }
111 EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspc_configure_vmmio_range);
112
113 /**
114 * gunyah_hypercall_bell_send() - Assert a gunyah doorbell
115 * @capid: capability ID of the doorbell
116 * @new_flags: bits to set on the doorbell
117 * @old_flags: Filled with the bits set before the send call if return value is GUNYAH_ERROR_OK
118 */
gunyah_hypercall_bell_send(u64 capid,u64 new_flags,u64 * old_flags)119 enum gunyah_error gunyah_hypercall_bell_send(u64 capid, u64 new_flags, u64 *old_flags)
120 {
121 struct arm_smccc_res res;
122
123 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_BELL_SEND, capid, new_flags, 0, &res);
124
125 if (res.a0 == GUNYAH_ERROR_OK && old_flags)
126 *old_flags = res.a1;
127
128 return res.a0;
129 }
130 EXPORT_SYMBOL_GPL(gunyah_hypercall_bell_send);
131
132 /**
133 * gunyah_hypercall_bell_set_mask() - Set masks on a Gunyah doorbell
134 * @capid: capability ID of the doorbell
135 * @enable_mask: which bits trigger the receiver interrupt
136 * @ack_mask: which bits are automatically acknowledged when the receiver
137 * interrupt is ack'd
138 */
gunyah_hypercall_bell_set_mask(u64 capid,u64 enable_mask,u64 ack_mask)139 enum gunyah_error gunyah_hypercall_bell_set_mask(u64 capid, u64 enable_mask, u64 ack_mask)
140 {
141 struct arm_smccc_res res;
142
143 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_BELL_SET_MASK, capid, enable_mask, ack_mask, 0, &res);
144
145 return res.a0;
146 }
147 EXPORT_SYMBOL_GPL(gunyah_hypercall_bell_set_mask);
148
149 /**
150 * gunyah_hypercall_msgq_send() - Send a buffer on a message queue
151 * @capid: capability ID of the message queue to add message
152 * @size: Size of @buff
153 * @buff: Address of buffer to send
154 * @tx_flags: See GUNYAH_HYPERCALL_MSGQ_TX_FLAGS_*
155 * @ready: If the send was successful, ready is filled with true if more
156 * messages can be sent on the queue. If false, then the tx IRQ will
157 * be raised in future when send can succeed.
158 */
gunyah_hypercall_msgq_send(u64 capid,size_t size,void * buff,u64 tx_flags,bool * ready)159 enum gunyah_error gunyah_hypercall_msgq_send(u64 capid, size_t size, void *buff,
160 u64 tx_flags, bool *ready)
161 {
162 struct arm_smccc_res res;
163
164 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_MSGQ_SEND, capid, size,
165 (uintptr_t)buff, tx_flags, 0, &res);
166
167 if (res.a0 == GUNYAH_ERROR_OK)
168 *ready = !!res.a1;
169
170 return res.a0;
171 }
172 EXPORT_SYMBOL_GPL(gunyah_hypercall_msgq_send);
173
174 /**
175 * gunyah_hypercall_msgq_recv() - Send a buffer on a message queue
176 * @capid: capability ID of the message queue to add message
177 * @buff: Address of buffer to copy received data into
178 * @size: Size of @buff
179 * @recv_size: If the receive was successful, recv_size is filled with the
180 * size of data received. Will be <= size.
181 * @ready: If the receive was successful, ready is filled with true if more
182 * messages are ready to be received on the queue. If false, then the
183 * rx IRQ will be raised in future when recv can succeed.
184 */
gunyah_hypercall_msgq_recv(u64 capid,void * buff,size_t size,size_t * recv_size,bool * ready)185 enum gunyah_error gunyah_hypercall_msgq_recv(u64 capid, void *buff, size_t size,
186 size_t *recv_size, bool *ready)
187 {
188 struct arm_smccc_res res;
189
190 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_MSGQ_RECV, capid, (uintptr_t)buff,
191 size, 0, &res);
192
193 if (res.a0 == GUNYAH_ERROR_OK) {
194 *recv_size = res.a1;
195 *ready = !!res.a2;
196 }
197
198 return res.a0;
199 }
200 EXPORT_SYMBOL_GPL(gunyah_hypercall_msgq_recv);
201
202 /**
203 * gunyah_hypercall_addrspace_map() - Add memory to an address space from a memory extent
204 * @capid: Address space capability ID
205 * @extent_capid: Memory extent capability ID
206 * @vbase: location in address space
207 * @extent_attrs: Attributes for the memory
208 * @flags: Flags for address space mapping
209 * @offset: Offset into memory extent (physical address of memory)
210 * @size: Size of memory to map; must be page-aligned
211 */
gunyah_hypercall_addrspace_map(u64 capid,u64 extent_capid,u64 vbase,u32 extent_attrs,u32 flags,u64 offset,u64 size)212 enum gunyah_error gunyah_hypercall_addrspace_map(u64 capid, u64 extent_capid, u64 vbase,
213 u32 extent_attrs, u32 flags, u64 offset, u64 size)
214 {
215 struct arm_smccc_1_2_regs args = {
216 .a0 = GUNYAH_HYPERCALL_ADDRSPACE_MAP,
217 .a1 = capid,
218 .a2 = extent_capid,
219 .a3 = vbase,
220 .a4 = extent_attrs,
221 .a5 = flags,
222 .a6 = offset,
223 .a7 = size,
224 /* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
225 .a8 = 0,
226 };
227 struct arm_smccc_1_2_regs res;
228
229 arm_smccc_1_2_hvc(&args, &res);
230
231 return res.a0;
232 }
233 EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspace_map);
234
235 /**
236 * gunyah_hypercall_addrspace_unmap() - Remove memory from an address space
237 * @capid: Address space capability ID
238 * @extent_capid: Memory extent capability ID
239 * @vbase: location in address space
240 * @flags: Flags for address space mapping
241 * @offset: Offset into memory extent (physical address of memory)
242 * @size: Size of memory to map; must be page-aligned
243 */
gunyah_hypercall_addrspace_unmap(u64 capid,u64 extent_capid,u64 vbase,u32 flags,u64 offset,u64 size)244 enum gunyah_error gunyah_hypercall_addrspace_unmap(u64 capid, u64 extent_capid, u64 vbase,
245 u32 flags, u64 offset, u64 size)
246 {
247 struct arm_smccc_1_2_regs args = {
248 .a0 = GUNYAH_HYPERCALL_ADDRSPACE_UNMAP,
249 .a1 = capid,
250 .a2 = extent_capid,
251 .a3 = vbase,
252 .a4 = flags,
253 .a5 = offset,
254 .a6 = size,
255 /* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
256 .a7 = 0,
257 };
258 struct arm_smccc_1_2_regs res;
259
260 arm_smccc_1_2_hvc(&args, &res);
261
262 return res.a0;
263 }
264 EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspace_unmap);
265
266 /**
267 * gunyah_hypercall_memextent_donate() - Donate memory from one memory extent to another
268 * @options: donate options
269 * @from_capid: Memory extent capability ID to donate from
270 * @to_capid: Memory extent capability ID to donate to
271 * @offset: Offset into memory extent (physical address of memory)
272 * @size: Size of memory to donate; must be page-aligned
273 */
gunyah_hypercall_memextent_donate(u32 options,u64 from_capid,u64 to_capid,u64 offset,u64 size)274 enum gunyah_error gunyah_hypercall_memextent_donate(u32 options, u64 from_capid, u64 to_capid,
275 u64 offset, u64 size)
276 {
277 struct arm_smccc_res res;
278
279 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_MEMEXTENT_DONATE, options, from_capid, to_capid,
280 offset, size, 0, &res);
281
282 return res.a0;
283 }
284 EXPORT_SYMBOL_GPL(gunyah_hypercall_memextent_donate);
285
286 /**
287 * gunyah_hypercall_vcpu_run() - Donate CPU time to a vcpu
288 * @capid: capability ID of the vCPU to run
289 * @resume_data: Array of 3 state-specific resume data
290 * @resp: Filled reason why vCPU exited when return value is GUNYAH_ERROR_OK
291 *
292 * See also:
293 * https://github.com/quic/gunyah-hypervisor/blob/develop/docs/api/gunyah_api.md#run-a-proxy-scheduled-vcpu-thread
294 */
295 enum gunyah_error
gunyah_hypercall_vcpu_run(u64 capid,unsigned long * resume_data,struct gunyah_hypercall_vcpu_run_resp * resp)296 gunyah_hypercall_vcpu_run(u64 capid, unsigned long *resume_data,
297 struct gunyah_hypercall_vcpu_run_resp *resp)
298 {
299 struct arm_smccc_1_2_regs args = {
300 .a0 = GUNYAH_HYPERCALL_VCPU_RUN,
301 .a1 = capid,
302 .a2 = resume_data[0],
303 .a3 = resume_data[1],
304 .a4 = resume_data[2],
305 /* C language says this will be implictly zero. Gunyah requires 0, so be explicit */
306 .a5 = 0,
307 };
308 struct arm_smccc_1_2_regs res;
309
310 arm_smccc_1_2_hvc(&args, &res);
311 if (res.a0 == GUNYAH_ERROR_OK) {
312 resp->sized_state = res.a1;
313 resp->state_data[0] = res.a2;
314 resp->state_data[1] = res.a3;
315 resp->state_data[2] = res.a4;
316 }
317
318 /*
319 * PSCI_SYSTEM_RESET is also a state where VM is shutdown
320 * Translate it to GUNYAH_VCPU_STATE_SYSTEM_OFF as VMM will
321 * be able to take the action based on the exit_info.
322 */
323 if (resp->sized_state == GUNYAH_VCPU_RUN_STATE_PSCI_SYSTEM_RESET)
324 resp->sized_state = GUNYAH_VCPU_STATE_SYSTEM_OFF;
325
326 return res.a0;
327 }
328 EXPORT_SYMBOL_GPL(gunyah_hypercall_vcpu_run);
329
330 /**
331 * gunyah_hypercall_addrspace_find_info_area() - Find the IPA and size of the info area
332 * @ipa: Filled with the IPA of the info area
333 * @size: Filled with the size of the info area
334 *
335 * See also:
336 * https://github.com/quic/gunyah-hypervisor/blob/develop/docs/api/gunyah_api.md#address-space-management
337 */
338 enum gunyah_error
gunyah_hypercall_addrspace_find_info_area(unsigned long * ipa,unsigned long * size)339 gunyah_hypercall_addrspace_find_info_area(unsigned long *ipa, unsigned long *size)
340 {
341 struct arm_smccc_res res;
342
343 arm_smccc_1_1_hvc(GUNYAH_HYPERCALL_ADDRSPACE_FIND_INFO_AREA, 0, &res);
344 if (res.a0 == GUNYAH_ERROR_OK) {
345 *ipa = res.a1;
346 *size = res.a2;
347 }
348
349 return res.a0;
350 }
351 EXPORT_SYMBOL_GPL(gunyah_hypercall_addrspace_find_info_area);
352
353 MODULE_LICENSE("GPL");
354 MODULE_DESCRIPTION("Gunyah Hypervisor Hypercalls");
355