// SPDX-License-Identifier: BSD-2-Clause /* * Copyright (C) 2016 Freescale Semiconductor, Inc. * Copyright 2017-2021 NXP */ #include #include #include #include #include #include #include #include #define RNG_INIT_RETRY 100 #define SC_RPC_VERSION 1 #define SC_RPC_MAX_MSG 8 /* Macros to fill struct sc_rpc_msg data field */ #define RPC_U32(mesg, idx) ((mesg)->data.u32[(idx)]) #define RPC_U16(mesg, idx) ((mesg)->data.u16[(idx)]) #define RPC_U8(mesg, idx) ((mesg)->data.u8[(idx)]) /* Defines for struct sc_rpc_msg svc field */ #define SC_RPC_SVC_PM 2 #define SC_RPC_SVC_RM 3 #define SC_RPC_SVC_SECO 9 /* Define for PM function calls */ enum sc_pm_func { SC_PM_FUNC_SET_RESOURCE_POWER_MODE = 3 }; /* Defines for RM function calls */ enum sc_rm_func { SC_RM_FUNC_GET_PARTITION = 5, SC_RM_FUNC_ASSIGN_RESOURCE = 8 }; /* Define for SECO function calls */ enum sc_seco_func { SC_SECO_FUNC_START_RNG = 22 }; /* Internal SCFW API error codes */ enum sc_error { SC_ERR_NONE = 0, /* Success */ SC_ERR_VERSION, /* Incompatible API version */ SC_ERR_CONFIG, /* Configuration error */ SC_ERR_PARM, /* Bad parameter */ SC_ERR_NOACCESS, /* Permission error (no access) */ SC_ERR_LOCKED, /* Permission error (locked) */ SC_ERR_UNAVAILABLE, /* Unavailable (out of resources) */ SC_ERR_NOTFOUND, /* Not found */ SC_ERR_NOPOWER, /* No power */ SC_ERR_IPC, /* Generic IPC error */ SC_ERR_BUSY, /* Resource is currently busy/active */ SC_ERR_FAIL, /* General I/O failure */ SC_ERR_LAST }; /* RNG SECO states */ enum sc_seco_rng_status { SC_SECO_RNG_STAT_UNAVAILABLE = 0, SC_SECO_RNG_STAT_INPROGRESS, SC_SECO_RNG_STAT_READY }; /* Resources IDs */ enum sc_resource { SC_RES_CAAM_JR1 = 500, SC_RES_CAAM_JR2, SC_RES_CAAM_JR3, SC_RES_CAAM_JR1_OUT = 514, SC_RES_CAAM_JR2_OUT, SC_RES_CAAM_JR3_OUT, SC_RES_CAAM_JR0 = 519, SC_RES_CAAM_JR0_OUT, SC_RES_LAST = 546 }; /* Power modes */ enum sc_power_mode { SC_PM_PW_MODE_OFF = 0, SC_PM_PW_MODE_STBY, SC_PM_PW_MODE_LP, SC_PM_PW_MODE_ON }; /* RPC message header */ struct sc_rpc_msg_header { uint8_t version; /* SC RPC version */ uint8_t size; /* Size of the message */ uint8_t svc; /* Type of service: one of SC_RPC_SVC_* */ uint8_t func; /* Function ID or Error code */ }; /* RPC message format */ struct sc_rpc_msg { struct sc_rpc_msg_header header; union { uint32_t u32[SC_RPC_MAX_MSG - 1]; uint16_t u16[(SC_RPC_MAX_MSG - 1) * 2]; uint8_t u8[(SC_RPC_MAX_MSG - 1) * 4]; } data; }; static struct mutex scu_mu_mutex = MUTEX_INITIALIZER; static vaddr_t secure_ipc_addr; register_phys_mem(MEM_AREA_IO_SEC, SC_IPC_BASE_SECURE, SC_IPC_SIZE); /* * Read a message from an IPC channel * * @ipc IPC channel * @msg Received message */ static TEE_Result sc_ipc_read(vaddr_t ipc, struct sc_rpc_msg *msg) { TEE_Result res = TEE_ERROR_GENERIC; unsigned int count = 0; if (!msg) { EMSG("msg is NULL"); return TEE_ERROR_BAD_PARAMETERS; } assert(ipc); res = mu_receive_msg(ipc, 0, (uint32_t *)msg); if (res) return res; /* Check the size of the message to receive */ if (msg->header.size > SC_RPC_MAX_MSG) { EMSG("Size of the message is > than SC_RPC_MAX_MSG"); return TEE_ERROR_BAD_FORMAT; } for (count = 1; count < msg->header.size; count++) { res = mu_receive_msg(ipc, count % MU_NB_RR, &msg->data.u32[count - 1]); if (res) return res; } return TEE_SUCCESS; } /* * Write a message to an IPC channel * * @ipc IPC channel * @msg Send message pointer */ static TEE_Result sc_ipc_write(vaddr_t ipc, struct sc_rpc_msg *msg) { TEE_Result res = TEE_ERROR_GENERIC; unsigned int count = 0; if (!msg) { EMSG("msg is NULL"); return TEE_ERROR_BAD_PARAMETERS; } if (msg->header.size > SC_RPC_MAX_MSG) { EMSG("msg->size is > than SC_RPC_MAX_MSG"); return TEE_ERROR_BAD_FORMAT; } assert(ipc); res = mu_send_msg(ipc, 0, *(uint32_t *)msg); if (res) return res; for (count = 1; count < msg->header.size; count++) { res = mu_send_msg(ipc, count % MU_NB_TR, msg->data.u32[count - 1]); if (res) return res; } return TEE_SUCCESS; } /* * Send an RPC message over the secure world IPC channel * * @msg Message to send. This pointer will also return the answer * message if expected. * @wait_resp Set to true if an answer is expected. */ static TEE_Result sc_call_rpc(struct sc_rpc_msg *msg, bool wait_resp) { TEE_Result res = TEE_ERROR_GENERIC; mutex_lock(&scu_mu_mutex); res = sc_ipc_write(secure_ipc_addr, msg); if (res == TEE_SUCCESS && wait_resp) res = sc_ipc_read(secure_ipc_addr, msg); mutex_unlock(&scu_mu_mutex); return res; } /* * Get the partition ID of secure world * * @partition Partition ID */ static TEE_Result sc_rm_get_partition(uint8_t *partition) { TEE_Result res = TEE_ERROR_GENERIC; enum sc_error err = SC_ERR_LAST; struct sc_rpc_msg msg = { .header.version = SC_RPC_VERSION, .header.size = 1, .header.svc = SC_RPC_SVC_RM, .header.func = SC_RM_FUNC_GET_PARTITION, }; res = sc_call_rpc(&msg, true); if (res != TEE_SUCCESS) { EMSG("Communication error"); return res; } err = msg.header.func; if (err != SC_ERR_NONE) { EMSG("Unable to get partition ID, sc_error: %d", err); return TEE_ERROR_GENERIC; } *partition = RPC_U8(&msg, 0); return TEE_SUCCESS; } /* * Set the given power mode of a resource * * @resource ID of the resource * @mode Power mode to apply */ static TEE_Result sc_pm_set_resource_power_mode(enum sc_resource resource, enum sc_power_mode mode) { TEE_Result res = TEE_ERROR_GENERIC; enum sc_error scu_error = SC_ERR_LAST; struct sc_rpc_msg msg = { .header.version = SC_RPC_VERSION, .header.size = 2, .header.svc = SC_RPC_SVC_PM, .header.func = SC_PM_FUNC_SET_RESOURCE_POWER_MODE, }; RPC_U16(&msg, 0) = (uint16_t)resource; RPC_U8(&msg, 2) = (uint8_t)mode; res = sc_call_rpc(&msg, true); if (res != TEE_SUCCESS) { EMSG("Communication error"); return res; } scu_error = msg.header.func; if (scu_error != SC_ERR_NONE) { EMSG("Unable to set resource power mode sc_error: %d", scu_error); return TEE_ERROR_GENERIC; } return TEE_SUCCESS; } /* * Assign ownership of a resource to the secure partition * * @resource Resource to assign */ static TEE_Result sc_rm_assign_resource(enum sc_resource resource) { TEE_Result res = TEE_ERROR_GENERIC; enum sc_error err = SC_ERR_LAST; uint8_t secure_partition = 0; struct sc_rpc_msg msg = { .header.version = SC_RPC_VERSION, .header.size = 2, .header.svc = SC_RPC_SVC_RM, .header.func = SC_RM_FUNC_ASSIGN_RESOURCE, }; res = sc_rm_get_partition(&secure_partition); if (res != TEE_SUCCESS) { EMSG("Cannot get secure partition ID"); return res; } RPC_U16(&msg, 0) = (uint16_t)resource; RPC_U8(&msg, 2) = secure_partition; res = sc_call_rpc(&msg, true); if (res != TEE_SUCCESS) { EMSG("Communication error"); return res; } err = msg.header.func; if (err != SC_ERR_NONE) { EMSG("Unable to assign resource, sc_error: %d", err); return TEE_ERROR_GENERIC; } return TEE_SUCCESS; } TEE_Result imx_sc_rm_enable_jr(unsigned int jr_index) { TEE_Result res = TEE_ERROR_GENERIC; enum sc_resource jr_res = SC_RES_LAST; enum sc_resource jr_out_res = SC_RES_LAST; switch (jr_index) { case 0: jr_res = SC_RES_CAAM_JR0; jr_out_res = SC_RES_CAAM_JR0_OUT; break; case 1: jr_res = SC_RES_CAAM_JR1; jr_out_res = SC_RES_CAAM_JR1_OUT; break; case 2: jr_res = SC_RES_CAAM_JR2; jr_out_res = SC_RES_CAAM_JR2_OUT; break; case 3: jr_res = SC_RES_CAAM_JR3; jr_out_res = SC_RES_CAAM_JR3_OUT; break; default: EMSG("Wrong JR Index, should be 0, 1, 2 or 3"); return TEE_ERROR_GENERIC; } /* Assign JR resources to secure world */ res = sc_rm_assign_resource(jr_res); if (res != TEE_SUCCESS) { EMSG("Assign SC_R_CAAM_JR%u resource failed", jr_index); return res; } res = sc_rm_assign_resource(jr_out_res); if (res != TEE_SUCCESS) { EMSG("Assign SC_R_CAAM_JR%u_OUT resource failed", jr_index); return res; } /* Power ON JR resources */ res = sc_pm_set_resource_power_mode(jr_res, SC_PM_PW_MODE_ON); if (res != TEE_SUCCESS) { EMSG("POWER ON SC_R_CAAM_JR%u resource failed", jr_index); return res; } res = sc_pm_set_resource_power_mode(jr_out_res, SC_PM_PW_MODE_ON); if (res != TEE_SUCCESS) { EMSG("POWER ON SC_R_CAAM_JR%u_OUT resource failed", jr_index); return res; } return TEE_SUCCESS; } TEE_Result imx_sc_seco_start_rng(void) { TEE_Result res = TEE_ERROR_GENERIC; enum sc_error err = SC_ERR_LAST; enum sc_seco_rng_status status = SC_SECO_RNG_STAT_UNAVAILABLE; unsigned int retry = 0; struct sc_rpc_msg msg = { .header.version = SC_RPC_VERSION, .header.size = 1, .header.svc = SC_RPC_SVC_SECO, .header.func = SC_SECO_FUNC_START_RNG, }; for (retry = RNG_INIT_RETRY; retry; retry--) { res = sc_call_rpc(&msg, true); if (res != TEE_SUCCESS) { EMSG("Configuration error"); return res; } err = msg.header.func; if (err != SC_ERR_NONE) { EMSG("RNG status: %d", err); return TEE_ERROR_GENERIC; } status = RPC_U32(&msg, 0); if (status == SC_SECO_RNG_STAT_READY) return TEE_SUCCESS; } return TEE_ERROR_GENERIC; } TEE_Result imx_sc_driver_init(void) { vaddr_t va = 0; va = core_mmu_get_va(SC_IPC_BASE_SECURE, MEM_AREA_IO_SEC, SC_IPC_SIZE); if (!va) return TEE_ERROR_GENERIC; mutex_lock(&scu_mu_mutex); mu_init(va); secure_ipc_addr = va; mutex_unlock(&scu_mu_mutex); return TEE_SUCCESS; }