blob: 24485dd0fa10adb741e482f6cf61a23672f947bf [file] [edit]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2010,2015,2019 The Linux Foundation. All rights reserved.
* Copyright (C) 2015 Linaro Ltd.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
#include <linux/device/devres.h>
#include <linux/firmware/qcom/qcom_pas.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "qcom_pas.h"
static struct qcom_pas_ops *ops_ptr;
/**
* devm_qcom_pas_context_alloc() - Allocate peripheral authentication service
* context for a given peripheral
*
* PAS context is device-resource managed, so the caller does not need
* to worry about freeing the context memory.
*
* @dev: PAS firmware device
* @pas_id: peripheral authentication service id
* @mem_phys: Subsystem reserve memory start address
* @mem_size: Subsystem reserve memory size
*
* Return: The new PAS context, or ERR_PTR() on failure.
*/
struct qcom_pas_context *devm_qcom_pas_context_alloc(struct device *dev,
u32 pas_id,
phys_addr_t mem_phys,
size_t mem_size)
{
struct qcom_pas_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
ctx->pas_id = pas_id;
ctx->mem_phys = mem_phys;
ctx->mem_size = mem_size;
return ctx;
}
EXPORT_SYMBOL_GPL(devm_qcom_pas_context_alloc);
/**
* qcom_pas_init_image() - Initialize peripheral authentication service state
* machine for a given peripheral, using the metadata
* @pas_id: peripheral authentication service id
* @metadata: pointer to memory containing ELF header, program header table
* and optional blob of data used for authenticating the metadata
* and the rest of the firmware
* @size: size of the metadata
* @ctx: optional pas context
*
* Return: 0 on success.
*
* Upon successful return, the PAS metadata context (@ctx) will be used to
* track the metadata allocation, this needs to be released by invoking
* qcom_pas_metadata_release() by the caller.
*/
int qcom_pas_init_image(u32 pas_id, const void *metadata, size_t size,
struct qcom_pas_context *ctx)
{
if (!ops_ptr)
return -ENODEV;
return ops_ptr->init_image(ops_ptr->dev, pas_id, metadata, size, ctx);
}
EXPORT_SYMBOL_GPL(qcom_pas_init_image);
/**
* qcom_pas_metadata_release() - release metadata context
* @ctx: pas context
*/
void qcom_pas_metadata_release(struct qcom_pas_context *ctx)
{
if (!ops_ptr || !ctx || !ctx->ptr)
return;
ops_ptr->metadata_release(ops_ptr->dev, ctx);
}
EXPORT_SYMBOL_GPL(qcom_pas_metadata_release);
/**
* qcom_pas_mem_setup() - Prepare the memory related to a given peripheral
* for firmware loading
* @pas_id: peripheral authentication service id
* @addr: start address of memory area to prepare
* @size: size of the memory area to prepare
*
* Return: 0 on success.
*/
int qcom_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
{
if (!ops_ptr)
return -ENODEV;
return ops_ptr->mem_setup(ops_ptr->dev, pas_id, addr, size);
}
EXPORT_SYMBOL_GPL(qcom_pas_mem_setup);
/**
* qcom_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
* for a given peripheral.
*
* Qualcomm remote processor may rely on both static and dynamic resources for
* its functionality. Static resources typically refer to memory-mapped
* addresses required by the subsystem and are often embedded within the
* firmware binary and dynamic resources, such as shared memory in DDR etc.,
* are determined at runtime during the boot process.
*
* On Qualcomm Technologies devices, it's possible that static resources are
* not embedded in the firmware binary and instead are provided by TrustZone.
* However, dynamic resources are always expected to come from TrustZone. This
* indicates that for Qualcomm devices, all resources (static and dynamic) will
* be provided by TrustZone PAS service.
*
* If the remote processor firmware binary does contain static resources, they
* should be passed in input_rt. These will be forwarded to TrustZone for
* authentication. TrustZone will then append the dynamic resources and return
* the complete resource table in output_rt_tzm.
*
* If the remote processor firmware binary does not include a resource table,
* the caller of this function should set input_rt as NULL and input_rt_size
* as zero respectively.
*
* More about documentation on resource table data structures can be found in
* include/linux/remoteproc.h
*
* @ctx: PAS context
* @input_rt: resource table buffer which is present in firmware binary
* @input_rt_size: size of the resource table present in firmware binary
* @output_rt_size: TrustZone expects caller should pass worst case size for
* the output_rt_tzm.
*
* Return:
* On success, returns a pointer to the allocated buffer containing the final
* resource table and output_rt_size will have actual resource table size from
* TrustZone. The caller is responsible for freeing the buffer. On failure,
* returns ERR_PTR(-errno).
*/
struct resource_table *qcom_pas_get_rsc_table(struct qcom_pas_context *ctx,
void *input_rt,
size_t input_rt_size,
size_t *output_rt_size)
{
if (!ops_ptr)
return ERR_PTR(-ENODEV);
if (!ctx)
return ERR_PTR(-EINVAL);
return ops_ptr->get_rsc_table(ops_ptr->dev, ctx, input_rt,
input_rt_size, output_rt_size);
}
EXPORT_SYMBOL_GPL(qcom_pas_get_rsc_table);
/**
* qcom_pas_auth_and_reset() - Authenticate the given peripheral firmware
* and reset the remote processor
* @pas_id: peripheral authentication service id
*
* Return: 0 on success.
*/
int qcom_pas_auth_and_reset(u32 pas_id)
{
if (!ops_ptr)
return -ENODEV;
return ops_ptr->auth_and_reset(ops_ptr->dev, pas_id);
}
EXPORT_SYMBOL_GPL(qcom_pas_auth_and_reset);
/**
* qcom_pas_prepare_and_auth_reset() - Prepare, authenticate, and reset the
* remote processor
*
* @ctx: Context saved during call to devm_qcom_pas_context_alloc()
*
* This function performs the necessary steps to prepare a PAS subsystem,
* authenticate it using the provided metadata, and initiate a reset sequence.
*
* It should be used when Linux is in control setting up the IOMMU hardware
* for remote subsystem during secure firmware loading processes. The
* preparation step sets up a shmbridge over the firmware memory before
* TrustZone accesses the firmware memory region for authentication. The
* authentication step verifies the integrity and authenticity of the firmware
* or configuration using secure metadata. Finally, the reset step ensures the
* subsystem starts in a clean and sane state.
*
* Return: 0 on success, negative errno on failure.
*/
int qcom_pas_prepare_and_auth_reset(struct qcom_pas_context *ctx)
{
if (!ops_ptr)
return -ENODEV;
if (!ctx)
return -EINVAL;
return ops_ptr->prepare_and_auth_reset(ops_ptr->dev, ctx);
}
EXPORT_SYMBOL_GPL(qcom_pas_prepare_and_auth_reset);
/**
* qcom_pas_set_remote_state() - Set the remote processor state
* @state: peripheral state
* @pas_id: peripheral authentication service id
*
* Return: 0 on success.
*/
int qcom_pas_set_remote_state(u32 state, u32 pas_id)
{
if (!ops_ptr)
return -ENODEV;
return ops_ptr->set_remote_state(ops_ptr->dev, state, pas_id);
}
EXPORT_SYMBOL_GPL(qcom_pas_set_remote_state);
/**
* qcom_pas_shutdown() - Shut down the remote processor
* @pas_id: peripheral authentication service id
*
* Return: 0 on success.
*/
int qcom_pas_shutdown(u32 pas_id)
{
if (!ops_ptr)
return -ENODEV;
return ops_ptr->shutdown(ops_ptr->dev, pas_id);
}
EXPORT_SYMBOL_GPL(qcom_pas_shutdown);
/**
* qcom_pas_supported() - Check if the peripheral authentication service is
* supported for the given peripheral
* @pas_id: peripheral authentication service id
*
* Return: true if PAS is supported for this peripheral, otherwise false.
*/
bool qcom_pas_supported(u32 pas_id)
{
if (!ops_ptr)
return false;
return ops_ptr->supported(ops_ptr->dev, pas_id);
}
EXPORT_SYMBOL_GPL(qcom_pas_supported);
/**
* qcom_pas_is_available() - Check if the peripheral authentication service is
* available. Note that it is mandatory for any PAS
* client to invoke this API. If it returns true then
* only any other PAS API can be invoked.
*
* Return: true if PAS is available, otherwise false.
*/
bool qcom_pas_is_available(void)
{
/*
* The barrier for ops_ptr is intended to synchronize the data stores
* for the ops data structure when client drivers are in parallel
* checking for PAS service availability.
*
* Once the PAS backend becomes available, it is allowed for multiple
* threads to enter TZ for parallel bringup of co-processors during
* boot.
*/
return !!smp_load_acquire(&ops_ptr);
}
EXPORT_SYMBOL_GPL(qcom_pas_is_available);
void qcom_pas_ops_register(struct qcom_pas_ops *ops)
{
if (!qcom_pas_is_available())
/* Paired with smp_load_acquire() in qcom_pas_is_available() */
smp_store_release(&ops_ptr, ops);
else
pr_err("qcom_pas: ops already registered by %s\n",
ops_ptr->drv_name);
}
EXPORT_SYMBOL_GPL(qcom_pas_ops_register);
void qcom_pas_ops_unregister(void)
{
/* Paired with smp_load_acquire() in qcom_pas_is_available() */
smp_store_release(&ops_ptr, NULL);
}
EXPORT_SYMBOL_GPL(qcom_pas_ops_unregister);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Qualcomm generic TZ PAS driver");