kernel_samsung_a34x-permissive/drivers/misc/mediatek/cmdq/mailbox/cmdq-sec-helper.c
2024-04-28 15:51:13 +02:00

353 lines
9 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "cmdq-util.h"
#include "cmdq-sec.h"
#include "cmdq-sec-mailbox.h"
#define ADDR_METADATA_MAX_COUNT_ORIGIN (8)
#define CMDQ_IMMEDIATE_VALUE (0)
#define CMDQ_REG_TYPE (1)
#define CMDQ_PREDUMP_TIMEOUT_MS 200
static s32 cmdq_sec_realloc_addr_list(struct cmdq_pkt *pkt, const u32 count)
{
struct cmdq_sec_data *sec_data =
(struct cmdq_sec_data *)pkt->sec_data;
void *prev = (void *)(unsigned long)sec_data->addrMetadatas, *curr;
if (count <= sec_data->addrMetadataMaxCount)
return 0;
curr = kcalloc(count, sizeof(*sec_data), GFP_KERNEL);
if (!curr)
return -ENOMEM;
if (count && sec_data->addrMetadatas)
memcpy(curr, prev,
sizeof(*sec_data) * sec_data->addrMetadataMaxCount);
kfree(prev);
sec_data->addrMetadatas = (u64)curr;
sec_data->addrMetadataMaxCount = count;
return 0;
}
static s32 cmdq_sec_check_sec(struct cmdq_pkt *pkt)
{
struct cmdq_sec_data *sec_data;
if (pkt->sec_data)
return 0;
sec_data = kzalloc(sizeof(*sec_data), GFP_KERNEL);
if (!sec_data)
return -ENOMEM;
pkt->sec_data = (void *)sec_data;
return 0;
}
static s32 cmdq_sec_append_metadata(
struct cmdq_pkt *pkt, const enum CMDQ_IWC_ADDR_METADATA_TYPE type,
const u64 base, const u32 offset, const u32 size, const u32 port, uint32_t sec_id)
{
struct cmdq_sec_data *sec_data;
struct cmdq_sec_addr_meta *meta;
s32 idx, max, ret;
cmdq_log("pkt:%p type:%u base:%#llx offset:%#x size:%#x port:%#x sec_id:%d",
pkt, type, base, offset, size, port, sec_id);
cmdq_msg("%s pkt:%p type:%u base:%#llx offset:%#x size:%#x port:%#x sec_id:%d",
__func__, pkt, type, base, offset, size, port, sec_id);
ret = cmdq_sec_check_sec(pkt);
if (ret < 0)
return ret;
sec_data = (struct cmdq_sec_data *)pkt->sec_data;
idx = sec_data->addrMetadataCount;
if (idx >= CMDQ_IWC_MAX_ADDR_LIST_LENGTH) {
cmdq_err("idx:%u reach over:%u",
idx, CMDQ_IWC_MAX_ADDR_LIST_LENGTH);
return -EFAULT;
}
if (!sec_data->addrMetadataMaxCount)
max = ADDR_METADATA_MAX_COUNT_ORIGIN;
else if (idx >= sec_data->addrMetadataMaxCount)
max = sec_data->addrMetadataMaxCount * 2;
else
max = sec_data->addrMetadataMaxCount;
ret = cmdq_sec_realloc_addr_list(pkt, max);
if (ret)
return ret;
if (!sec_data->addrMetadatas) {
cmdq_log("addrMetadatas is missing");
meta = kzalloc(sizeof(*meta), GFP_KERNEL);
if (!meta)
return -ENOMEM;
sec_data->addrMetadatas = (u64)(void *)meta;
}
meta = (struct cmdq_sec_addr_meta *)
(unsigned long)sec_data->addrMetadatas;
meta[idx].instrIndex = pkt->cmd_buf_size / CMDQ_INST_SIZE - 1;
meta[idx].type = type;
meta[idx].baseHandle = base;
meta[idx].offset = offset;
meta[idx].size = size;
meta[idx].port = port;
meta[idx].useSecIdinMeta = 1;
meta[idx].sec_id = sec_id;
sec_data->addrMetadataCount += 1;
return 0;
}
s32 cmdq_sec_pkt_set_data(struct cmdq_pkt *pkt, const u64 dapc_engine,
const u64 port_sec_engine, const enum CMDQ_SEC_SCENARIO scenario,
const enum cmdq_sec_meta_type meta_type)
{
struct cmdq_sec_data *sec_data;
s32 ret;
if (!pkt) {
cmdq_err("invalid pkt:%p", pkt);
return -EINVAL;
}
ret = cmdq_sec_check_sec(pkt);
if (ret < 0)
return ret;
cmdq_log(
"pkt:%p sec_data:%p dapc:%llu port_sec:%llu scen:%u",
pkt, pkt->sec_data, dapc_engine, port_sec_engine, scenario);
sec_data = (struct cmdq_sec_data *)pkt->sec_data;
sec_data->enginesNeedDAPC |= dapc_engine;
sec_data->enginesNeedPortSecurity |= port_sec_engine;
sec_data->scenario = scenario;
sec_data->client_meta_type = meta_type;
return 0;
}
EXPORT_SYMBOL(cmdq_sec_pkt_set_data);
void cmdq_sec_pkt_set_mtee(struct cmdq_pkt *pkt, const bool enable, const int32_t sec_id)
{
struct cmdq_sec_data *sec_data =
(struct cmdq_sec_data *)pkt->sec_data;
sec_data->mtee = enable;
sec_data->sec_id = sec_id;
cmdq_msg("%s pkt:%p mtee:%d sec_id:%d\n",
__func__, pkt, ((struct cmdq_sec_data *)pkt->sec_data)->mtee,
((struct cmdq_sec_data *)pkt->sec_data)->sec_id);
}
EXPORT_SYMBOL(cmdq_sec_pkt_set_mtee);
void cmdq_sec_pkt_free_data(struct cmdq_pkt *pkt)
{
if (pkt->sec_data == NULL)
return;
kfree((void *)((struct cmdq_sec_data *)pkt->sec_data)->addrMetadatas);
kfree(pkt->sec_data);
}
EXPORT_SYMBOL(cmdq_sec_pkt_free_data);
s32 cmdq_sec_pkt_set_payload(struct cmdq_pkt *pkt, u8 idx,
const u32 meta_size, u32 *meta)
{
struct cmdq_sec_data *sec_data;
s32 ret;
if (idx == 0) {
cmdq_err("not allow set reserved payload 0");
return -EINVAL;
}
if (!meta_size || !meta) {
cmdq_err("not allow empty size or buffer");
return -EINVAL;
}
ret = cmdq_sec_check_sec(pkt);
if (ret < 0)
return ret;
if (idx == CMDQ_IWC_MSG1 &&
meta_size >= sizeof(struct iwcCmdqMessageEx_t)) {
cmdq_err("not enough size payload 1:%u msg size:%zu",
meta_size, sizeof(struct iwcCmdqMessageEx_t));
return -EINVAL;
}
if (idx == CMDQ_IWC_MSG2 &&
meta_size >= sizeof(struct iwcCmdqMessageEx2_t)) {
cmdq_err("not enough size payload 2:%u msg size:%zu",
meta_size, sizeof(struct iwcCmdqMessageEx2_t));
return -EINVAL;
}
sec_data = (struct cmdq_sec_data *)pkt->sec_data;
sec_data->client_meta_size[idx] = meta_size;
sec_data->client_meta[idx] = meta;
return 0;
}
EXPORT_SYMBOL(cmdq_sec_pkt_set_payload);
s32 cmdq_sec_pkt_write_reg(struct cmdq_pkt *pkt, u32 addr, u64 base,
const enum CMDQ_IWC_ADDR_METADATA_TYPE type,
const u32 offset, const u32 size, const u32 port, uint32_t sec_id)
{
s32 ret;
ret = cmdq_pkt_assign_command(pkt, CMDQ_SPR_FOR_TEMP, addr);
if (ret)
return ret;
ret = cmdq_pkt_append_command(pkt,
base & 0xffff, base >> 16, CMDQ_SPR_FOR_TEMP, 0,
CMDQ_IMMEDIATE_VALUE, CMDQ_IMMEDIATE_VALUE, CMDQ_REG_TYPE,
CMDQ_CODE_WRITE_S);
if (ret)
return ret;
/* check boundary size and append at first before append metadata */
if (unlikely(!pkt->avail_buf_size)) {
if (cmdq_pkt_add_cmd_buffer(pkt) < 0)
return -ENOMEM;
}
return cmdq_sec_append_metadata(pkt, type, base, offset, size, port, sec_id);
}
EXPORT_SYMBOL(cmdq_sec_pkt_write_reg);
s32 cmdq_sec_pkt_assign_metadata(struct cmdq_pkt *pkt,
u32 count, void *meta_array)
{
struct cmdq_sec_data *data;
void *pkt_meta_array;
size_t size;
s32 ret;
if (!count)
return -EINVAL;
ret = cmdq_sec_check_sec(pkt);
if (ret < 0)
return ret;
data = (struct cmdq_sec_data *)pkt->sec_data;
size = count * sizeof(struct cmdq_sec_addr_meta);
pkt_meta_array = kzalloc(size, GFP_KERNEL);
if (!pkt_meta_array)
return -ENOMEM;
memcpy(pkt_meta_array, meta_array, size);
data->addrMetadatas = (unsigned long)pkt_meta_array;
data->addrMetadataCount = count;
return 0;
}
EXPORT_SYMBOL(cmdq_sec_pkt_assign_metadata);
void cmdq_sec_dump_secure_data(struct cmdq_pkt *pkt)
{
struct cmdq_sec_data *data;
struct cmdq_sec_addr_meta *meta;
s32 i;
u64 *inst;
if (!pkt || !pkt->sec_data) {
cmdq_msg("pkt without sec_data");
return;
}
data = (struct cmdq_sec_data *)pkt->sec_data;
cmdq_util_msg(
"meta cnt:%u addr:%#llx max:%u scen:%d dapc:%#llx port:%#llx wait:%d reset:%d metatype:%u",
data->addrMetadataCount, data->addrMetadatas,
data->addrMetadataMaxCount, data->scenario,
data->enginesNeedDAPC, data->enginesNeedPortSecurity,
data->waitCookie, data->resetExecCnt,
(u32)data->client_meta_type);
meta = (struct cmdq_sec_addr_meta *)(unsigned long)
data->addrMetadatas;
for (i = 0; i < data->addrMetadataCount; i++) {
inst = cmdq_pkt_get_va_by_offset(pkt,
meta[i].instrIndex * CMDQ_INST_SIZE);
cmdq_util_msg(
"meta:%d instr:%u type:%u base:%#llx block:%u ofst:%u size:%u port:%u inst:%#018llx",
i, meta[i].instrIndex, meta[i].type,
meta[i].baseHandle, meta[i].blockOffset, meta[i].offset,
meta[i].size, meta[i].port,
inst ? *inst : 0);
}
}
EXPORT_SYMBOL(cmdq_sec_dump_secure_data);
int cmdq_sec_pkt_wait_complete(struct cmdq_pkt *pkt)
{
struct cmdq_client *client = pkt->cl;
unsigned long ret;
u8 cnt = 0;
s32 thread_id = cmdq_sec_mbox_chan_id(client->chan);
#if IS_ENABLED(CONFIG_MMPROFILE)
cmdq_sec_mmp_wait(client->chan, pkt);
#endif
cmdq_sec_mbox_enable(client->chan);
do {
ret = wait_for_completion_timeout(&pkt->cmplt,
msecs_to_jiffies(CMDQ_PREDUMP_TIMEOUT_MS));
if (ret)
break;
cmdq_util_dump_lock();
cmdq_msg("===== SW timeout Pre-dump %hhu =====", cnt);
cnt++;
cmdq_dump_core(client->chan);
cmdq_msg("thd:%d Hidden thread info since it's secure",
thread_id);
cmdq_sec_dump_operation(client->chan);
cmdq_sec_dump_secure_thread_cookie(client->chan);
cmdq_dump_pkt(pkt, 0, false);
cmdq_sec_dump_notify_loop(client->chan);
cmdq_util_dump_unlock();
} while (1);
cmdq_sec_mbox_disable(client->chan);
#if IS_ENABLED(CONFIG_MMPROFILE)
cmdq_sec_mmp_wait_done(client->chan, pkt);
#endif
return 0;
}
EXPORT_SYMBOL(cmdq_sec_pkt_wait_complete);
void cmdq_sec_err_dump(struct cmdq_pkt *pkt, struct cmdq_client *client,
u64 **inst, const char **dispatch)
{
cmdq_sec_dump_operation(client->chan);
cmdq_sec_dump_secure_thread_cookie(client->chan);
cmdq_sec_dump_notify_loop(client->chan);
cmdq_sec_dump_secure_data(pkt);
cmdq_sec_dump_response(client->chan, pkt, inst, dispatch);
}
EXPORT_SYMBOL(cmdq_sec_err_dump);