mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
The HW supports only storing 15 keys at a time. This limits the number of tfms that can work without failutes. Reserve keyslots to solve this and use the reserved ones during the encryption/decryption operation. This allow users to have the capability of hardware protected keys and faster operations if there are limited number of tfms while not halting the operation if there are more tfms. Bug 4883011 Change-Id: I220f1e8205dde1f078be6ed4cb09b699b6d5dfa2 Signed-off-by: Akhil R <akhilrajeev@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3263283 GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com> Reviewed-by: Brad Griffis <bgriffis@nvidia.com>
178 lines
4.4 KiB
C
178 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
// SPDX-FileCopyrightText: Copyright (c) 2023-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
/*
|
|
* Crypto driver file to manage keys of NVIDIA Security Engine.
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/module.h>
|
|
#include <crypto/aes.h>
|
|
|
|
#include "tegra-se.h"
|
|
|
|
#define SE_KEY_FULL_MASK GENMASK(SE_MAX_KEYSLOT, 0)
|
|
|
|
/* Reserve keyslot 0, 14, 15 */
|
|
#define SE_KEY_RSVD_MASK (BIT(0) | BIT(14) | BIT(15))
|
|
#define SE_KEY_VALID_MASK (SE_KEY_FULL_MASK & ~SE_KEY_RSVD_MASK)
|
|
|
|
/* Mutex lock to guard keyslots */
|
|
static DEFINE_MUTEX(kslt_lock);
|
|
|
|
/* Keyslot bitmask (0 = available, 1 = in use/not available) */
|
|
static u16 tegra_se_keyslots = SE_KEY_RSVD_MASK;
|
|
|
|
static u16 tegra_keyslot_alloc(void)
|
|
{
|
|
u16 keyid;
|
|
|
|
mutex_lock(&kslt_lock);
|
|
/* Check if all key slots are full */
|
|
if (tegra_se_keyslots == GENMASK(SE_MAX_KEYSLOT, 0)) {
|
|
mutex_unlock(&kslt_lock);
|
|
return 0;
|
|
}
|
|
|
|
keyid = ffz(tegra_se_keyslots);
|
|
tegra_se_keyslots |= BIT(keyid);
|
|
|
|
mutex_unlock(&kslt_lock);
|
|
|
|
return keyid;
|
|
}
|
|
|
|
static void tegra_keyslot_free(u16 slot)
|
|
{
|
|
mutex_lock(&kslt_lock);
|
|
tegra_se_keyslots &= ~(BIT(slot));
|
|
mutex_unlock(&kslt_lock);
|
|
}
|
|
|
|
static unsigned int tegra_key_prep_ins_cmd(struct tegra_se *se, u32 *cpuvaddr,
|
|
const u32 *key, u32 keylen, u16 slot, u32 alg)
|
|
{
|
|
int i = 0, j;
|
|
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->op);
|
|
cpuvaddr[i++] = SE_AES_OP_WRSTALL | SE_AES_OP_DUMMY;
|
|
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->manifest);
|
|
cpuvaddr[i++] = se->manifest(se->owner, alg, keylen);
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->key_dst);
|
|
|
|
cpuvaddr[i++] = SE_AES_KEY_DST_INDEX(slot);
|
|
|
|
for (j = 0; j < keylen / 4; j++) {
|
|
/* Set key address */
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->key_addr);
|
|
cpuvaddr[i++] = j;
|
|
|
|
/* Set key data */
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->key_data);
|
|
cpuvaddr[i++] = key[j];
|
|
}
|
|
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->config);
|
|
cpuvaddr[i++] = SE_CFG_INS;
|
|
|
|
cpuvaddr[i++] = host1x_opcode_setpayload(1);
|
|
cpuvaddr[i++] = se_host1x_opcode_incr_w(se->hw->regs->op);
|
|
cpuvaddr[i++] = SE_AES_OP_WRSTALL | SE_AES_OP_START |
|
|
SE_AES_OP_LASTBUF;
|
|
|
|
cpuvaddr[i++] = se_host1x_opcode_nonincr(host1x_uclass_incr_syncpt_r(), 1);
|
|
cpuvaddr[i++] = host1x_uclass_incr_syncpt_cond_f(1) |
|
|
host1x_uclass_incr_syncpt_indx_f(se->syncpt_id);
|
|
|
|
dev_dbg(se->dev, "key-slot %u key-manifest %#x\n",
|
|
slot, se->manifest(se->owner, alg, keylen));
|
|
|
|
return i;
|
|
}
|
|
|
|
static bool tegra_key_in_kslt(u32 keyid)
|
|
{
|
|
bool ret;
|
|
|
|
if (keyid > SE_MAX_KEYSLOT)
|
|
return false;
|
|
|
|
mutex_lock(&kslt_lock);
|
|
ret = ((BIT(keyid) & SE_KEY_VALID_MASK) &&
|
|
(BIT(keyid) & tegra_se_keyslots));
|
|
mutex_unlock(&kslt_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_key_insert(struct tegra_se *se, const u8 *key,
|
|
u32 keylen, u16 slot, u32 alg)
|
|
{
|
|
const u32 *keyval = (u32 *)key;
|
|
u32 *addr = se->keybuf->addr, size;
|
|
int ret;
|
|
|
|
mutex_lock(&kslt_lock);
|
|
size = tegra_key_prep_ins_cmd(se, addr, keyval, keylen, slot, alg);
|
|
ret = tegra_se_host1x_submit(se, se->keybuf, size);
|
|
mutex_unlock(&kslt_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void tegra_key_invalidate(struct tegra_se *se, u32 keyid, u32 alg)
|
|
{
|
|
u8 zkey[AES_MAX_KEY_SIZE] = {0};
|
|
|
|
if (!keyid)
|
|
return;
|
|
|
|
/* Overwrite the key with 0s */
|
|
tegra_key_insert(se, zkey, AES_MAX_KEY_SIZE, keyid, alg);
|
|
|
|
tegra_keyslot_free(keyid);
|
|
}
|
|
|
|
int tegra_key_submit(struct tegra_se *se, const u8 *key, u32 keylen, u32 alg, u32 *keyid)
|
|
{
|
|
int ret;
|
|
|
|
/* Use the existing slot if it is already allocated */
|
|
if (!tegra_key_in_kslt(*keyid)) {
|
|
*keyid = tegra_keyslot_alloc();
|
|
if (!(*keyid)) {
|
|
dev_dbg(se->dev, "failed to allocate key slot\n");
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
ret = tegra_key_insert(se, key, keylen, *keyid, alg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void tegra_key_invalidate_reserved(struct tegra_se *se, u32 keyid, u32 alg)
|
|
{
|
|
u8 zkey[AES_MAX_KEY_SIZE] = {0};
|
|
|
|
if (!keyid)
|
|
return;
|
|
|
|
/* Overwrite the key with 0s */
|
|
tegra_key_insert(se, zkey, AES_MAX_KEY_SIZE, keyid, alg);
|
|
}
|
|
|
|
inline int tegra_key_submit_reserved(struct tegra_se *se, const u8 *key,
|
|
u32 keylen, u32 alg, u32 *keyid)
|
|
{
|
|
return tegra_key_insert(se, key, keylen, *keyid, alg);
|
|
}
|