Skip to content

Commit 8b9d205

Browse files
mukeshojha-linuxandersson
authored andcommitted
firmware: qcom_scm: Add qcom_scm_pas_get_rsc_table() to get resource table
Qualcomm remote processor may rely on Static and Dynamic resources for it to be functional. Static resources are fixed like for example, memory-mapped addresses required by the subsystem and dynamic resources, such as shared memory in DDR etc., are determined at runtime during the boot process. For most of the Qualcomm SoCs, when run with Gunyah or older QHEE hypervisor, all the resources whether it is static or dynamic, is managed by the hypervisor. Dynamic resources if it is present for a remote processor will always be coming from secure world via SMC call while static resources may be present in remote processor firmware binary or it may be coming qcom_scm_pas_get_rsc_table() SMC call along with dynamic resources. Some of the remote processor drivers, such as video, GPU, IPA, etc., do not check whether resources are present in their remote processor firmware binary. In such cases, the caller of this function should set input_rt and input_rt_size as NULL and zero respectively. Remoteproc framework has method to check whether firmware binary contain resources or not and they should be pass resource table pointer to input_rt and resource table size to input_rt_size and this will be forwarded to TrustZone for authentication. TrustZone will then append the dynamic resources and return the complete resource table in the passed output buffer. More about documentation on resource table format can be found in include/linux/remoteproc.h Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com> Link: https://lore.kernel.org/r/20260105-kvmrprocv10-v10-11-022e96815380@oss.qualcomm.com Signed-off-by: Bjorn Andersson <andersson@kernel.org>
1 parent b019925 commit 8b9d205

3 files changed

Lines changed: 176 additions & 0 deletions

File tree

drivers/firmware/qcom/qcom_scm.c

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/of_reserved_mem.h>
2828
#include <linux/platform_device.h>
2929
#include <linux/reset-controller.h>
30+
#include <linux/remoteproc.h>
3031
#include <linux/sizes.h>
3132
#include <linux/types.h>
3233

@@ -111,6 +112,8 @@ enum qcom_scm_qseecom_tz_cmd_info {
111112
QSEECOM_TZ_CMD_INFO_VERSION = 3,
112113
};
113114

115+
#define RSCTABLE_BUFFER_NOT_SUFFICIENT 20
116+
114117
#define QSEECOM_MAX_APP_NAME_SIZE 64
115118
#define SHMBRIDGE_RESULT_NOTSUPP 4
116119

@@ -766,6 +769,174 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size)
766769
}
767770
EXPORT_SYMBOL_GPL(qcom_scm_pas_mem_setup);
768771

772+
static void *__qcom_scm_pas_get_rsc_table(u32 pas_id, void *input_rt_tzm,
773+
size_t input_rt_size,
774+
size_t *output_rt_size)
775+
{
776+
struct qcom_scm_desc desc = {
777+
.svc = QCOM_SCM_SVC_PIL,
778+
.cmd = QCOM_SCM_PIL_PAS_GET_RSCTABLE,
779+
.arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RO, QCOM_SCM_VAL,
780+
QCOM_SCM_RW, QCOM_SCM_VAL),
781+
.args[0] = pas_id,
782+
.owner = ARM_SMCCC_OWNER_SIP,
783+
};
784+
struct qcom_scm_res res;
785+
void *output_rt_tzm;
786+
int ret;
787+
788+
output_rt_tzm = qcom_tzmem_alloc(__scm->mempool, *output_rt_size, GFP_KERNEL);
789+
if (!output_rt_tzm)
790+
return ERR_PTR(-ENOMEM);
791+
792+
desc.args[1] = qcom_tzmem_to_phys(input_rt_tzm);
793+
desc.args[2] = input_rt_size;
794+
desc.args[3] = qcom_tzmem_to_phys(output_rt_tzm);
795+
desc.args[4] = *output_rt_size;
796+
797+
/*
798+
* Whether SMC fail or pass, res.result[2] will hold actual resource table
799+
* size.
800+
*
801+
* If passed 'output_rt_size' buffer size is not sufficient to hold the
802+
* resource table TrustZone sends, response code in res.result[1] as
803+
* RSCTABLE_BUFFER_NOT_SUFFICIENT so that caller can retry this SMC call
804+
* with output_rt_tzm buffer with res.result[2] size however, It should not
805+
* be of unresonable size.
806+
*/
807+
ret = qcom_scm_call(__scm->dev, &desc, &res);
808+
if (!ret && res.result[2] > SZ_1G) {
809+
ret = -E2BIG;
810+
goto free_output_rt;
811+
}
812+
813+
*output_rt_size = res.result[2];
814+
if (ret && res.result[1] == RSCTABLE_BUFFER_NOT_SUFFICIENT)
815+
ret = -EOVERFLOW;
816+
817+
free_output_rt:
818+
if (ret)
819+
qcom_tzmem_free(output_rt_tzm);
820+
821+
return ret ? ERR_PTR(ret) : output_rt_tzm;
822+
}
823+
824+
/**
825+
* qcom_scm_pas_get_rsc_table() - Retrieve the resource table in passed output buffer
826+
* for a given peripheral.
827+
*
828+
* Qualcomm remote processor may rely on both static and dynamic resources for
829+
* its functionality. Static resources typically refer to memory-mapped addresses
830+
* required by the subsystem and are often embedded within the firmware binary
831+
* and dynamic resources, such as shared memory in DDR etc., are determined at
832+
* runtime during the boot process.
833+
*
834+
* On Qualcomm Technologies devices, it's possible that static resources are not
835+
* embedded in the firmware binary and instead are provided by TrustZone However,
836+
* dynamic resources are always expected to come from TrustZone. This indicates
837+
* that for Qualcomm devices, all resources (static and dynamic) will be provided
838+
* by TrustZone via the SMC call.
839+
*
840+
* If the remote processor firmware binary does contain static resources, they
841+
* should be passed in input_rt. These will be forwarded to TrustZone for
842+
* authentication. TrustZone will then append the dynamic resources and return
843+
* the complete resource table in output_rt_tzm.
844+
*
845+
* If the remote processor firmware binary does not include a resource table,
846+
* the caller of this function should set input_rt as NULL and input_rt_size
847+
* as zero respectively.
848+
*
849+
* More about documentation on resource table data structures can be found in
850+
* include/linux/remoteproc.h
851+
*
852+
* @ctx: PAS context
853+
* @pas_id: peripheral authentication service id
854+
* @input_rt: resource table buffer which is present in firmware binary
855+
* @input_rt_size: size of the resource table present in firmware binary
856+
* @output_rt_size: TrustZone expects caller should pass worst case size for
857+
* the output_rt_tzm.
858+
*
859+
* Return:
860+
* On success, returns a pointer to the allocated buffer containing the final
861+
* resource table and output_rt_size will have actual resource table size from
862+
* TrustZone. The caller is responsible for freeing the buffer. On failure,
863+
* returns ERR_PTR(-errno).
864+
*/
865+
struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
866+
void *input_rt,
867+
size_t input_rt_size,
868+
size_t *output_rt_size)
869+
{
870+
struct resource_table empty_rsc = {};
871+
size_t size = SZ_16K;
872+
void *output_rt_tzm;
873+
void *input_rt_tzm;
874+
void *tbl_ptr;
875+
int ret;
876+
877+
ret = qcom_scm_clk_enable();
878+
if (ret)
879+
return ERR_PTR(ret);
880+
881+
ret = qcom_scm_bw_enable();
882+
if (ret)
883+
goto disable_clk;
884+
885+
/*
886+
* TrustZone can not accept buffer as NULL value as argument hence,
887+
* we need to pass a input buffer indicating that subsystem firmware
888+
* does not have resource table by filling resource table structure.
889+
*/
890+
if (!input_rt) {
891+
input_rt = &empty_rsc;
892+
input_rt_size = sizeof(empty_rsc);
893+
}
894+
895+
input_rt_tzm = qcom_tzmem_alloc(__scm->mempool, input_rt_size, GFP_KERNEL);
896+
if (!input_rt_tzm) {
897+
ret = -ENOMEM;
898+
goto disable_scm_bw;
899+
}
900+
901+
memcpy(input_rt_tzm, input_rt, input_rt_size);
902+
903+
output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id, input_rt_tzm,
904+
input_rt_size, &size);
905+
if (PTR_ERR(output_rt_tzm) == -EOVERFLOW)
906+
/* Try again with the size requested by the TZ */
907+
output_rt_tzm = __qcom_scm_pas_get_rsc_table(ctx->pas_id,
908+
input_rt_tzm,
909+
input_rt_size,
910+
&size);
911+
if (IS_ERR(output_rt_tzm)) {
912+
ret = PTR_ERR(output_rt_tzm);
913+
goto free_input_rt;
914+
}
915+
916+
tbl_ptr = kzalloc(size, GFP_KERNEL);
917+
if (!tbl_ptr) {
918+
qcom_tzmem_free(output_rt_tzm);
919+
ret = -ENOMEM;
920+
goto free_input_rt;
921+
}
922+
923+
memcpy(tbl_ptr, output_rt_tzm, size);
924+
*output_rt_size = size;
925+
qcom_tzmem_free(output_rt_tzm);
926+
927+
free_input_rt:
928+
qcom_tzmem_free(input_rt_tzm);
929+
930+
disable_scm_bw:
931+
qcom_scm_bw_disable();
932+
933+
disable_clk:
934+
qcom_scm_clk_disable();
935+
936+
return ret ? ERR_PTR(ret) : tbl_ptr;
937+
}
938+
EXPORT_SYMBOL_GPL(qcom_scm_pas_get_rsc_table);
939+
769940
/**
770941
* qcom_scm_pas_auth_and_reset() - Authenticate the given peripheral firmware
771942
* and reset the remote processor

drivers/firmware/qcom/qcom_scm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ int qcom_scm_shm_bridge_enable(struct device *scm_dev);
105105
#define QCOM_SCM_PIL_PAS_SHUTDOWN 0x06
106106
#define QCOM_SCM_PIL_PAS_IS_SUPPORTED 0x07
107107
#define QCOM_SCM_PIL_PAS_MSS_RESET 0x0a
108+
#define QCOM_SCM_PIL_PAS_GET_RSCTABLE 0x21
108109

109110
#define QCOM_SCM_SVC_IO 0x05
110111
#define QCOM_SCM_IO_READ 0x01

include/linux/firmware/qcom/qcom_scm.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ int qcom_scm_pas_mem_setup(u32 pas_id, phys_addr_t addr, phys_addr_t size);
8888
int qcom_scm_pas_auth_and_reset(u32 pas_id);
8989
int qcom_scm_pas_shutdown(u32 pas_id);
9090
bool qcom_scm_pas_supported(u32 pas_id);
91+
struct resource_table *qcom_scm_pas_get_rsc_table(struct qcom_scm_pas_context *ctx,
92+
void *input_rt, size_t input_rt_size,
93+
size_t *output_rt_size);
94+
9195
int qcom_scm_pas_prepare_and_auth_reset(struct qcom_scm_pas_context *ctx);
9296

9397
int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val);

0 commit comments

Comments
 (0)