• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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