1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
4 * Copyright (C) 2015 Linaro Ltd.
5 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
6 */
7
8 #include <linux/device/devres.h>
9 #include <linux/firmware/qcom/qcom_pas.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12
13 #include "qcom_pas.h"
14
15 static struct qcom_pas_ops *ops_ptr;
16
17 /**
18 * devm_qcom_pas_context_alloc() - Allocate peripheral authentication service
19 * context for a given peripheral
20 *
21 * PAS context is device-resource managed, so the caller does not need
22 * to worry about freeing the context memory.
23 *
24 * @dev: PAS firmware device
25 * @pas_id: peripheral authentication service id
26 * @mem_phys: Subsystem reserve memory start address
27 * @mem_size: Subsystem reserve memory size
28 *
29 * Return: The new PAS context, or ERR_PTR() on failure.
30 */
devm_qcom_pas_context_alloc(struct device * dev,u32 pas_id,phys_addr_t mem_phys,size_t mem_size)31 struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
32 u32 pas_id,
33 phys_addr_t mem_phys,
34 size_t mem_size)
35 {
36 struct qcom_pas_context *ctx;
37
38 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
39 if (!ctx)
40 return ERR_PTR(-ENOMEM);
41
42 ctx->dev = dev;
43 ctx->pas_id = pas_id;
44 ctx->mem_phys = mem_phys;
45 ctx->mem_size = mem_size;
46
47 return ctx;
48 }
49 EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc);
50
51 /**
52 * qcom_pas_init_image() - Initialize peripheral authentication service state
53 * machine for a given peripheral, using the metadata
54 * @pas_id: peripheral authentication service id
55 * @metadata: pointer to memory containing ELF header, program header table
56 * and optional blob of data used for authenticating the metadata
57 * and the rest of the firmware
58 * @size: size of the metadata
59 * @ctx: optional pas context
60 *
61 * Return: 0 on success.
62 *
63 * Upon successful return, the PAS metadata context (@ctx) will be used to
64 * track the metadata allocation, this needs to be released by invoking
65 * qcom_pas_metadata_release() by the caller.
66 */
qcom_pas_init_image(u32 pas_id,const void * metadata,size_t size,struct qcom_pas_context * ctx)67 int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
68 struct qcom_pas_context *ctx)
69 {
70 if (!ops_ptr)
71 return -ENODEV;
72
73 return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx);
74 }
75 EXPORT_SYMBOL_GPL(qcom_pas_init_image);
76
77 /**
78 * qcom_pas_metadata_release() - release metadata context
79 * @ctx: pas context
80 */
qcom_pas_metadata_release(struct qcom_pas_context * ctx)81 void qcom_pas_metadata_release(struct qcom_pas_context *ctx)
82 {
83 if (!ops_ptr || !ctx || !ctx->ptr)
84 return;
85
86 ops_ptr->metadata_release(ops_ptr->dev, ctx);
87 }
88 EXPORT_SYMBOL_GPL(qcom_pas_metadata_release);
89
90 /**
91 * qcom_pas_mem_setup() - Prepare the memory related to a given peripheral
92 * for firmware loading
93 * @pas_id: peripheral authentication service id
94 * @addr: start address of memory area to prepare
95 * @size: size of the memory area to prepare
96 *
97 * Return: 0 on success.
98 */
qcom_pas_mem_setup(u32 pas_id,phys_addr_t addr,phys_addr_t size)99 int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
100 {
101 if (!ops_ptr)
102 return -ENODEV;
103
104 return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
105 }
106 EXPORT_SYMBOL_GPL(qcom_pas_mem_setup);
107
108 /**
109 * qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
110 * for a given peripheral.
111 *
112 * Qualcomm remote processor may rely on both static and dynamic resources for
113 * its functionality. Static resources typically refer to memory-mapped
114 * addresses required by the subsystem and are often embedded within the
115 * firmware binary and dynamic resources, such as shared memory in DDR etc.,
116 * are determined at runtime during the boot process.
117 *
118 * On Qualcomm Technologies devices, it's possible that static resources are
119 * not embedded in the firmware binary and instead are provided by TrustZone.
120 * However, dynamic resources are always expected to come from TrustZone. This
121 * indicates that for Qualcomm devices, all resources (static and dynamic) will
122 * be provided by TrustZone PAS service.
123 *
124 * If the remote processor firmware binary does contain static resources, they
125 * should be passed in input_rt. These will be forwarded to TrustZone for
126 * authentication. TrustZone will then append the dynamic resources and return
127 * the complete resource table in output_rt_tzm.
128 *
129 * If the remote processor firmware binary does not include a resource table,
130 * the caller of this function should set input_rt as NULL and input_rt_size
131 * as zero respectively.
132 *
133 * More about documentation on resource table data structures can be found in
134 * include/linux/remoteproc.h
135 *
136 * @ctx: PAS context
137 * @input_rt: resource table buffer which is present in firmware binary
138 * @input_rt_size: size of the resource table present in firmware binary
139 * @output_rt_size: TrustZone expects caller should pass worst case size for
140 * the output_rt_tzm.
141 *
142 * Return:
143 * On success, returns a pointer to the allocated buffer containing the final
144 * resource table and output_rt_size will have actual resource table size from
145 * TrustZone. The caller is responsible for freeing the buffer. On failure,
146 * returns ERR_PTR(-errno).
147 */
qcom_pas_get_rsc_table(struct qcom_pas_context * ctx,void * input_rt,size_t input_rt_size,size_t * output_rt_size)148 struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
149 void *input_rt,
150 size_t input_rt_size,
151 size_t *output_rt_size)
152 {
153 if (!ops_ptr)
154 return ERR_PTR(-ENODEV);
155 if (!ctx)
156 return ERR_PTR(-EINVAL);
157
158 return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
159 input_rt_size, output_rt_size);
160 }
161 EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table);
162
163 /**
164 * qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware
165 * and reset the remote processor
166 * @pas_id: peripheral authentication service id
167 *
168 * Return: 0 on success.
169 */
qcom_pas_auth_and_reset(u32 pas_id)170 int qcom_pas_auth_and_reset(u32 pas_id)
171 {
172 if (!ops_ptr)
173 return -ENODEV;
174
175 return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
176 }
177 EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset);
178
179 /**
180 * qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
181 * remote processor
182 *
183 * @ctx: Context saved during call to devm_qcom_pas_context_alloc()
184 *
185 * This function performs the necessary steps to prepare a PAS subsystem,
186 * authenticate it using the provided metadata, and initiate a reset sequence.
187 *
188 * It should be used when Linux is in control setting up the IOMMU hardware
189 * for remote subsystem during secure firmware loading processes. The
190 * preparation step sets up a shmbridge over the firmware memory before
191 * TrustZone accesses the firmware memory region for authentication. The
192 * authentication step verifies the integrity and authenticity of the firmware
193 * or configuration using secure metadata. Finally, the reset step ensures the
194 * subsystem starts in a clean and sane state.
195 *
196 * Return: 0 on success, negative errno on failure.
197 */
qcom_pas_prepare_and_auth_reset(struct qcom_pas_context * ctx)198 int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx)
199 {
200 if (!ops_ptr)
201 return -ENODEV;
202 if (!ctx)
203 return -EINVAL;
204
205 return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
206 }
207 EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset);
208
209 /**
210 * qcom_pas_set_remote_state() - Set the remote processor state
211 * @state: peripheral state
212 * @pas_id: peripheral authentication service id
213 *
214 * Return: 0 on success.
215 */
qcom_pas_set_remote_state(u32 state,u32 pas_id)216 int qcom_pas_set_remote_state(u32 state, u32 pas_id)
217 {
218 if (!ops_ptr)
219 return -ENODEV;
220
221 return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
222 }
223 EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state);
224
225 /**
226 * qcom_pas_shutdown() - Shut down the remote processor
227 * @pas_id: peripheral authentication service id
228 *
229 * Return: 0 on success.
230 */
qcom_pas_shutdown(u32 pas_id)231 int qcom_pas_shutdown(u32 pas_id)
232 {
233 if (!ops_ptr)
234 return -ENODEV;
235
236 return ops_ptr->shutdown(ops_ptr->dev, pas_id);
237 }
238 EXPORT_SYMBOL_GPL(qcom_pas_shutdown);
239
240 /**
241 * qcom_pas_supported() - Check if the peripheral authentication service is
242 * supported for the given peripheral
243 * @pas_id: peripheral authentication service id
244 *
245 * Return: true if PAS is supported for this peripheral, otherwise false.
246 */
qcom_pas_supported(u32 pas_id)247 bool qcom_pas_supported(u32 pas_id)
248 {
249 if (!ops_ptr)
250 return false;
251
252 return ops_ptr->supported(ops_ptr->dev, pas_id);
253 }
254 EXPORT_SYMBOL_GPL(qcom_pas_supported);
255
256 /**
257 * qcom_pas_is_available() - Check if the peripheral authentication service is
258 * available. Note that it is mandatory for any PAS
259 * client to invoke this API. If it returns true then
260 * only any other PAS API can be invoked.
261 *
262 * Return: true if PAS is available, otherwise false.
263 */
qcom_pas_is_available(void)264 bool qcom_pas_is_available(void)
265 {
266 /*
267 * The barrier for ops_ptr is intended to synchronize the data stores
268 * for the ops data structure when client drivers are in parallel
269 * checking for PAS service availability.
270 *
271 * Once the PAS backend becomes available, it is allowed for multiple
272 * threads to enter TZ for parallel bringup of co-processors during
273 * boot.
274 */
275 return !!smp_load_acquire(&ops_ptr);
276 }
277 EXPORT_SYMBOL_GPL(qcom_pas_is_available);
278
qcom_pas_ops_register(struct qcom_pas_ops * ops)279 void qcom_pas_ops_register(struct qcom_pas_ops *ops)
280 {
281 if (!qcom_pas_is_available())
282 /* Paired with smp_load_acquire() in qcom_pas_is_available() */
283 smp_store_release(&ops_ptr, ops);
284 else
285 pr_err("qcom_pas: ops already registered by %s\n",
286 ops_ptr->drv_name);
287 }
288 EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
289
qcom_pas_ops_unregister(void)290 void qcom_pas_ops_unregister(void)
291 {
292 /* Paired with smp_load_acquire() in qcom_pas_is_available() */
293 smp_store_release(&ops_ptr, NULL);
294 }
295 EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister);
296
297 MODULE_LICENSE("GPL");
298 MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver");
299