| 1 | |
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 279 | void qcom_pas_ops_register(struct qcom_pas_ops *ops)
|
| 280 | {
|
| 281 | if (!qcom_pas_is_available())
|
| 282 |
|
| 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 |
|
| 290 | void qcom_pas_ops_unregister(void)
|
| 291 | {
|
| 292 |
|
| 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");
|