1427 lines
41 KiB
C
1427 lines
41 KiB
C
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of The Linux Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "QSEECOM: %s: " fmt, __func__
|
|
|
|
#include <partition_parser.h>
|
|
#include <qseecom_lk.h>
|
|
#include <scm.h>
|
|
#include <qseecomi_lk.h>
|
|
#include "qseecom_lk_api.h"
|
|
#include <debug.h>
|
|
#include <kernel/mutex.h>
|
|
#include <malloc.h>
|
|
#include <stdlib.h>
|
|
#include <arch/defines.h>
|
|
#include <string.h>
|
|
#include <platform/iomap.h>
|
|
#include <platform.h>
|
|
|
|
#define QSEOS_VERSION_14 0x14
|
|
#define QSEEE_VERSION_00 0x400000
|
|
#define QSEE_VERSION_20 0x800000
|
|
|
|
|
|
#define QSEOS_CHECK_VERSION_CMD 0x00001803
|
|
|
|
#define MAX_SCM_ARGS 10
|
|
#define N_EXT_SCM_ARGS 7
|
|
#define FIRST_EXT_ARG_IDX 3
|
|
|
|
#define N_REGISTER_ARGS (MAX_SCM_ARGS - N_EXT_SCM_ARGS + 1)
|
|
|
|
#define QSEE_LOG_BUF_SIZE (4096)
|
|
#define GENERIC_ERROR -1
|
|
#define LISTENER_ALREADY_PRESENT_ERROR -2
|
|
|
|
#define TZ_CALL 6
|
|
enum qseecom_client_handle_type {
|
|
QSEECOM_CLIENT_APP = 1,
|
|
QSEECOM_LISTENER_SERVICE,
|
|
QSEECOM_SECURE_SERVICE,
|
|
QSEECOM_GENERIC,
|
|
QSEECOM_UNAVAILABLE_CLIENT_APP,
|
|
};
|
|
|
|
struct qseecom_registered_listener_list {
|
|
struct list_node node;
|
|
struct qseecom_register_listener_req svc;
|
|
ListenerCallback CallbackFn;
|
|
};
|
|
|
|
struct qseecom_registered_app_list {
|
|
struct list_node node;
|
|
uint32_t app_id;
|
|
uint32_t ref_cnt;
|
|
char app_name[MAX_APP_NAME_SIZE];
|
|
int handle;
|
|
};
|
|
|
|
struct qseecom_control {
|
|
struct list_node registered_listener_list_head;
|
|
mutex_t registered_listener_list_lock;
|
|
|
|
struct list_node registered_app_list_head;
|
|
mutex_t registered_app_list_lock;
|
|
|
|
uint32_t qseos_version;
|
|
uint32_t qsee_version;
|
|
int handle;
|
|
bool commonlib_loaded;
|
|
mutex_t global_data_lock;
|
|
uint32_t cmnlib_loaded;
|
|
uint32_t qseecom_init_done;
|
|
uint32_t qseecom_tz_init_done;
|
|
};
|
|
|
|
struct qseecom_listener_handle {
|
|
uint32_t id;
|
|
};
|
|
|
|
static struct qseecom_reg_log_buf_ireq logbuf_req;
|
|
static struct qseecom_control qseecom;
|
|
static int __qseecom_process_incomplete_cmd(struct qseecom_command_scm_resp *resp,
|
|
struct qseecom_client_listener_data_irsp *send_data_rsp);
|
|
|
|
/*
|
|
* Appsbl runs in Aarch32 when this is ported for Aarch64,
|
|
* change return type for uint64_t.
|
|
*/
|
|
static uint32_t __qseecom_uvirt_to_kphys(uint64_t virt)
|
|
{
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
return (uint32_t)platform_get_virt_to_phys_mapping((addr_t)virt);
|
|
}
|
|
|
|
static int _disp_log_stats(struct tzdbg_log_t *log, uint32_t log_len,
|
|
uint32_t startOffset, uint32_t endOffset)
|
|
{
|
|
uint32_t MaxBufSize = 0;
|
|
uint32_t LogBufSize = 0;
|
|
uint32_t LogBufFirstHalf = 0;
|
|
uint32_t len = 0;
|
|
char *pCurPos, *pPrintPos = NULL;
|
|
void *pLogBuf = NULL;
|
|
int ret = GENERIC_ERROR;
|
|
|
|
MaxBufSize = QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t);
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
if (startOffset < endOffset)
|
|
{
|
|
LogBufSize = endOffset - startOffset;
|
|
pLogBuf = malloc(LogBufSize);
|
|
if (NULL == pLogBuf)
|
|
{
|
|
ret = GENERIC_ERROR;
|
|
dprintf(CRITICAL, "Failed to alloc buffer to print TZ Log:%u\n", LogBufSize);
|
|
goto err;
|
|
}
|
|
memset(pLogBuf, 0, LogBufSize);
|
|
memscpy(pLogBuf, LogBufSize, (char *)((uint32_t)log->log_buf + startOffset), LogBufSize);
|
|
}
|
|
else if ( endOffset < startOffset)
|
|
{
|
|
LogBufSize = MaxBufSize - (startOffset - endOffset);
|
|
LogBufFirstHalf = MaxBufSize - startOffset;
|
|
pLogBuf = malloc(LogBufSize);
|
|
if (NULL == pLogBuf)
|
|
{
|
|
ret = GENERIC_ERROR;
|
|
dprintf(CRITICAL, "Failed to alloc buffer to print TZ Log:%u\n", LogBufSize);
|
|
goto err;
|
|
}
|
|
memset(pLogBuf, 0, LogBufSize);
|
|
memscpy(pLogBuf, LogBufSize, (char *)((uint32_t)log->log_buf + startOffset), LogBufFirstHalf);
|
|
memscpy((char *)((uint32_t)pLogBuf+ LogBufFirstHalf), (LogBufSize - LogBufFirstHalf), log->log_buf, endOffset);
|
|
}
|
|
else //endOffset == startOffset
|
|
{
|
|
ret = 0;
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* Read from ring buff while there is data and space in return buff
|
|
*/
|
|
pCurPos = pLogBuf;
|
|
pPrintPos = pCurPos;
|
|
while (len < LogBufSize)
|
|
{
|
|
//QSEE separate each line by "\r \n"
|
|
if ((*pCurPos == '\r')&&(*(pCurPos+1) == '\n'))
|
|
{
|
|
//update the line to dump
|
|
*pCurPos = '\0';
|
|
len++;
|
|
pCurPos++;
|
|
*pCurPos = '\0';
|
|
len++;
|
|
pCurPos++;
|
|
dprintf(ALWAYS, "%s\n", pPrintPos);
|
|
pPrintPos = pCurPos;
|
|
continue;
|
|
}
|
|
len++;
|
|
pCurPos++;
|
|
}
|
|
ret = 0;
|
|
free(pLogBuf);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
static int allocate_extra_arg_buffer(uint32_t fn_id, struct scm_desc *desc)
|
|
{
|
|
int i;
|
|
int ret = GENERIC_ERROR;
|
|
scmcall_arg arg = {0};
|
|
scmcall_ret ret_arg = {0};
|
|
int arglen = 0;
|
|
|
|
if (!desc) {
|
|
dprintf(CRITICAL, "%s: Invalid input\n", __func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
arglen = desc->arginfo & 0xf;
|
|
|
|
dprintf(SPEW, "%s:fn_id:%u, desc->arginfo:%u desc->args[0]:%u desc->args[1]:%u desc->args[2]:%u desc->args[3]:%u desc->args[4]:%u\n",
|
|
__func__, fn_id, desc->arginfo, desc->args[0], desc->args[1], desc->args[2], desc->args[3], desc->args[4]);
|
|
|
|
arg.x0 = fn_id;
|
|
arg.x1 = desc->arginfo;
|
|
arg.x2 = desc->args[0];
|
|
arg.x3 = desc->args[1];
|
|
arg.x4 = desc->args[2];
|
|
|
|
if (arglen > FIRST_EXT_ARG_IDX) {
|
|
for (i = 0; i < (arglen - FIRST_EXT_ARG_IDX); i++) {
|
|
arg.x5[i] = desc->args[i + FIRST_EXT_ARG_IDX];
|
|
}
|
|
}
|
|
ret = scm_call2(&arg, &ret_arg);
|
|
desc->ret[0] = ret_arg.x1;
|
|
desc->ret[1] = ret_arg.x2;
|
|
desc->ret[2] = ret_arg.x3;
|
|
|
|
dprintf(SPEW, "%s:ret:%d, desc->ret[0]]:%u desc->ret[1]:%u desc->ret[2]:%u\n",
|
|
__func__, ret, desc->ret[0], desc->ret[1], desc->ret[2]);
|
|
return ret;
|
|
}
|
|
|
|
static int qseecom_scm_call2(uint32_t svc_id, uint32_t tz_cmd_id,
|
|
const void *req_buf, void *resp_buf)
|
|
{
|
|
int ret = 0;
|
|
uint32_t smc_id = 0;
|
|
uint32_t qseos_cmd_id = 0;
|
|
struct scm_desc desc = {0};
|
|
struct qseecom_command_scm_resp *scm_resp = NULL;
|
|
|
|
if (!req_buf || !resp_buf) {
|
|
dprintf(CRITICAL, "Invalid buffer pointer\n");
|
|
return GENERIC_ERROR;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
qseos_cmd_id = *(uint32_t *)req_buf;
|
|
scm_resp = (struct qseecom_command_scm_resp *)resp_buf;
|
|
|
|
switch (svc_id) {
|
|
case TZ_CALL: {
|
|
if (tz_cmd_id == 1) {
|
|
smc_id = TZ_INFO_IS_SVC_AVAILABLE_ID;
|
|
desc.arginfo = TZ_INFO_IS_SVC_AVAILABLE_ID_PARAM_ID;
|
|
desc.args[0] = TZ_INFO_GET_FEATURE_VERSION_ID;
|
|
} else if (tz_cmd_id == 3) {
|
|
smc_id = TZ_INFO_GET_FEATURE_VERSION_ID;
|
|
desc.arginfo = TZ_INFO_GET_FEATURE_VERSION_ID_PARAM_ID;
|
|
desc.args[0] = *(uint32_t *)req_buf;
|
|
}
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
|
|
case SCM_SVC_TZSCHEDULER: {
|
|
switch (qseos_cmd_id) {
|
|
case QSEE_APP_START_COMMAND: {
|
|
struct qseecom_load_app_ireq *req;
|
|
req = (struct qseecom_load_app_ireq *)req_buf;
|
|
smc_id = TZ_OS_APP_START_ID;
|
|
desc.arginfo = TZ_OS_APP_START_ID_PARAM_ID;
|
|
desc.args[0] = req->mdt_len;
|
|
desc.args[1] = req->img_len;
|
|
desc.args[2] = req->phy_addr;
|
|
dprintf(SPEW, "args[0]:%u args[1]:%u args[2]:%u\n",
|
|
desc.args[0], desc.args[1], desc.args[2]);
|
|
dprintf(SPEW, "mdt_len:%u img_len:%u phy_addr:%u\n",
|
|
req->mdt_len, req->img_len, req->phy_addr);
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
|
|
break;
|
|
}
|
|
case QSEE_APP_SHUTDOWN_COMMAND: {
|
|
struct qseecom_unload_app_ireq *req;
|
|
req = (struct qseecom_unload_app_ireq *)req_buf;
|
|
smc_id = TZ_OS_APP_SHUTDOWN_ID;
|
|
desc.arginfo = TZ_OS_APP_SHUTDOWN_ID_PARAM_ID;
|
|
desc.args[0] = req->app_id;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_APP_REGION_NOTIFICATION: {
|
|
struct qsee_apps_region_info_ireq *req;
|
|
req = (struct qsee_apps_region_info_ireq *)req_buf;
|
|
smc_id = TZ_OS_APP_REGION_NOTIFICATION_ID;
|
|
desc.arginfo =
|
|
TZ_OS_APP_REGION_NOTIFICATION_ID_PARAM_ID;
|
|
desc.args[0] = req->addr;
|
|
desc.args[1] = req->size;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_LOAD_SERV_IMAGE_COMMAND: {
|
|
struct qseecom_load_app_ireq *req;
|
|
req = (struct qseecom_load_app_ireq *)req_buf;
|
|
smc_id = TZ_OS_LOAD_SERVICES_IMAGE_ID;
|
|
desc.arginfo = TZ_OS_LOAD_SERVICES_IMAGE_ID_PARAM_ID;
|
|
desc.args[0] = req->mdt_len;
|
|
desc.args[1] = req->img_len;
|
|
desc.args[2] = req->phy_addr;
|
|
dprintf(SPEW, "QSEE_LOAD_SERV_IMAGE_COMMAND mdt_len:%u img_len:%u phy_addr:%u\n",
|
|
req->mdt_len, req->img_len, req->phy_addr);
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_UNLOAD_SERV_IMAGE_COMMAND: {
|
|
smc_id = TZ_OS_UNLOAD_SERVICES_IMAGE_ID;
|
|
desc.arginfo = TZ_OS_UNLOAD_SERVICES_IMAGE_ID_PARAM_ID;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_REGISTER_LISTENER: {
|
|
struct qseecom_register_listener_ireq *req;
|
|
req = (struct qseecom_register_listener_ireq *)req_buf;
|
|
smc_id = TZ_OS_REGISTER_LISTENER_ID;
|
|
desc.arginfo =
|
|
TZ_OS_REGISTER_LISTENER_ID_PARAM_ID;
|
|
desc.args[0] = req->listener_id;
|
|
desc.args[1] = req->sb_ptr;
|
|
desc.args[2] = req->sb_len;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_DEREGISTER_LISTENER: {
|
|
struct qseecom_unregister_listener_ireq *req;
|
|
req = (struct qseecom_unregister_listener_ireq *)
|
|
req_buf;
|
|
smc_id = TZ_OS_DEREGISTER_LISTENER_ID;
|
|
desc.arginfo = TZ_OS_DEREGISTER_LISTENER_ID_PARAM_ID;
|
|
desc.args[0] = req->listener_id;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_LISTENER_DATA_RSP_COMMAND: {
|
|
struct qseecom_client_listener_data_irsp *req;
|
|
req = (struct qseecom_client_listener_data_irsp *)
|
|
req_buf;
|
|
smc_id = TZ_OS_LISTENER_RESPONSE_HANDLER_ID;
|
|
desc.arginfo =
|
|
TZ_OS_LISTENER_RESPONSE_HANDLER_ID_PARAM_ID;
|
|
desc.args[0] = req->listener_id;
|
|
desc.args[1] = req->status;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_CLIENT_SEND_DATA_COMMAND: {
|
|
struct qseecom_client_send_data_ireq *req;
|
|
req = (struct qseecom_client_send_data_ireq *)req_buf;
|
|
smc_id = TZ_APP_QSAPP_SEND_DATA_ID;
|
|
desc.arginfo = TZ_APP_QSAPP_SEND_DATA_ID_PARAM_ID;
|
|
desc.args[0] = req->app_id;
|
|
desc.args[1] = req->req_ptr;
|
|
desc.args[2] = req->req_len;
|
|
desc.args[3] = req->rsp_ptr;
|
|
desc.args[4] = req->rsp_len;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_RPMB_PROVISION_KEY_COMMAND: {
|
|
struct qseecom_client_send_service_ireq *req;
|
|
req = (struct qseecom_client_send_service_ireq *)
|
|
req_buf;
|
|
smc_id = TZ_OS_RPMB_PROVISION_KEY_ID;
|
|
desc.arginfo = TZ_OS_RPMB_PROVISION_KEY_ID_PARAM_ID;
|
|
desc.args[0] = req->key_type;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
case QSEE_RPMB_ERASE_COMMAND: {
|
|
smc_id = TZ_OS_RPMB_ERASE_ID;
|
|
desc.arginfo = TZ_OS_RPMB_ERASE_ID_PARAM_ID;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
|
|
case QSEE_REGISTER_LOG_BUF_COMMAND: {
|
|
struct qseecom_reg_log_buf_ireq *req;
|
|
req = (struct qseecom_reg_log_buf_ireq *)req_buf;
|
|
smc_id = TZ_OS_REGISTER_LOG_BUFFER_ID;
|
|
desc.arginfo = TZ_OS_REGISTER_LOG_BUFFER_ID_PARAM_ID;
|
|
desc.args[0] = req->phy_addr;
|
|
desc.args[1] = req->len;
|
|
ret = allocate_extra_arg_buffer(smc_id, &desc);
|
|
break;
|
|
}
|
|
default: {
|
|
dprintf(CRITICAL, "qseos_cmd_id 0x%d is not supported by armv8 scm_call2.\n",
|
|
qseos_cmd_id);
|
|
ret = GENERIC_ERROR;
|
|
break;
|
|
}
|
|
} /*end of switch (qsee_cmd_id) */
|
|
break;
|
|
} /*end of case SCM_SVC_TZSCHEDULER*/
|
|
default: {
|
|
dprintf(CRITICAL, "svc_id 0x%x is not supported by armv8 scm_call2.\n",
|
|
svc_id);
|
|
ret = GENERIC_ERROR;
|
|
break;
|
|
}
|
|
} /*end of switch svc_id */
|
|
scm_resp->result = desc.ret[0];
|
|
scm_resp->resp_type = desc.ret[1];
|
|
scm_resp->data = desc.ret[2];
|
|
dprintf(SPEW, "svc_id = 0x%x, tz_cmd_id = 0x%x, qseos_cmd_id = 0x%x, smc_id = 0x%x, param_id = 0x%x\n",
|
|
svc_id, tz_cmd_id, qseos_cmd_id, smc_id, desc.arginfo);
|
|
dprintf(SPEW, "scm_resp->result = 0x%x, scm_resp->resp_type = 0x%x, scm_resp->data = 0x%x\n",
|
|
scm_resp->result, scm_resp->resp_type, scm_resp->data);
|
|
return ret;
|
|
}
|
|
|
|
static int qseecom_scm_call(uint32_t svc_id, uint32_t tz_cmd_id, void *cmd_buf,
|
|
size_t cmd_len, void *resp_buf, size_t resp_len)
|
|
{
|
|
void *req = NULL;
|
|
struct qseecom_command_scm_resp *resp = NULL;
|
|
struct qseecom_client_listener_data_irsp send_data_rsp = {0};
|
|
int ret = GENERIC_ERROR;
|
|
uint32_t qseos_cmd_id = 0;
|
|
|
|
if ((!cmd_buf) || (!resp_buf))
|
|
return GENERIC_ERROR;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
mutex_acquire(&qseecom.registered_app_list_lock);
|
|
req = cmd_buf;
|
|
qseos_cmd_id = *(uint32_t *)req;
|
|
resp = (struct qseecom_command_scm_resp *) resp_buf;
|
|
|
|
do {
|
|
if (!is_scm_armv8_support()) {
|
|
ret = scm_call(svc_id, tz_cmd_id, req, cmd_len,
|
|
resp_buf, resp_len);
|
|
} else {
|
|
ret = qseecom_scm_call2(svc_id, tz_cmd_id, req, resp);
|
|
}
|
|
|
|
if (ret) {
|
|
dprintf(CRITICAL, "ERROR: scm_call to load failed : ret %d\n", ret);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
if (svc_id == TZ_CALL) {
|
|
goto err;
|
|
}
|
|
|
|
switch (resp->result) {
|
|
case QSEOS_RESULT_SUCCESS:
|
|
if(((resp->resp_type != QSEOS_APP_ID) || (resp->data <= 0)) &&
|
|
((qseos_cmd_id == QSEE_CLIENT_SEND_DATA_COMMAND) ||
|
|
(qseos_cmd_id == QSEE_LISTENER_DATA_RSP_COMMAND)))
|
|
{
|
|
dprintf(CRITICAL, "ERROR: Resp type %d or Resp Data %d incorrect\n",
|
|
resp->resp_type, resp->data);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
goto err;
|
|
case QSEOS_RESULT_FAILURE:
|
|
dprintf(CRITICAL, "scm call failed w/response result%d\n", resp->result);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
case QSEOS_RESULT_INCOMPLETE:
|
|
if(resp->resp_type != QSEOS_LISTENER_ID)
|
|
{
|
|
ret = GENERIC_ERROR;
|
|
dprintf(CRITICAL, "Listener service incorrect resp->result:%d resp->resp_type:%d\n",
|
|
resp->result, resp->resp_type);
|
|
goto err;
|
|
}
|
|
__qseecom_process_incomplete_cmd(resp, &send_data_rsp);
|
|
req = (void *)&send_data_rsp;
|
|
qseos_cmd_id = QSEE_LISTENER_DATA_RSP_COMMAND;
|
|
break;
|
|
default:
|
|
dprintf(CRITICAL, "scm call return unknown response %d\n", resp->result);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
} while(true);
|
|
|
|
err:
|
|
mutex_release(&qseecom.registered_app_list_lock);
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int __qseecom_process_incomplete_cmd(struct qseecom_command_scm_resp *resp,
|
|
struct qseecom_client_listener_data_irsp *send_data_rsp)
|
|
{
|
|
int ret = 0;
|
|
struct qseecom_registered_listener_list *entry;
|
|
|
|
if ((!resp) || (!send_data_rsp))
|
|
{
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
|
|
list_for_every_entry(&qseecom.registered_listener_list_head,
|
|
entry, struct qseecom_registered_listener_list, node) {
|
|
if (resp->data == entry->svc.listener_id) {
|
|
arch_invalidate_cache_range((addr_t) entry->svc.virt_sb_base, entry->svc.sb_size);
|
|
entry->CallbackFn(entry->svc.virt_sb_base, entry->svc.sb_size);
|
|
arch_clean_invalidate_cache_range((addr_t) entry->svc.virt_sb_base, entry->svc.sb_size);
|
|
break;
|
|
}
|
|
}
|
|
send_data_rsp->qsee_cmd_id = QSEE_LISTENER_DATA_RSP_COMMAND;
|
|
send_data_rsp->listener_id = entry->svc.listener_id;
|
|
send_data_rsp->status = 0;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int __qseecom_load_app(const char *app_name, unsigned int *app_id)
|
|
{
|
|
int index = INVALID_PTN;
|
|
unsigned long long ptn = 0;
|
|
unsigned long long size = 0;
|
|
void *buf = NULL;
|
|
void *req = NULL;
|
|
struct qseecom_load_app_ireq load_req = {0};
|
|
struct qseecom_command_scm_resp resp;
|
|
struct tzdbg_log_t *log = NULL;
|
|
uint32_t QseeLogStart = 0;
|
|
uint32_t QseeLogNewStart = 0;
|
|
|
|
int ret = GENERIC_ERROR;
|
|
uint8_t lun = 0;
|
|
|
|
if (!app_name)
|
|
return GENERIC_ERROR;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
index = partition_get_index(app_name);
|
|
lun = partition_get_lun(index);
|
|
mmc_set_lun(lun);
|
|
|
|
size = partition_get_size(index);
|
|
|
|
buf = memalign(PAGE_SIZE, ROUNDUP(size, PAGE_SIZE));
|
|
if (!buf) {
|
|
dprintf(CRITICAL, "%s: Aloc failed for %s image\n",
|
|
__func__, app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
ptn = partition_get_offset(index);
|
|
if(ptn == 0) {
|
|
dprintf(CRITICAL, "ERROR: No %s found\n", app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
if (mmc_read(ptn, (unsigned int *) buf, size)) {
|
|
dprintf(CRITICAL, "ERROR: Cannot read %s image\n", app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
/* Currently on 8994 only 32-bit phy addr is supported
|
|
* Hence downcasting is okay
|
|
*/
|
|
load_req.phy_addr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t) buf);
|
|
load_req.qsee_cmd_id = QSEE_APP_START_COMMAND;
|
|
load_req.img_len = size;
|
|
load_req.mdt_len = 0;
|
|
dprintf(SPEW, "phy_addr:%u img_len:%u\n", load_req.phy_addr, load_req.img_len);
|
|
|
|
memscpy(&load_req.app_name, MAX_APP_NAME_SIZE, app_name, MAX_APP_NAME_SIZE);
|
|
req = (void *)&load_req;
|
|
|
|
log = (struct tzdbg_log_t *)logbuf_req.phy_addr;
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogStart = (uint32_t) log->log_pos.offset;
|
|
|
|
arch_clean_invalidate_cache_range((addr_t) load_req.phy_addr, load_req.img_len);
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, req,
|
|
sizeof(struct qseecom_load_lib_image_ireq),
|
|
&resp, sizeof(resp));
|
|
if(ret == 0)
|
|
*app_id = resp.data;
|
|
else
|
|
*app_id = 0;
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogNewStart = (uint32_t) log->log_pos.offset;
|
|
|
|
_disp_log_stats((struct tzdbg_log_t *) logbuf_req.phy_addr, QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t),
|
|
QseeLogStart, QseeLogNewStart);
|
|
err:
|
|
if (buf)
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static int qseecom_load_commonlib_image(char * app_name)
|
|
{
|
|
int index = INVALID_PTN;
|
|
unsigned long long ptn = 0;
|
|
unsigned long long size = 0;
|
|
void *buf = NULL;
|
|
void *req = NULL;
|
|
struct qseecom_load_app_ireq load_req = {0};
|
|
struct qseecom_command_scm_resp resp = {0};
|
|
int ret = GENERIC_ERROR;
|
|
uint8_t lun = 0;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
index = partition_get_index(app_name);
|
|
lun = partition_get_lun(index);
|
|
mmc_set_lun(lun);
|
|
|
|
size = partition_get_size(index);
|
|
|
|
buf = memalign(PAGE_SIZE, ROUNDUP(size, PAGE_SIZE));
|
|
if (!buf) {
|
|
dprintf(CRITICAL, "%s: Aloc failed for %s image\n",
|
|
__func__, app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
ptn = partition_get_offset(index);
|
|
if(ptn == 0) {
|
|
dprintf(CRITICAL, "ERROR: No %s found\n", app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
if (mmc_read(ptn, (unsigned int *) buf, size)) {
|
|
dprintf(CRITICAL, "ERROR: Cannot read %s image\n", app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
/* Currently on 8994 only 32-bit phy addr is supported
|
|
* Hence downcasting is okay
|
|
*/
|
|
load_req.phy_addr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t) buf);
|
|
load_req.qsee_cmd_id = QSEE_LOAD_SERV_IMAGE_COMMAND;
|
|
load_req.img_len = size;
|
|
load_req.mdt_len = 0;
|
|
|
|
memscpy(load_req.app_name, MAX_APP_NAME_SIZE, app_name, MAX_APP_NAME_SIZE);
|
|
req = (void *)&load_req;
|
|
|
|
arch_clean_invalidate_cache_range((addr_t) load_req.phy_addr, load_req.img_len);
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, req,
|
|
sizeof(struct qseecom_load_lib_image_ireq),
|
|
&resp, sizeof(resp));
|
|
if(ret == 0)
|
|
ret = resp.data;
|
|
|
|
err:
|
|
if (buf)
|
|
free(buf);
|
|
return ret;
|
|
}
|
|
|
|
static int qseecom_unload_commonlib_image(void)
|
|
{
|
|
int ret = GENERIC_ERROR;
|
|
struct qseecom_unload_lib_image_ireq unload_req = {0};
|
|
struct qseecom_command_scm_resp resp;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
/* Populate the remaining parameters */
|
|
unload_req.qsee_cmd_id = QSEE_UNLOAD_SERV_IMAGE_COMMAND;
|
|
/* SCM_CALL to load the image */
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &unload_req,
|
|
sizeof(struct qseecom_unload_lib_image_ireq),
|
|
&resp, sizeof(resp));
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* This function is called with the global
|
|
* data mutex acquired.
|
|
*/
|
|
static struct qseecom_registered_app_list *
|
|
__qseecom_add_app_entry(char *app_name, uint32_t app_id)
|
|
{
|
|
struct qseecom_registered_app_list *entry = NULL;
|
|
int32_t ret = GENERIC_ERROR;
|
|
|
|
if ((!app_name) || (app_id == 0)) {
|
|
dprintf(CRITICAL, "%s: Invalid Input\n", __func__);
|
|
return NULL;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
entry = malloc(sizeof(*entry));
|
|
if (!entry) {
|
|
dprintf(CRITICAL, "malloc for app entry failed\n");
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
entry->app_id = app_id;
|
|
entry->ref_cnt = 1;
|
|
strlcpy(entry->app_name, app_name, MAX_APP_NAME_SIZE);
|
|
|
|
dprintf(SPEW, "%s: Adding app:%s app_id:%u to list\n", __func__, entry->app_name, entry->app_id);
|
|
list_add_tail(&qseecom.registered_app_list_head, &entry->node);
|
|
ret = 0;
|
|
err:
|
|
if (entry && (ret < 0)) {
|
|
free(entry);
|
|
return NULL;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/*
|
|
* This function is called with the global
|
|
* data mutex acquired.
|
|
*/
|
|
static int
|
|
__qseecom_remove_app_entry(struct qseecom_registered_app_list *entry)
|
|
{
|
|
if (!entry) {
|
|
dprintf(CRITICAL, "%s: Invalid Input\n", __func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
list_delete(&entry->node);
|
|
free(entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function is called with the global
|
|
* data mutex acquired.
|
|
*/
|
|
struct qseecom_registered_listener_list *
|
|
__qseecom_check_listener_exists(uint32_t listener_id)
|
|
{
|
|
struct qseecom_registered_listener_list *entry = NULL;
|
|
bool listener_present = false;
|
|
|
|
if (!listener_id) {
|
|
dprintf(CRITICAL, "%s: Invalid Input\n", __func__);
|
|
return NULL;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
list_for_every_entry(&qseecom.registered_listener_list_head,
|
|
entry, struct qseecom_registered_listener_list, node) {
|
|
if (entry->svc.listener_id == listener_id) {
|
|
listener_present = true;
|
|
break;
|
|
}
|
|
}
|
|
if (listener_present)
|
|
return entry;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* This function is called with the global
|
|
* data mutex acquired.
|
|
*/
|
|
static struct qseecom_registered_app_list
|
|
*__qseecom_check_handle_exists(int handle)
|
|
{
|
|
struct qseecom_registered_app_list *entry;
|
|
bool app_present = false;
|
|
|
|
if (handle <= 0) {
|
|
dprintf(CRITICAL, "%s: Invalid Input\n", __func__);
|
|
return NULL;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
list_for_every_entry(&qseecom.registered_app_list_head,
|
|
entry, struct qseecom_registered_app_list, node) {
|
|
if (entry->handle == handle) {
|
|
app_present = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (app_present == true)
|
|
return entry;
|
|
else
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
static struct qseecom_registered_app_list *
|
|
__qseecom_check_app_exists(char *app_name)
|
|
{
|
|
struct qseecom_registered_app_list *entry = NULL;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
list_for_every_entry(&qseecom.registered_app_list_head,
|
|
entry, struct qseecom_registered_app_list, node) {
|
|
if (!strncmp(app_name, entry->app_name, 32)) {
|
|
dprintf(SPEW, "%s: app_name:%s\n", __func__, app_name);
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int qseecom_unload_app(uint32_t app_id)
|
|
{
|
|
int ret = 0;
|
|
struct qseecom_command_scm_resp resp;
|
|
struct qseecom_unload_app_ireq req;
|
|
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
/* Populate the structure for sending scm call to load image */
|
|
req.qsee_cmd_id = QSEE_APP_SHUTDOWN_COMMAND;
|
|
req.app_id = app_id;
|
|
|
|
/* SCM_CALL to unload the app */
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
|
|
sizeof(struct qseecom_unload_app_ireq),
|
|
&resp, sizeof(resp));
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int __qseecom_send_cmd(uint32_t app_id, struct qseecom_send_cmd_req *req)
|
|
{
|
|
int ret = 0;
|
|
struct qseecom_client_send_data_ireq send_data_req;
|
|
struct qseecom_command_scm_resp resp;
|
|
void *buf = NULL;
|
|
void *rsp_buf_temp = NULL;
|
|
uint32_t size = 0;
|
|
|
|
if (req->cmd_req_buf == NULL || req->resp_buf == NULL) {
|
|
dprintf(CRITICAL, "%s: cmd buffer or response buffer is null\n",
|
|
__func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
if (req->cmd_req_len > (UINT_MAX - req->resp_len)) {
|
|
dprintf(CRITICAL, "%s:Integer overflow\n", __func__);
|
|
dprintf(CRITICAL, "req->cmd_req_len: %u\n", req->cmd_req_len);
|
|
dprintf(CRITICAL, "req->resp_len: %u\n", req->resp_len);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
if ((req->cmd_req_len + req->resp_len) > (RPMB_SND_RCV_BUF_SZ * 1024 * 1024)) {
|
|
dprintf(CRITICAL, "%s:Cmd + Rsp len greater than TA buf\n", __func__);
|
|
dprintf(CRITICAL, "req->cmd_req_len: %u\n", req->cmd_req_len);
|
|
dprintf(CRITICAL, "req->resp_len: %u\n", req->resp_len);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
/* The req rsp buffer will be xPU protected by TZ during a TZ APP call
|
|
* This will still be protected during a listener call and there is a
|
|
* possibility of prefetching happening, which will cause xPU violation.
|
|
* Hence using (device memory with xN set) to prevent I or D prefetching.
|
|
* This is a contiguous region of 1MB used only for this, hence will not
|
|
* free this.
|
|
*/
|
|
buf = (void *)RPMB_SND_RCV_BUF;
|
|
if (!buf) {
|
|
dprintf(CRITICAL, "%s: Aloc failed for app_id:%d of size:%d\n",
|
|
__func__, app_id, size);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
send_data_req.qsee_cmd_id = QSEE_CLIENT_SEND_DATA_COMMAND;
|
|
send_data_req.app_id = app_id;
|
|
|
|
/* Currently on 8994 only 32-bit phy addr is supported
|
|
* Hence downcasting is okay
|
|
*/
|
|
send_data_req.req_ptr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t) buf);
|
|
send_data_req.req_len = req->cmd_req_len;
|
|
size = ROUNDUP(req->cmd_req_len, PAGE_SIZE);
|
|
rsp_buf_temp = (uint8_t *)buf + size;
|
|
send_data_req.rsp_ptr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t)rsp_buf_temp);
|
|
send_data_req.rsp_len = req->resp_len;
|
|
|
|
memscpy(buf, (RPMB_SND_RCV_BUF_SZ * 1024 * 1024), req->cmd_req_buf, req->cmd_req_len);
|
|
memscpy(rsp_buf_temp, req->resp_len, req->resp_buf, req->resp_len);
|
|
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
|
|
(void *)&send_data_req,
|
|
sizeof(send_data_req), (void *)&resp, sizeof(resp));
|
|
|
|
memscpy(req->cmd_req_buf, req->cmd_req_len, (void *)buf, send_data_req.req_len);
|
|
memscpy(req->resp_buf, req->resp_len, (void *)rsp_buf_temp, send_data_req.rsp_len);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Start a Secure App
|
|
*
|
|
* @param char* app_name
|
|
* App name of the Secure App to be started
|
|
*
|
|
* @return int
|
|
* Success: handle to be used for all calls to
|
|
* Secure app. Always greater than zero.
|
|
* Failure: Error code (negative only).
|
|
*/
|
|
int qseecom_start_app(char *app_name)
|
|
{
|
|
int32_t ret = GENERIC_ERROR;
|
|
int handle = 0;
|
|
struct qseecom_registered_app_list *entry = NULL;
|
|
unsigned int app_id = 0;
|
|
|
|
if (!app_name) {
|
|
dprintf(CRITICAL, "%s: Input error\n", __func__);
|
|
goto err;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
if ((!qseecom.qseecom_init_done)
|
|
|| (!qseecom.qseecom_tz_init_done)){
|
|
dprintf(CRITICAL, "%s qseecom_init not done\n",
|
|
__func__);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
/* Load commonlib image*/
|
|
if (!qseecom.cmnlib_loaded) {
|
|
ret = qseecom_load_commonlib_image("cmnlib");
|
|
if (ret) {
|
|
mutex_release(&qseecom.global_data_lock);
|
|
dprintf(CRITICAL, "%s qseecom_load_commonlib_image failed with status:%d\n",
|
|
__func__, ret);
|
|
goto err;
|
|
}
|
|
dprintf(DEBUG, "Loading cmnlib done\n");
|
|
#if ENABLE_CMNLIB64_LOADING
|
|
ret = qseecom_load_commonlib_image("cmnlib64");
|
|
if (ret) {
|
|
dprintf(CRITICAL, "%s qseecom_load_commonlib_image failed with status:%d\n",
|
|
__func__, ret);
|
|
goto err;
|
|
}
|
|
dprintf(DEBUG, "Loading cmnlib64 done\n");
|
|
#endif
|
|
qseecom.cmnlib_loaded = 1;
|
|
}
|
|
/* Check if App already exits, if exits increase ref_cnt
|
|
* and return handle, else load the app from partition,
|
|
* call into TZ to load it, add to list and then return
|
|
* handle.
|
|
*/
|
|
|
|
entry = __qseecom_check_app_exists(app_name);
|
|
if (!entry) {
|
|
mutex_release(&qseecom.global_data_lock);
|
|
/* load the app and get the app_id */
|
|
dprintf(INFO, "%s: Loading app %s for the first time'\n",
|
|
__func__, app_name);
|
|
|
|
ret = __qseecom_load_app(app_name, &app_id);
|
|
if ((ret < 0) || (app_id ==0)) {
|
|
dprintf(CRITICAL, "%s: __qseecom_load_app failed with err:%d for app:%s\n",
|
|
__func__, ret, app_name);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
entry = __qseecom_add_app_entry(app_name, app_id);
|
|
if (!entry)
|
|
{
|
|
dprintf(CRITICAL, "%s: __qseecom_add_app_entry failed\n", __func__);
|
|
ret = GENERIC_ERROR;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
goto err;
|
|
}
|
|
qseecom.handle++;
|
|
entry->handle = qseecom.handle;
|
|
handle = entry->handle;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
}
|
|
else {
|
|
entry->ref_cnt++;
|
|
handle = entry->handle;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
}
|
|
return handle;
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Shutdown a Secure App
|
|
*
|
|
* @param int handle
|
|
* Handle of the Secure App to be shutdown
|
|
*
|
|
* @return int
|
|
* Status:
|
|
* 0 - Success
|
|
* Negative value indicates failure.
|
|
*/
|
|
int qseecom_shutdown_app(int handle)
|
|
{
|
|
int ret = GENERIC_ERROR;
|
|
int ref_cnt = 0;
|
|
struct qseecom_registered_app_list *entry = NULL;
|
|
struct tzdbg_log_t *log = NULL;
|
|
uint32_t QseeLogStart = 0;
|
|
uint32_t QseeLogNewStart = 0;
|
|
|
|
if (handle <= 0) {
|
|
dprintf(CRITICAL, "%s: Invalid Handle %d\n", __func__, handle);
|
|
goto err;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
if ((!qseecom.qseecom_init_done)
|
|
|| (!qseecom.qseecom_tz_init_done)) {
|
|
dprintf(CRITICAL, "%s qseecom_init not done\n",
|
|
__func__);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
entry = __qseecom_check_handle_exists(handle);
|
|
if (!entry) {
|
|
dprintf(CRITICAL, "%s: Shutdown on an app that was never loaded handle:%d\n",
|
|
__func__, handle);
|
|
ret = GENERIC_ERROR;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
goto err;
|
|
}
|
|
|
|
/* Decrement ref_cnt by 1, if ref_cnt is 0 after
|
|
* decrementing unload the app by calling into
|
|
* TZ else just return.
|
|
*/
|
|
|
|
if(entry->ref_cnt != 0)
|
|
entry->ref_cnt--;
|
|
ref_cnt = entry->ref_cnt;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
if (ref_cnt == 0) {
|
|
log = (struct tzdbg_log_t *)logbuf_req.phy_addr;
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogStart = (uint32_t) log->log_pos.offset;
|
|
|
|
ret = qseecom_unload_app(entry->app_id);
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogNewStart = (uint32_t) log->log_pos.offset;
|
|
|
|
_disp_log_stats((struct tzdbg_log_t *) logbuf_req.phy_addr, QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t),
|
|
QseeLogStart, QseeLogNewStart);
|
|
if(ret) {
|
|
dprintf(CRITICAL, "%s: qseecom_unload_app failed with err:%d for handle:%d\n",
|
|
__func__, ret, handle);
|
|
goto err;
|
|
}
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
ret = __qseecom_remove_app_entry(entry);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
if(ret) {
|
|
dprintf(CRITICAL, "%s: __qseecom_remove_app_entry failed with err:%d for handle:%d\n",
|
|
__func__, ret, handle);
|
|
goto err;
|
|
}
|
|
}
|
|
ret = 0;
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Send cmd to a Secure App
|
|
*
|
|
* @param int handle
|
|
* Handle of the Secure App to send the cmd
|
|
*
|
|
* @param void *send_buf
|
|
* Pointer to the App request buffer
|
|
*
|
|
* @param uint32_t sbuf_len
|
|
* Size of the request buffer
|
|
*
|
|
* @param void *resp_buf
|
|
* Pointer to the App response buffer
|
|
*
|
|
* @param uint32_t rbuf_len
|
|
* Size of the response buffer
|
|
*
|
|
* @return int
|
|
* Status:
|
|
* 0 - Success
|
|
* Negative value indicates failure.
|
|
*/
|
|
int qseecom_send_command(int handle, void *send_buf,
|
|
uint32_t sbuf_len, void *resp_buf, uint32_t rbuf_len)
|
|
{
|
|
int ret = GENERIC_ERROR;
|
|
uint32_t app_id = 0;
|
|
struct qseecom_registered_app_list *entry = NULL;
|
|
struct qseecom_send_cmd_req req = {0, 0, 0, 0};
|
|
struct tzdbg_log_t *log = NULL;
|
|
uint32_t QseeLogStart = 0;
|
|
uint32_t QseeLogNewStart = 0;
|
|
|
|
if (handle <= 0) {
|
|
dprintf(CRITICAL, "%s Handle is Invalid\n", __func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
if((!send_buf) || (!resp_buf)) {
|
|
dprintf(CRITICAL, "%s: Input Buffers invalid\n", __func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
if ((!qseecom.qseecom_init_done)
|
|
|| (!qseecom.qseecom_tz_init_done)) {
|
|
dprintf(CRITICAL, "%s qseecom_init not done\n",
|
|
__func__);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
entry = __qseecom_check_handle_exists(handle);
|
|
if (!entry) {
|
|
dprintf(CRITICAL, "%s: Send cmd on an app that was never loaded handle:%d\n",
|
|
__func__, handle);
|
|
ret = GENERIC_ERROR;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
goto err;
|
|
}
|
|
|
|
app_id = entry->app_id;
|
|
mutex_release(&qseecom.global_data_lock);
|
|
|
|
req.cmd_req_len = sbuf_len;
|
|
req.resp_len = rbuf_len;
|
|
req.cmd_req_buf = send_buf;
|
|
req.resp_buf = resp_buf;
|
|
|
|
log = (struct tzdbg_log_t *)logbuf_req.phy_addr;
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogStart = (uint32_t) log->log_pos.offset;
|
|
|
|
ret = __qseecom_send_cmd(app_id, &req);
|
|
if (ret) {
|
|
dprintf(CRITICAL, "%s __qseecom_send_cmd failed with err:%d for handle:%d\n",
|
|
__func__, ret, handle);
|
|
goto err;
|
|
}
|
|
arch_invalidate_cache_range((addr_t) logbuf_req.phy_addr, logbuf_req.len);
|
|
QseeLogNewStart = (uint32_t) log->log_pos.offset;
|
|
|
|
_disp_log_stats((struct tzdbg_log_t *) logbuf_req.phy_addr, QSEE_LOG_BUF_SIZE - sizeof(struct tzdbg_log_pos_t),
|
|
QseeLogStart, QseeLogNewStart);
|
|
ret = 0;
|
|
dprintf(SPEW, "sending cmd_req->rsp size: %u, ptr: 0x%p\n",
|
|
req.resp_len, req.resp_buf);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Registers a Listener Service with QSEE
|
|
*
|
|
* @param uint32_t listnr_id
|
|
* Pre-defined Listener ID to be registered
|
|
*
|
|
* @param uint32_t sb_size
|
|
* Shared buffer size required for the listener
|
|
* service.
|
|
*
|
|
* @return int
|
|
* Status:
|
|
* 0 - Success
|
|
* Negative value indicates failure.
|
|
*/
|
|
int qseecom_register_listener(struct qseecom_listener_services *listnr)
|
|
{
|
|
int ret = GENERIC_ERROR;
|
|
struct qseecom_registered_listener_list *new_entry = NULL;
|
|
struct qseecom_register_listener_ireq req;
|
|
struct qseecom_command_scm_resp resp;
|
|
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
if (!qseecom.qseecom_init_done) {
|
|
dprintf(CRITICAL, "%s qseecom_init not done\n",
|
|
__func__);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
mutex_release(&qseecom.global_data_lock);
|
|
|
|
mutex_acquire(&qseecom.registered_listener_list_lock);
|
|
|
|
if ((!listnr)) {
|
|
dprintf(CRITICAL, "%s Invalid Input listnr\n", __func__);
|
|
return GENERIC_ERROR;
|
|
}
|
|
|
|
if ((!listnr->id) || (!listnr->sb_size) || (!listnr->service_cmd_handler)) {
|
|
dprintf(CRITICAL, "%s Invalid Input listnr_id:%d sb_size:%d\n",
|
|
__func__, listnr->id, listnr->sb_size);
|
|
return GENERIC_ERROR;
|
|
}
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
new_entry = __qseecom_check_listener_exists(listnr->id);
|
|
if (new_entry) {
|
|
dprintf(CRITICAL, "Service is not unique and is already registered\n");
|
|
ret = LISTENER_ALREADY_PRESENT_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
new_entry = malloc(sizeof(*new_entry));
|
|
if (!new_entry) {
|
|
dprintf(CRITICAL, "%s new_entry malloc failed for size:%d\n", __func__, sizeof(*new_entry));
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
memset(new_entry, 0, sizeof(*new_entry));
|
|
new_entry->svc.listener_id = listnr->id;
|
|
new_entry->svc.sb_size = listnr->sb_size;
|
|
new_entry->CallbackFn = listnr->service_cmd_handler;
|
|
|
|
new_entry->svc.virt_sb_base = memalign(PAGE_SIZE, ROUNDUP(listnr->sb_size, PAGE_SIZE));
|
|
if (!new_entry->svc.virt_sb_base) {
|
|
dprintf(CRITICAL, "%s virt_sb_base malloc failed for size:%d\n", __func__, listnr->sb_size);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
memset(new_entry->svc.virt_sb_base, 0, ROUNDUP(listnr->sb_size, PAGE_SIZE));
|
|
arch_clean_invalidate_cache_range((addr_t) new_entry->svc.virt_sb_base, ROUNDUP(listnr->sb_size, PAGE_SIZE));
|
|
|
|
req.qsee_cmd_id = QSEE_REGISTER_LISTENER;
|
|
req.listener_id = new_entry->svc.listener_id;
|
|
req.sb_len = new_entry->svc.sb_size;
|
|
/* convert to 32bit addr for tz */
|
|
req.sb_ptr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t) new_entry->svc.virt_sb_base);
|
|
|
|
resp.result = QSEOS_RESULT_INCOMPLETE;
|
|
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
|
|
sizeof(req), &resp, sizeof(resp));
|
|
if (ret) {
|
|
dprintf(CRITICAL, "qseecom_scm_call failed with err: %d\n", ret);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
/* Add entry to Listener list */
|
|
list_add_tail(&qseecom.registered_listener_list_head, &new_entry->node);
|
|
err:
|
|
if ((ret) &&
|
|
(ret != LISTENER_ALREADY_PRESENT_ERROR)) {
|
|
if ((new_entry) &&
|
|
(new_entry->svc.virt_sb_base))
|
|
free(new_entry->svc.virt_sb_base);
|
|
if (new_entry)
|
|
free(new_entry);
|
|
}
|
|
mutex_release(&qseecom.registered_listener_list_lock);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* De-Registers a Listener Service with QSEE
|
|
*
|
|
* @param uint32_t listnr_id
|
|
* Pre-defined Listener ID to be de-registered
|
|
*
|
|
* @return int
|
|
* Status:
|
|
* 0 - Success
|
|
* Negative value indicates failure.
|
|
*/
|
|
int qseecom_deregister_listener(uint32_t listnr_id)
|
|
{
|
|
int ret = GENERIC_ERROR;
|
|
struct qseecom_registered_listener_list *new_entry = NULL;
|
|
struct qseecom_unregister_listener_ireq req;
|
|
struct qseecom_command_scm_resp resp;
|
|
|
|
mutex_acquire(&qseecom.global_data_lock);
|
|
if (!qseecom.qseecom_init_done) {
|
|
dprintf(CRITICAL, "%s qseecom_init not done\n",
|
|
__func__);
|
|
mutex_release(&qseecom.global_data_lock);
|
|
return ret;
|
|
}
|
|
mutex_release(&qseecom.global_data_lock);
|
|
|
|
mutex_acquire(&qseecom.registered_listener_list_lock);
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
new_entry = __qseecom_check_listener_exists(listnr_id);
|
|
if (!new_entry) {
|
|
dprintf(CRITICAL, "Service not present\n");
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
req.qsee_cmd_id = QSEE_DEREGISTER_LISTENER;
|
|
req.listener_id = listnr_id;
|
|
resp.result = QSEOS_RESULT_INCOMPLETE;
|
|
|
|
ret = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1, &req,
|
|
sizeof(req), &resp, sizeof(resp));
|
|
if (ret) {
|
|
dprintf(CRITICAL, "scm_call() failed with err: %d (lstnr id=%d)\n",
|
|
ret, req.listener_id);
|
|
ret = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
|
|
list_delete(&new_entry->node);
|
|
|
|
err:
|
|
if (ret == 0) {
|
|
if (new_entry)
|
|
free(new_entry);
|
|
}
|
|
mutex_release(&qseecom.registered_listener_list_lock);
|
|
return ret;
|
|
}
|
|
|
|
int qseecom_tz_init()
|
|
{
|
|
struct qsee_apps_region_info_ireq req;
|
|
struct qseecom_command_scm_resp resp;
|
|
int rc = GENERIC_ERROR;
|
|
/* register log buffer scm request */
|
|
void *buf = NULL;
|
|
/* Register app region with TZ */
|
|
req.qsee_cmd_id = QSEE_APP_REGION_NOTIFICATION;
|
|
req.addr = APP_REGION_ADDR;
|
|
req.size = APP_REGION_SIZE;
|
|
dprintf(ALWAYS, "secure app region addr=0x%x size=0x%x",
|
|
req.addr, req.size);
|
|
rc = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
|
|
&req, sizeof(req),
|
|
&resp, sizeof(resp));
|
|
dprintf(ALWAYS, "TZ App region notif returned with status:%d addr:%x size:%d\n",
|
|
rc, req.addr, req.size);
|
|
if (rc)
|
|
goto err;
|
|
buf = memalign(PAGE_SIZE, ROUNDUP(QSEE_LOG_BUF_SIZE, PAGE_SIZE));
|
|
if (!buf) {
|
|
rc = GENERIC_ERROR;
|
|
goto err;
|
|
}
|
|
memset(buf, 0, ROUNDUP(QSEE_LOG_BUF_SIZE, PAGE_SIZE));
|
|
/* Make sure the buffer given to TZ is flushed */
|
|
arch_clean_invalidate_cache_range((addr_t) buf, QSEE_LOG_BUF_SIZE);
|
|
logbuf_req.qsee_cmd_id = QSEE_REGISTER_LOG_BUF_COMMAND;
|
|
logbuf_req.phy_addr = (uint32_t)__qseecom_uvirt_to_kphys((uint32_t) buf);
|
|
logbuf_req.len = QSEE_LOG_BUF_SIZE;
|
|
|
|
rc = qseecom_scm_call(SCM_SVC_TZSCHEDULER, 1,
|
|
&logbuf_req, sizeof(logbuf_req),
|
|
&resp, sizeof(resp));
|
|
dprintf(ALWAYS, "TZ App log region register returned with status:%d addr:%x size:%d\n",
|
|
rc, logbuf_req.phy_addr, logbuf_req.len);
|
|
if (rc)
|
|
goto err;
|
|
err:
|
|
if (!rc) {
|
|
qseecom.qseecom_tz_init_done = 1;
|
|
dprintf(ALWAYS, "Qseecom TZ Init Done in Appsbl\n");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int qseecom_init()
|
|
{
|
|
int rc = GENERIC_ERROR;
|
|
|
|
memset (&qseecom, 0, sizeof(struct qseecom_control));
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
mutex_init(&(qseecom.global_data_lock));
|
|
mutex_init(&(qseecom.registered_app_list_lock));
|
|
mutex_init(&(qseecom.registered_listener_list_lock));
|
|
|
|
list_initialize(&(qseecom.registered_app_list_head));
|
|
list_initialize(&(qseecom.registered_listener_list_head));
|
|
|
|
qseecom.qseos_version = QSEOS_VERSION_14;
|
|
rc = 0;
|
|
|
|
if (!rc) {
|
|
qseecom.qseecom_init_done = 1;
|
|
dprintf(ALWAYS, "Qseecom Init Done in Appsbl\n");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int qseecom_exit()
|
|
{
|
|
dprintf(SPEW, "%s called\n", __func__);
|
|
|
|
if (logbuf_req.phy_addr)
|
|
free((void *)logbuf_req.phy_addr);
|
|
qseecom.qseecom_init_done = 0;
|
|
dprintf(ALWAYS, "Qseecom De-Init Done in Appsbl\n");
|
|
return 0;
|
|
}
|