// SPDX-License-Identifier: GPL-2.0-only // SPDX-FileCopyrightText: Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. /* * Crypto driver file to manage keys of NVIDIA Security Engine. */ #include #include #include #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->cmdbuf->addr, size; size = tegra_key_prep_ins_cmd(se, addr, keyval, keylen, slot, alg); return tegra_se_host1x_submit(se, size); } 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_err(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; }