tegrakernel/kernel/nvidia/drivers/crypto/tegra-se.c

4365 lines
113 KiB
C
Raw Normal View History

2022-02-16 09:13:02 -06:00
/*
* Cryptographic API.
* drivers/crypto/tegra-se.c
*
* Support for Tegra Security Engine hardware crypto algorithms.
*
* Copyright (c) 2011-2019, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/version.h>
#include <soc/tegra/fuse.h>
#include <soc/tegra/chip-id.h>
#include <soc/tegra/ahb.h>
#include <crypto/scatterwalk.h>
#include <soc/tegra/pmc.h>
#include <crypto/algapi.h>
#include <crypto/aes.h>
#include <crypto/akcipher.h>
#include <crypto/internal/rng.h>
#include <crypto/internal/hash.h>
#include <crypto/internal/akcipher.h>
#include <crypto/sha.h>
#include <linux/pm_runtime.h>
#include <linux/tegra_pm_domains.h>
#include <crypto/internal/kpp.h>
#include <crypto/kpp.h>
#include <crypto/dh.h>
#include "tegra-se.h"
#define DRIVER_NAME "tegra-se"
#if defined(CONFIG_PM)
static struct device *save_se_device;
#endif
enum tegra_se_dev_algo {
SE_DRBG,
SE_AES,
SE_CMAC,
SE_RSA,
SE_SHA,
NUM_SE_ALGO,
};
static struct tegra_se_dev *se_devices[NUM_SE_ALGO];
/* Security Engine operation modes */
enum tegra_se_aes_op_mode {
SE_AES_OP_MODE_CBC, /* Cipher Block Chaining (CBC) mode */
SE_AES_OP_MODE_ECB, /* Electronic Codebook (ECB) mode */
SE_AES_OP_MODE_CTR, /* Counter (CTR) mode */
SE_AES_OP_MODE_OFB, /* Output feedback (CFB) mode */
SE_AES_OP_MODE_RNG_X931, /* Random number generator (RNG) mode */
SE_AES_OP_MODE_RNG_DRBG, /* Deterministic Random Bit Generator */
SE_AES_OP_MODE_CMAC, /* Cipher-based MAC (CMAC) mode */
SE_AES_OP_MODE_SHA1, /* Secure Hash Algorithm-1 (SHA1) mode */
SE_AES_OP_MODE_SHA224, /* Secure Hash Algorithm-224 (SHA224) mode */
SE_AES_OP_MODE_SHA256, /* Secure Hash Algorithm-256 (SHA256) mode */
SE_AES_OP_MODE_SHA384, /* Secure Hash Algorithm-384 (SHA384) mode */
SE_AES_OP_MODE_SHA512 /* Secure Hash Algorithm-512 (SHA512) mode */
};
/* Security Engine key table type */
enum tegra_se_key_table_type {
SE_KEY_TABLE_TYPE_KEY, /* Key */
SE_KEY_TABLE_TYPE_ORGIV, /* Original IV */
SE_KEY_TABLE_TYPE_UPDTDIV /* Updated IV */
};
/* Security Engine request context */
struct tegra_se_req_context {
enum tegra_se_aes_op_mode op_mode; /* Security Engine operation mode */
bool encrypt; /* Operation type */
};
struct tegra_se_chipdata {
bool cprng_supported;
bool drbg_supported;
bool rsa_supported;
bool drbg_src_entropy_clk_enable;
bool const_freq;
unsigned long aes_freq;
unsigned long rng_freq;
unsigned long sha1_freq;
unsigned long sha224_freq;
unsigned long sha256_freq;
unsigned long sha384_freq;
unsigned long sha512_freq;
unsigned long rsa_freq;
bool mccif_supported;
bool rsa_key_rw_op;
u32 aes_keydata_reg_sz;
bool ahb_ack;
bool handle_sc7;
};
struct tegra_se_dev {
struct device *dev;
void __iomem *io_reg; /* se device memory/io */
int irq; /* irq allocated */
spinlock_t lock; /* spin lock */
struct clk *pclk; /* Security Engine clock */
struct clk *enclk; /* Entropy clock */
struct crypto_queue queue; /* Security Engine crypto queue */
struct tegra_se_slot *slot_list; /* pointer to key slots */
struct tegra_se_rsa_slot *rsa_slot_list; /* rsa key slot pointer */
u64 ctr;
u32 *src_ll_buf; /* pointer to source linked list buffer */
dma_addr_t src_ll_buf_adr; /* Source linked list buffer dma address */
u32 src_ll_size; /* Size of source linked list buffer */
u32 *dst_ll_buf; /* pointer to destination linked list buffer */
dma_addr_t dst_ll_buf_adr; /* Destination linked list dma address */
u32 dst_ll_size; /* Size of destination linked list buffer */
u32 *ctx_save_buf; /* LP context buffer pointer*/
dma_addr_t ctx_save_buf_adr; /* LP context buffer dma address*/
u32 *sg_in_buf;
dma_addr_t sg_in_buf_adr;
u32 *sg_out_buf;
dma_addr_t sg_out_buf_adr;
u32 *dh_buf1, *dh_buf2;
struct completion complete; /* Tells the task completion */
bool work_q_busy; /* Work queue busy status */
bool polling;
struct tegra_se_chipdata *chipdata; /* chip specific data */
u32 ahb_id;
};
static struct tegra_se_dev *sg_tegra_se_dev;
/* Security Engine AES context */
struct tegra_se_aes_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
struct tegra_se_slot *slot; /* Security Engine key slot */
u32 keylen; /* key length in bits */
u32 op_mode; /* AES operation mode */
};
/* Security Engine random number generator context */
struct tegra_se_rng_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
struct tegra_se_slot *slot; /* Security Engine key slot */
u32 *dt_buf; /* Destination buffer pointer */
dma_addr_t dt_buf_adr; /* Destination buffer dma address */
u32 *rng_buf; /* RNG buffer pointer */
dma_addr_t rng_buf_adr; /* RNG buffer dma address */
};
struct tegra_se_sha_zero_length_vector {
unsigned int size;
char *digest;
};
/* Security Engine SHA context */
struct tegra_se_sha_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
u32 op_mode; /* SHA operation mode */
u32 total_count; /* Total bytes in all the requests */
};
/* Security Engine AES CMAC context */
struct tegra_se_aes_cmac_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
struct tegra_se_slot *slot; /* Security Engine key slot */
u32 keylen; /* key length in bits */
u8 K1[TEGRA_SE_KEY_128_SIZE]; /* Key1 */
u8 K2[TEGRA_SE_KEY_128_SIZE]; /* Key2 */
dma_addr_t dma_addr; /* DMA address of local buffer */
u32 buflen; /* local buffer length */
u8 *buffer; /* local buffer pointer */
};
struct tegra_se_dh_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
struct tegra_se_rsa_slot *slot; /* Security Engine rsa key slot */
void *key;
void *p;
void *g;
unsigned int key_size;
unsigned int p_size;
unsigned int g_size;
};
/* Security Engine key slot */
struct tegra_se_slot {
struct list_head node;
u8 slot_num; /* Key slot number */
bool available; /* Tells whether key slot is free to use */
};
static struct tegra_se_slot ssk_slot = {
.slot_num = 15,
.available = false,
};
static struct tegra_se_slot srk_slot = {
.slot_num = 0,
.available = false,
};
/* Security Engine Linked List */
struct tegra_se_ll {
u32 addr; /* DMA buffer address */
u32 data_len; /* Data length in DMA buffer */
};
static LIST_HEAD(key_slot);
static LIST_HEAD(rsa_key_slot);
static DEFINE_SPINLOCK(rsa_key_slot_lock);
#define RSA_MIN_SIZE 64
#define RSA_MAX_SIZE 256
#define DISK_ENCR_BUF_SZ 512
#define RNG_RESEED_INTERVAL 0x00773594
#define TEGRA_SE_RSA_CONTEXT_SAVE_KEYSLOT_COUNT 2
#define MIN_DH_SZ_BITS 1536
static DEFINE_SPINLOCK(key_slot_lock);
static DEFINE_MUTEX(se_hw_lock);
/* create a work for handling the async transfers */
static void tegra_se_work_handler(struct work_struct *work);
static DECLARE_WORK(se_work, tegra_se_work_handler);
static struct workqueue_struct *se_work_q;
#define PMC_SCRATCH43_REG_OFFSET 0x22c
#define GET_MSB(x) ((x) >> (8 * sizeof(x) - 1))
static int force_reseed_count;
static void tegra_se_leftshift_onebit(u8 *in_buf, u32 size, u8 *org_msb)
{
u8 carry;
u32 i;
*org_msb = GET_MSB(in_buf[0]);
/* left shift one bit */
in_buf[0] <<= 1;
for (carry = 0, i = 1; i < size; i++) {
carry = GET_MSB(in_buf[i]);
in_buf[i - 1] |= carry;
in_buf[i] <<= 1;
}
}
static inline void se_writel(struct tegra_se_dev *se_dev,
unsigned int val, unsigned int reg_offset)
{
writel(val, se_dev->io_reg + reg_offset);
}
static inline unsigned int se_readl(struct tegra_se_dev *se_dev,
unsigned int reg_offset)
{
unsigned int val;
val = readl(se_dev->io_reg + reg_offset);
return val;
}
static void tegra_se_free_key_slot(struct tegra_se_slot *slot)
{
if (slot) {
spin_lock(&key_slot_lock);
slot->available = true;
spin_unlock(&key_slot_lock);
}
}
static struct tegra_se_slot *tegra_se_alloc_key_slot(void)
{
struct tegra_se_slot *slot = NULL;
bool found = false;
spin_lock(&key_slot_lock);
list_for_each_entry(slot, &key_slot, node) {
if (slot->available) {
slot->available = false;
found = true;
break;
}
}
spin_unlock(&key_slot_lock);
return found ? slot : NULL;
}
static int tegra_init_key_slot(struct tegra_se_dev *se_dev)
{
int i;
se_dev->slot_list = devm_kzalloc(se_dev->dev,
sizeof(struct tegra_se_slot) *
TEGRA_SE_KEYSLOT_COUNT, GFP_KERNEL);
if (!se_dev->slot_list)
return -ENOMEM;
spin_lock_init(&key_slot_lock);
spin_lock(&key_slot_lock);
for (i = 0; i < TEGRA_SE_KEYSLOT_COUNT; i++) {
/*
* Slot 0 and 15 are reserved and will not be added to the
* free slots pool. Slot 0 is used for SRK generation and
* Slot 15 is used for SSK operation
*/
if ((i == srk_slot.slot_num) || (i == ssk_slot.slot_num))
continue;
se_dev->slot_list[i].available = true;
se_dev->slot_list[i].slot_num = i;
INIT_LIST_HEAD(&se_dev->slot_list[i].node);
list_add_tail(&se_dev->slot_list[i].node, &key_slot);
}
spin_unlock(&key_slot_lock);
return 0;
}
static void tegra_se_key_read_disable(u8 slot_num)
{
struct tegra_se_dev *se_dev = sg_tegra_se_dev;
u32 val;
val = se_readl(se_dev,
(SE_KEY_TABLE_ACCESS_REG_OFFSET + (slot_num * 4)));
val &= ~(1 << SE_KEY_READ_DISABLE_SHIFT);
se_writel(se_dev,
val, (SE_KEY_TABLE_ACCESS_REG_OFFSET + (slot_num * 4)));
}
static void tegra_se_key_read_disable_all(void)
{
struct tegra_se_dev *se_dev = sg_tegra_se_dev;
u8 slot_num;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
for (slot_num = 0; slot_num < TEGRA_SE_KEYSLOT_COUNT; slot_num++)
tegra_se_key_read_disable(slot_num);
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
}
static void tegra_se_config_algo(struct tegra_se_dev *se_dev,
enum tegra_se_aes_op_mode mode, bool encrypt,
u32 key_len)
{
u32 val = 0;
switch (mode) {
case SE_AES_OP_MODE_CBC:
case SE_AES_OP_MODE_CMAC:
if (encrypt) {
val = SE_CONFIG_ENC_ALG(ALG_AES_ENC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
val |= SE_CONFIG_DEC_ALG(ALG_NOP);
} else {
val = SE_CONFIG_DEC_ALG(ALG_AES_DEC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_DEC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_DEC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_DEC_MODE(MODE_KEY128);
}
if (mode == SE_AES_OP_MODE_CMAC)
val |= SE_CONFIG_DST(DST_HASHREG);
else
val |= SE_CONFIG_DST(DST_MEMORY);
break;
case SE_AES_OP_MODE_RNG_X931:
case SE_AES_OP_MODE_RNG_DRBG:
val = SE_CONFIG_ENC_ALG(ALG_RNG) |
SE_CONFIG_ENC_MODE(MODE_KEY192) |
SE_CONFIG_DST(DST_MEMORY);
break;
case SE_AES_OP_MODE_ECB:
if (encrypt) {
val = SE_CONFIG_ENC_ALG(ALG_AES_ENC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
} else {
val = SE_CONFIG_DEC_ALG(ALG_AES_DEC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_DEC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_DEC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_DEC_MODE(MODE_KEY128);
}
val |= SE_CONFIG_DST(DST_MEMORY);
break;
case SE_AES_OP_MODE_CTR:
if (encrypt) {
val = SE_CONFIG_ENC_ALG(ALG_AES_ENC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
} else {
val = SE_CONFIG_DEC_ALG(ALG_AES_DEC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
}
val |= SE_CONFIG_DST(DST_MEMORY);
break;
case SE_AES_OP_MODE_OFB:
if (encrypt) {
val = SE_CONFIG_ENC_ALG(ALG_AES_ENC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
} else {
val = SE_CONFIG_DEC_ALG(ALG_AES_DEC);
if (key_len == TEGRA_SE_KEY_256_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
else if (key_len == TEGRA_SE_KEY_192_SIZE)
val |= SE_CONFIG_ENC_MODE(MODE_KEY192);
else
val |= SE_CONFIG_ENC_MODE(MODE_KEY128);
}
val |= SE_CONFIG_DST(DST_MEMORY);
break;
case SE_AES_OP_MODE_SHA1:
val = SE_CONFIG_ENC_ALG(ALG_SHA) |
SE_CONFIG_ENC_MODE(MODE_SHA1) |
SE_CONFIG_DST(DST_HASHREG);
break;
case SE_AES_OP_MODE_SHA224:
val = SE_CONFIG_ENC_ALG(ALG_SHA) |
SE_CONFIG_ENC_MODE(MODE_SHA224) |
SE_CONFIG_DST(DST_HASHREG);
break;
case SE_AES_OP_MODE_SHA256:
val = SE_CONFIG_ENC_ALG(ALG_SHA) |
SE_CONFIG_ENC_MODE(MODE_SHA256) |
SE_CONFIG_DST(DST_HASHREG);
break;
case SE_AES_OP_MODE_SHA384:
val = SE_CONFIG_ENC_ALG(ALG_SHA) |
SE_CONFIG_ENC_MODE(MODE_SHA384) |
SE_CONFIG_DST(DST_HASHREG);
break;
case SE_AES_OP_MODE_SHA512:
val = SE_CONFIG_ENC_ALG(ALG_SHA) |
SE_CONFIG_ENC_MODE(MODE_SHA512) |
SE_CONFIG_DST(DST_HASHREG);
break;
default:
dev_warn(se_dev->dev, "Invalid operation mode\n");
break;
}
se_writel(se_dev, val, SE_CONFIG_REG_OFFSET);
}
static void tegra_se_write_seed(struct tegra_se_dev *se_dev, u32 *pdata)
{
u32 i;
for (i = 0; i < SE_CRYPTO_CTR_REG_COUNT; i++)
se_writel(se_dev, pdata[i], SE_CRYPTO_CTR_REG_OFFSET + (i * 4));
}
static void tegra_se_write_key_table(u8 *pdata, u32 data_len, u8 slot_num,
enum tegra_se_key_table_type type)
{
struct tegra_se_dev *se_dev = se_devices[SE_AES];
u32 data_size;
u32 *pdata_buf = (u32 *)pdata;
u8 pkt = 0, quad = 0;
u32 val = 0, i;
if (!pdata_buf)
return;
if ((type == SE_KEY_TABLE_TYPE_KEY) &&
(slot_num == ssk_slot.slot_num))
return;
if (type == SE_KEY_TABLE_TYPE_ORGIV)
quad = QUAD_ORG_IV;
else if (type == SE_KEY_TABLE_TYPE_UPDTDIV)
quad = QUAD_UPDTD_IV;
else
quad = QUAD_KEYS_128;
/* write data to the key table */
if (se_dev->chipdata->aes_keydata_reg_sz == 128) {
data_size = SE_KEYTABLE_REG_MAX_DATA;
do {
for (i = 0; i < data_size; i += 4, data_len -= 4)
se_writel(se_dev, *pdata_buf++,
SE_KEYTABLE_DATA0_REG_OFFSET + i);
pkt = SE_KEYTABLE_SLOT(slot_num) |
SE_KEYTABLE_QUAD(quad);
val = SE_KEYTABLE_OP_TYPE(OP_WRITE) |
SE_KEYTABLE_TABLE_SEL(TABLE_KEYIV) |
SE_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_KEYTABLE_REG_OFFSET);
data_size = data_len;
quad = QUAD_KEYS_256;
} while (data_len);
} else {
data_size = SE_KEYTABLE_QUAD_SIZE_BYTES;
do {
pkt = SE_KEYTABLE_SLOT(slot_num) |
SE_KEYTABLE_QUAD(quad);
for (i = 0; i < data_size; i += 4, data_len -= 4) {
val = SE_KEYTABLE_PKT(pkt) |
SE_KEYTABLE_IV_WORD(i / 4);
se_writel(se_dev, val, SE_KEYTABLE_REG_OFFSET);
se_writel(se_dev, *pdata_buf++,
SE_KEYTABLE_DATA_REG_OFFSET);
}
data_size = data_len;
quad = QUAD_KEYS_256;
} while (data_len);
}
}
static void tegra_se_config_crypto(struct tegra_se_dev *se_dev,
enum tegra_se_aes_op_mode mode, bool encrypt,
u8 slot_num, bool org_iv)
{
u32 val = 0;
unsigned long freq = 0;
int err = 0;
switch (mode) {
case SE_AES_OP_MODE_CMAC:
case SE_AES_OP_MODE_CBC:
if (encrypt) {
val = SE_CRYPTO_INPUT_SEL(INPUT_AHB) |
SE_CRYPTO_VCTRAM_SEL(VCTRAM_AESOUT) |
SE_CRYPTO_XOR_POS(XOR_TOP) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
} else {
val = SE_CRYPTO_INPUT_SEL(INPUT_AHB) |
SE_CRYPTO_VCTRAM_SEL(VCTRAM_PREVAHB) |
SE_CRYPTO_XOR_POS(XOR_BOTTOM) |
SE_CRYPTO_CORE_SEL(CORE_DECRYPT);
}
freq = se_dev->chipdata->aes_freq;
break;
case SE_AES_OP_MODE_RNG_X931:
val = SE_CRYPTO_INPUT_SEL(INPUT_AHB) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
freq = se_dev->chipdata->rng_freq;
break;
case SE_AES_OP_MODE_RNG_DRBG:
val = SE_CRYPTO_INPUT_SEL(INPUT_RANDOM) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
if (tegra_get_chip_id() == TEGRA114)
val |= SE_CRYPTO_KEY_INDEX(slot_num);
freq = se_dev->chipdata->rng_freq;
break;
case SE_AES_OP_MODE_ECB:
if (encrypt) {
val = SE_CRYPTO_INPUT_SEL(INPUT_AHB) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
} else {
val = SE_CRYPTO_INPUT_SEL(INPUT_AHB) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_DECRYPT);
}
freq = se_dev->chipdata->aes_freq;
break;
case SE_AES_OP_MODE_CTR:
val = SE_CRYPTO_INPUT_SEL(INPUT_LNR_CTR) |
SE_CRYPTO_VCTRAM_SEL(VCTRAM_AHB) |
SE_CRYPTO_XOR_POS(XOR_BOTTOM) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
freq = se_dev->chipdata->aes_freq;
break;
case SE_AES_OP_MODE_OFB:
val = SE_CRYPTO_INPUT_SEL(INPUT_AESOUT) |
SE_CRYPTO_VCTRAM_SEL(VCTRAM_AHB) |
SE_CRYPTO_XOR_POS(XOR_BOTTOM) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
freq = se_dev->chipdata->aes_freq;
break;
default:
dev_warn(se_dev->dev, "Invalid operation mode\n");
break;
}
if (mode == SE_AES_OP_MODE_CTR) {
val |= SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(slot_num) |
SE_CRYPTO_CTR_CNTN(1);
} else {
val |= SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(slot_num) |
(org_iv ? SE_CRYPTO_IV_SEL(IV_ORIGINAL) :
SE_CRYPTO_IV_SEL(IV_UPDATED));
}
if (se_dev->pclk && !se_dev->chipdata->const_freq) {
err = clk_set_rate(se_dev->pclk, freq);
if (err) {
dev_err(se_dev->dev, "clock set_rate failed.\n");
return;
}
}
/* enable hash for CMAC */
if (mode == SE_AES_OP_MODE_CMAC)
val |= SE_CRYPTO_HASH(HASH_ENABLE);
if (se_dev->chipdata->mccif_supported)
val |= SE_CRYPTO_MEMIF(MEMIF_MCCIF);
else
val |= SE_CRYPTO_MEMIF(MEMIF_AHB);
se_writel(se_dev, val, SE_CRYPTO_REG_OFFSET);
if (mode == SE_AES_OP_MODE_RNG_DRBG) {
if (force_reseed_count <= 0) {
val = SE_RNG_CONFIG_MODE(DRBG_MODE_FORCE_RESEED) |
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY);
se_writel(se_dev, val, SE_RNG_CONFIG_REG_OFFSET);
force_reseed_count = RNG_RESEED_INTERVAL;
} else {
val = SE_RNG_CONFIG_MODE(DRBG_MODE_NORMAL) |
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY);
se_writel(se_dev, val, SE_RNG_CONFIG_REG_OFFSET);
}
--force_reseed_count;
se_writel(se_dev, RNG_RESEED_INTERVAL,
SE_RNG_RESEED_INTERVAL_REG_OFFSET);
}
if (mode == SE_AES_OP_MODE_CTR)
se_writel(se_dev, 1, SE_SPARE_0_REG_OFFSET);
if (mode == SE_AES_OP_MODE_OFB)
se_writel(se_dev, 1, SE_SPARE_0_REG_OFFSET);
}
static void tegra_se_config_sha(struct tegra_se_dev *se_dev, u32 count,
unsigned long freq)
{
int i;
int err = 0;
se_writel(se_dev, (count * 8), SE_SHA_MSG_LENGTH_REG_OFFSET);
se_writel(se_dev, (count * 8), SE_SHA_MSG_LEFT_REG_OFFSET);
for (i = 1; i < 4; i++) {
se_writel(se_dev, 0, SE_SHA_MSG_LENGTH_REG_OFFSET + (4 * i));
se_writel(se_dev, 0, SE_SHA_MSG_LEFT_REG_OFFSET + (4 * i));
}
if (se_dev->pclk && !se_dev->chipdata->const_freq) {
err = clk_set_rate(se_dev->pclk, freq);
if (err) {
dev_err(se_dev->dev, "clock set_rate failed.\n");
return;
}
}
se_writel(se_dev, SHA_ENABLE, SE_SHA_CONFIG_REG_OFFSET);
}
static int tegra_se_start_operation(struct tegra_se_dev *se_dev, u32 nbytes,
bool context_save, bool diff_dst)
{
u32 nblocks = nbytes / TEGRA_SE_AES_BLOCK_SIZE;
int ret = 0;
u32 val = 0, timeout = TEGRA_SE_TIMEOUT_1S;
if ((tegra_get_chip_id() == TEGRA114) &&
(nblocks > SE_MAX_LAST_BLOCK_SIZE)) {
dev_err(se_dev->dev, "nblocks out of range\n");
return -EDOM;
}
/* clear any pending interrupts */
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
se_writel(se_dev, val, SE_INT_STATUS_REG_OFFSET);
se_writel(se_dev, se_dev->src_ll_buf_adr, SE_IN_LL_ADDR_REG_OFFSET);
if (diff_dst)
se_writel(se_dev, se_dev->dst_ll_buf_adr,
SE_OUT_LL_ADDR_REG_OFFSET);
else
se_writel(se_dev, se_dev->src_ll_buf_adr,
SE_OUT_LL_ADDR_REG_OFFSET);
if (nblocks)
se_writel(se_dev, nblocks - 1, SE_BLOCK_COUNT_REG_OFFSET);
if (!se_dev->polling) {
/* enable interupts */
val = SE_INT_ERROR(INT_ENABLE) | SE_INT_OP_DONE(INT_ENABLE);
se_writel(se_dev, val, SE_INT_ENABLE_REG_OFFSET);
reinit_completion(&se_dev->complete);
}
if (context_save)
se_writel(se_dev, SE_OPERATION(OP_CTX_SAVE),
SE_OPERATION_REG_OFFSET);
else
se_writel(se_dev, SE_OPERATION(OP_START),
SE_OPERATION_REG_OFFSET);
if (se_dev->polling) {
/* polling */
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
while (!SE_OP_DONE(val, OP_DONE))
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
} else {
ret = wait_for_completion_timeout(&se_dev->complete,
msecs_to_jiffies(1000));
if (ret == 0) {
dev_err(se_dev->dev, "operation timed out no interrupt\n");
return -ETIMEDOUT;
}
}
if (se_dev->chipdata->ahb_ack) {
/* Ensure data is out from SE using MEM_INTERFACE signal */
val = se_readl(se_dev, SE_STATUS_REG_OFFSET);
while (val & SE_STATUS_MEM_INTERFACE(MEM_INTERFACE_BUSY)) {
if (!timeout) {
dev_err(se_dev->dev, "mem operation timeout\n");
return -ETIMEDOUT;
}
udelay(1);
timeout--;
val = se_readl(se_dev, SE_STATUS_REG_OFFSET);
}
timeout = TEGRA_SE_TIMEOUT_1S;
while (tegra_ahb_is_mem_wrque_busy(se_dev->ahb_id)) {
if (!timeout) {
dev_err(se_dev->dev, "mem operation timeout\n");
return -ETIMEDOUT;
}
udelay(1);
timeout--;
}
}
return 0;
}
static void tegra_se_read_hash_result(struct tegra_se_dev *se_dev,
u8 *pdata, u32 nbytes, bool swap32)
{
u32 *result = (u32 *)pdata;
u32 i;
for (i = 0; i < nbytes / 4; i++) {
result[i] = se_readl(se_dev, SE_HASH_RESULT_REG_OFFSET +
(i * sizeof(u32)));
if (swap32)
result[i] = be32_to_cpu(result[i]);
}
}
static int tegra_map_sg(struct device *dev, struct scatterlist *sg,
unsigned int nents, enum dma_data_direction dir,
struct tegra_se_ll *se_ll, u32 total)
{
u32 total_loop = 0;
int ret = 0;
total_loop = total;
while (sg) {
ret = dma_map_sg(dev, sg, nents, dir);
if (!ret) {
dev_err(dev, "dma_map_sg error\n");
return ret;
}
se_ll->addr = (u32)sg_dma_address(sg);
se_ll->data_len = min(sg->length, (size_t)total_loop);
total_loop -= min(sg->length, (size_t)total_loop);
sg = sg_next(sg);
se_ll++;
}
return ret;
}
static void tegra_unmap_sg(struct device *dev, struct scatterlist *sg,
enum dma_data_direction dir, u32 total)
{
while (sg) {
dma_unmap_sg(dev, sg, 1, dir);
sg = sg_next(sg);
}
}
static unsigned int tegra_se_count_sgs(struct scatterlist *sl, u32 nbytes)
{
unsigned int sg_nents = 0;
while (sl) {
sg_nents++;
nbytes -= min(sl->length, (size_t)nbytes);
if (!nbytes)
break;
sl = sg_next(sl);
}
return sg_nents;
}
static int tegra_se_alloc_ll_buf(struct tegra_se_dev *se_dev,
u32 num_src_sgs, u32 num_dst_sgs)
{
if (se_dev->src_ll_buf || se_dev->dst_ll_buf) {
dev_err(se_dev->dev, "trying to allocate memory to allocated memory\n");
return -EBUSY;
}
if (num_src_sgs) {
se_dev->src_ll_size =
(sizeof(struct tegra_se_ll) * num_src_sgs) +
sizeof(u32);
se_dev->src_ll_buf = dma_alloc_coherent(se_dev->dev,
se_dev->src_ll_size,
&se_dev->src_ll_buf_adr,
GFP_KERNEL);
if (!se_dev->src_ll_buf) {
dev_err(se_dev->dev, "can not allocate src lldma buffer\n");
return -ENOMEM;
}
}
if (num_dst_sgs) {
se_dev->dst_ll_size =
(sizeof(struct tegra_se_ll) * num_dst_sgs) +
sizeof(u32);
se_dev->dst_ll_buf = dma_alloc_coherent(se_dev->dev,
se_dev->dst_ll_size,
&se_dev->dst_ll_buf_adr,
GFP_KERNEL);
if (!se_dev->dst_ll_buf) {
dev_err(se_dev->dev, "can not allocate dst ll dma buffer\n");
return -ENOMEM;
}
}
return 0;
}
static void tegra_se_free_ll_buf(struct tegra_se_dev *se_dev)
{
if (se_dev->src_ll_buf) {
dma_free_coherent(se_dev->dev, se_dev->src_ll_size,
se_dev->src_ll_buf, se_dev->src_ll_buf_adr);
se_dev->src_ll_buf = NULL;
}
if (se_dev->dst_ll_buf) {
dma_free_coherent(se_dev->dev, se_dev->dst_ll_size,
se_dev->dst_ll_buf, se_dev->dst_ll_buf_adr);
se_dev->dst_ll_buf = NULL;
}
}
static void tegra_se_get_sg_dma_buf(struct scatterlist *sg, u32 num_sgs,
u32 nbytes, u32 *sg_buf)
{
struct sg_mapping_iter miter;
unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG;
unsigned long flags;
u32 *temp_buffer = sg_buf;
unsigned int total = 0;
sg_miter_start(&miter, sg, num_sgs, sg_flags);
local_irq_save(flags);
while (sg_miter_next(&miter) && total < nbytes) {
unsigned int len;
len = min(miter.length, (size_t)(nbytes - total));
memcpy(temp_buffer, miter.addr + total, len);
temp_buffer += len;
total += len;
}
sg_miter_stop(&miter);
local_irq_restore(flags);
}
static void tegra_se_get_dst_sg(struct scatterlist *sg, u32 num_sgs,
u32 nbytes, u32 *sg_buf)
{
struct sg_mapping_iter miter;
unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG;
unsigned long flags;
u32 *temp_buffer = sg_buf;
unsigned int total = 0;
sg_miter_start(&miter, sg, num_sgs, sg_flags);
local_irq_save(flags);
total = 0;
while (sg_miter_next(&miter) && total < nbytes) {
unsigned int len;
len = min(miter.length, (size_t)(nbytes - total));
memcpy(miter.addr + total, temp_buffer, len);
temp_buffer += len;
total += len;
}
sg_miter_stop(&miter);
local_irq_restore(flags);
}
static int tegra_se_setup_ablk_req(struct tegra_se_dev *se_dev,
struct ablkcipher_request *req)
{
struct scatterlist *src_sg, *dst_sg;
struct tegra_se_ll *src_ll, *dst_ll;
u32 total, num_src_sgs, num_dst_sgs;
int ret1 = 0, ret2 = 0;
num_src_sgs = tegra_se_count_sgs(req->src, req->nbytes);
num_dst_sgs = tegra_se_count_sgs(req->dst, req->nbytes);
if ((num_src_sgs > SE_MAX_SRC_SG_COUNT) ||
(num_dst_sgs > SE_MAX_DST_SG_COUNT)) {
dev_err(se_dev->dev, "num of SG buffers are more\n");
return -EDOM;
}
*se_dev->src_ll_buf = num_src_sgs - 1;
*se_dev->dst_ll_buf = num_dst_sgs - 1;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
src_sg = req->src;
dst_sg = req->dst;
total = req->nbytes;
if (total) {
if (req->nbytes == DISK_ENCR_BUF_SZ) {
tegra_se_get_sg_dma_buf(src_sg, num_src_sgs, total,
se_dev->sg_in_buf);
src_ll->addr = se_dev->sg_in_buf_adr;
src_ll->data_len = req->nbytes;
dst_ll->addr = se_dev->sg_out_buf_adr;
dst_ll->data_len = req->nbytes;
} else {
if (src_sg == dst_sg) {
ret1 = tegra_map_sg(se_dev->dev, src_sg, 1,
DMA_BIDIRECTIONAL, src_ll,
total);
if (!ret1)
return -EINVAL;
} else {
ret1 = tegra_map_sg(se_dev->dev, src_sg, 1,
DMA_TO_DEVICE, src_ll,
total);
ret2 = tegra_map_sg(se_dev->dev, dst_sg, 1,
DMA_FROM_DEVICE, dst_ll,
total);
if (!ret1 || !ret2)
return -EINVAL;
}
}
WARN_ON(src_sg->length != dst_sg->length);
}
return 0;
}
static void tegra_se_dequeue_complete_req(struct tegra_se_dev *se_dev,
struct ablkcipher_request *req)
{
struct scatterlist *src_sg, *dst_sg;
u32 total;
if (req) {
src_sg = req->src;
dst_sg = req->dst;
total = req->nbytes;
if (src_sg == dst_sg)
tegra_unmap_sg(se_dev->dev, dst_sg, DMA_BIDIRECTIONAL,
total);
else {
tegra_unmap_sg(se_dev->dev, dst_sg, DMA_FROM_DEVICE,
total);
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE,
total);
}
}
}
static void tegra_se_process_new_req(struct crypto_async_request *async_req)
{
struct tegra_se_dev *se_dev = se_devices[SE_AES];
struct ablkcipher_request *req = ablkcipher_request_cast(async_req);
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
struct tegra_se_aes_context *aes_ctx =
crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
int ret = 0;
/* take access to the hw */
mutex_lock(&se_hw_lock);
/* write IV */
if (req->info) {
if (req_ctx->op_mode == SE_AES_OP_MODE_CTR) {
tegra_se_write_seed(se_dev, (u32 *)req->info);
} else {
tegra_se_write_key_table(req->info,
TEGRA_SE_AES_IV_SIZE,
aes_ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_UPDTDIV);
}
}
ret = tegra_se_setup_ablk_req(se_dev, req);
if (ret)
goto out;
tegra_se_config_algo(se_dev, req_ctx->op_mode,
req_ctx->encrypt, aes_ctx->keylen);
tegra_se_config_crypto(se_dev, req_ctx->op_mode,
req_ctx->encrypt, aes_ctx->slot->slot_num,
false);
ret = tegra_se_start_operation(se_dev, req->nbytes, false,
((req->src == req->dst) ? false : true));
if (req->nbytes == DISK_ENCR_BUF_SZ)
tegra_se_get_dst_sg(req->dst, 1, req->nbytes,
se_dev->sg_out_buf);
else
tegra_se_dequeue_complete_req(se_dev, req);
out:
mutex_unlock(&se_hw_lock);
req->base.complete(&req->base, ret);
}
static irqreturn_t tegra_se_irq(int irq, void *dev)
{
struct tegra_se_dev *se_dev = dev;
u32 val, err_stat;
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
se_writel(se_dev, val, SE_INT_STATUS_REG_OFFSET);
if (val & SE_INT_ERROR(INT_SET)) {
err_stat = se_readl(se_dev, SE_ERR_STATUS_0);
dev_err(se_dev->dev, "tegra_se_irq::error status is %x\n",
err_stat);
}
if (val & SE_INT_OP_DONE(INT_SET))
complete(&se_dev->complete);
return IRQ_HANDLED;
}
static void tegra_se_work_handler(struct work_struct *work)
{
struct tegra_se_dev *se_dev = se_devices[SE_AES];
struct crypto_async_request *async_req = NULL;
struct crypto_async_request *backlog = NULL;
pm_runtime_get_sync(se_dev->dev);
do {
spin_lock_irq(&se_dev->lock);
backlog = crypto_get_backlog(&se_dev->queue);
async_req = crypto_dequeue_request(&se_dev->queue);
if (!async_req)
se_dev->work_q_busy = false;
spin_unlock_irq(&se_dev->lock);
if (backlog) {
backlog->complete(backlog, -EINPROGRESS);
backlog = NULL;
}
if (async_req) {
tegra_se_process_new_req(async_req);
async_req = NULL;
}
} while (se_dev->work_q_busy);
pm_runtime_put(se_dev->dev);
}
static int tegra_se_aes_queue_req(struct ablkcipher_request *req)
{
struct tegra_se_dev *se_dev = se_devices[SE_AES];
unsigned long flags;
bool idle = true;
int err = 0;
if (!tegra_se_count_sgs(req->src, req->nbytes)) {
dev_err(se_dev->dev, "invalid SG count");
return -EINVAL;
}
spin_lock_irqsave(&se_dev->lock, flags);
err = ablkcipher_enqueue_request(&se_dev->queue, req);
if (se_dev->work_q_busy)
idle = false;
spin_unlock_irqrestore(&se_dev->lock, flags);
if (idle) {
spin_lock_irq(&se_dev->lock);
se_dev->work_q_busy = true;
spin_unlock_irq(&se_dev->lock);
queue_work(se_work_q, &se_work);
}
return err;
}
static int tegra_se_aes_cbc_encrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = true;
req_ctx->op_mode = SE_AES_OP_MODE_CBC;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_cbc_decrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = false;
req_ctx->op_mode = SE_AES_OP_MODE_CBC;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ecb_encrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = true;
req_ctx->op_mode = SE_AES_OP_MODE_ECB;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ecb_decrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = false;
req_ctx->op_mode = SE_AES_OP_MODE_ECB;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ctr_encrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = true;
req_ctx->op_mode = SE_AES_OP_MODE_CTR;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ctr_decrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = false;
req_ctx->op_mode = SE_AES_OP_MODE_CTR;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ofb_encrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = true;
req_ctx->op_mode = SE_AES_OP_MODE_OFB;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_ofb_decrypt(struct ablkcipher_request *req)
{
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
req_ctx->encrypt = false;
req_ctx->op_mode = SE_AES_OP_MODE_OFB;
return tegra_se_aes_queue_req(req);
}
static int tegra_se_aes_setkey(struct crypto_ablkcipher *tfm,
const u8 *key, u32 keylen)
{
struct tegra_se_aes_context *ctx = crypto_ablkcipher_ctx(tfm);
struct tegra_se_dev *se_dev = NULL;
struct tegra_se_slot *pslot;
u8 *pdata = (u8 *)key;
if (!ctx || !ctx->se_dev) {
pr_err("invalid context or dev");
return -EINVAL;
}
se_dev = ctx->se_dev;
if ((keylen != TEGRA_SE_KEY_128_SIZE) &&
(keylen != TEGRA_SE_KEY_192_SIZE) &&
(keylen != TEGRA_SE_KEY_256_SIZE)) {
dev_err(se_dev->dev, "invalid key size");
return -EINVAL;
}
if (key) {
if (!ctx->slot ||
(ctx->slot && ctx->slot->slot_num == ssk_slot.slot_num)) {
pslot = tegra_se_alloc_key_slot();
if (!pslot) {
dev_err(se_dev->dev, "no free key slot\n");
return -ENOMEM;
}
ctx->slot = pslot;
}
ctx->keylen = keylen;
} else {
tegra_se_free_key_slot(ctx->slot);
ctx->slot = &ssk_slot;
ctx->keylen = AES_KEYSIZE_128;
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
/* load the key */
tegra_se_write_key_table(pdata, keylen, ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_KEY);
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static int tegra_se_aes_cra_init(struct crypto_tfm *tfm)
{
struct tegra_se_aes_context *ctx = crypto_tfm_ctx(tfm);
ctx->se_dev = sg_tegra_se_dev;
tfm->crt_ablkcipher.reqsize = sizeof(struct tegra_se_req_context);
return 0;
}
static void tegra_se_aes_cra_exit(struct crypto_tfm *tfm)
{
struct tegra_se_aes_context *ctx = crypto_tfm_ctx(tfm);
tegra_se_free_key_slot(ctx->slot);
ctx->slot = NULL;
}
static int tegra_se_rng_drbg_init(struct crypto_tfm *tfm)
{
struct tegra_se_rng_context *rng_ctx = crypto_tfm_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_DRBG];
rng_ctx->se_dev = se_dev;
rng_ctx->dt_buf = dma_alloc_coherent(se_dev->dev, TEGRA_SE_RNG_DT_SIZE,
&rng_ctx->dt_buf_adr, GFP_KERNEL);
if (!rng_ctx->dt_buf) {
dev_err(se_dev->dev, "can not allocate rng dma buffer");
return -ENOMEM;
}
rng_ctx->rng_buf = dma_alloc_coherent(rng_ctx->se_dev->dev,
TEGRA_SE_RNG_DT_SIZE,
&rng_ctx->rng_buf_adr,
GFP_KERNEL);
if (!rng_ctx->rng_buf) {
dev_err(se_dev->dev, "can not allocate rng dma buffer");
dma_free_coherent(rng_ctx->se_dev->dev, TEGRA_SE_RNG_DT_SIZE,
rng_ctx->dt_buf, rng_ctx->dt_buf_adr);
return -ENOMEM;
}
return 0;
}
static int tegra_se_rng_drbg_get_random(struct crypto_rng *tfm,
const u8 *src, unsigned int slen,
u8 *rdata, unsigned int dlen)
{
struct tegra_se_rng_context *rng_ctx = crypto_rng_ctx(tfm);
struct tegra_se_dev *se_dev = rng_ctx->se_dev;
struct tegra_se_ll *src_ll, *dst_ll;
u8 *rdata_addr;
int ret = 0, j;
unsigned int num_blocks, data_len = 0;
num_blocks = (dlen / TEGRA_SE_RNG_DT_SIZE);
data_len = (dlen % TEGRA_SE_RNG_DT_SIZE);
if (data_len == 0)
num_blocks = num_blocks - 1;
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
src_ll->addr = rng_ctx->dt_buf_adr;
src_ll->data_len = TEGRA_SE_RNG_DT_SIZE;
dst_ll->addr = rng_ctx->rng_buf_adr;
dst_ll->data_len = TEGRA_SE_RNG_DT_SIZE;
tegra_se_config_algo(se_dev, SE_AES_OP_MODE_RNG_DRBG, true,
TEGRA_SE_KEY_128_SIZE);
tegra_se_config_crypto(se_dev, SE_AES_OP_MODE_RNG_DRBG, true, 0, true);
for (j = 0; j <= num_blocks; j++) {
ret = tegra_se_start_operation(se_dev, TEGRA_SE_RNG_DT_SIZE,
false, true);
if (ret)
break;
rdata_addr = (rdata + (j * TEGRA_SE_RNG_DT_SIZE));
if (data_len && num_blocks == j) {
memcpy(rdata_addr, rng_ctx->rng_buf, data_len);
} else {
memcpy(rdata_addr, rng_ctx->rng_buf,
TEGRA_SE_RNG_DT_SIZE);
}
}
if (!ret)
ret = dlen;
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_rng_drbg_reset(struct crypto_rng *tfm, const u8 *seed,
unsigned int slen)
{
return 0;
}
static void tegra_se_rng_drbg_exit(struct crypto_tfm *tfm)
{
struct tegra_se_rng_context *rng_ctx = crypto_tfm_ctx(tfm);
if (rng_ctx->dt_buf) {
dma_free_coherent(rng_ctx->se_dev->dev, TEGRA_SE_RNG_DT_SIZE,
rng_ctx->dt_buf, rng_ctx->dt_buf_adr);
}
if (rng_ctx->rng_buf) {
dma_free_coherent(rng_ctx->se_dev->dev, TEGRA_SE_RNG_DT_SIZE,
rng_ctx->rng_buf, rng_ctx->rng_buf_adr);
}
rng_ctx->se_dev = NULL;
}
static int tegra_se_sha_init(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct tegra_se_sha_context *sha_ctx;
if (!tfm)
return -EINVAL;
sha_ctx = crypto_ahash_ctx(tfm);
if (!sha_ctx)
return -EINVAL;
/* Initialize total bytes with zero */
sha_ctx->total_count = 0;
return 0;
}
static int tegra_se_shash_init(struct shash_desc *desc)
{
return 0;
}
static int tegra_se_sha_update(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct tegra_se_sha_context *sha_ctx = crypto_ahash_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
struct scatterlist *src_sg;
struct tegra_se_ll *src_ll;
u32 total, num_sgs;
unsigned long freq = 0;
int err = 0;
if (!req->nbytes)
return 0;
switch (crypto_ahash_digestsize(tfm)) {
case SHA1_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA1;
freq = se_dev->chipdata->sha1_freq;
break;
case SHA224_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA224;
freq = se_dev->chipdata->sha224_freq;
break;
case SHA256_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA256;
freq = se_dev->chipdata->sha256_freq;
break;
case SHA384_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA384;
freq = se_dev->chipdata->sha384_freq;
break;
case SHA512_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA512;
freq = se_dev->chipdata->sha512_freq;
break;
default:
dev_err(se_dev->dev, "Invalid SHA digest size\n");
return -EINVAL;
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
if (num_sgs > SE_MAX_SRC_SG_COUNT) {
dev_err(se_dev->dev, "num of SG buffers are more\n");
err = -EDOM;
goto fail;
}
*se_dev->src_ll_buf = num_sgs - 1;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
src_sg = req->src;
total = req->nbytes;
err = tegra_map_sg(se_dev->dev, src_sg, 1, DMA_TO_DEVICE,
src_ll, total);
if (!err) {
err = -EINVAL;
goto fail;
}
tegra_se_config_algo(se_dev, sha_ctx->op_mode, false, 0);
tegra_se_config_sha(se_dev, req->nbytes, freq);
err = tegra_se_start_operation(se_dev, 0, false, true);
src_sg = req->src;
total = req->nbytes;
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE, total);
/* Increase total count with current req number of bytes */
sha_ctx->total_count += req->nbytes;
fail:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return err;
}
static int tegra_se_sha_finup(struct ahash_request *req)
{
return 0;
}
static int tegra_se_sha_final(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct tegra_se_sha_context *sha_ctx = crypto_ahash_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
int err = 0;
u32 mode;
struct tegra_se_sha_zero_length_vector zero_vec[] = {
{
.size = SHA1_DIGEST_SIZE,
.digest = "\xda\x39\xa3\xee\x5e\x6b\x4b\x0d"
"\x32\x55\xbf\xef\x95\x60\x18\x90"
"\xaf\xd8\x07\x09",
}, {
.size = SHA224_DIGEST_SIZE,
.digest = "\xd1\x4a\x02\x8c\x2a\x3a\x2b\xc9"
"\x47\x61\x02\xbb\x28\x82\x34\xc4"
"\x15\xa2\xb0\x1f\x82\x8e\xa6\x2a"
"\xc5\xb3\xe4\x2f",
}, {
.size = SHA256_DIGEST_SIZE,
.digest = "\xe3\xb0\xc4\x42\x98\xfc\x1c\x14"
"\x9a\xfb\xf4\xc8\x99\x6f\xb9\x24"
"\x27\xae\x41\xe4\x64\x9b\x93\x4c"
"\xa4\x95\x99\x1b\x78\x52\xb8\x55",
}, {
.size = SHA384_DIGEST_SIZE,
.digest = "\x38\xb0\x60\xa7\x51\xac\x96\x38"
"\x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a"
"\x21\xfd\xb7\x11\x14\xbe\x07\x43"
"\x4c\x0c\xc7\xbf\x63\xf6\xe1\xda"
"\x27\x4e\xde\xbf\xe7\x6f\x65\xfb"
"\xd5\x1a\xd2\xf1\x48\x98\xb9\x5b",
}, {
.size = SHA512_DIGEST_SIZE,
.digest = "\xcf\x83\xe1\x35\x7e\xef\xb8\xbd"
"\xf1\x54\x28\x50\xd6\x6d\x80\x07"
"\xd6\x20\xe4\x05\x0b\x57\x15\xdc"
"\x83\xf4\xa9\x21\xd3\x6c\xe9\xce"
"\x47\xd0\xd1\x3c\x5d\x85\xf2\xb0"
"\xff\x83\x18\xd2\x87\x7e\xec\x2f"
"\x63\xb9\x31\xbd\x47\x41\x7a\x81"
"\xa5\x38\x32\x7a\xf9\x27\xda\x3e",
}
};
if (req->nbytes) {
err = tegra_se_sha_update(req);
if (err)
return err;
} else
switch (crypto_ahash_digestsize(tfm)) {
case SHA1_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA1;
break;
case SHA224_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA224;
break;
case SHA256_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA256;
break;
case SHA384_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA384;
break;
case SHA512_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA512;
break;
default:
dev_err(se_dev->dev, "Invalid SHA digest size\n");
return -EINVAL;
}
/* If total length is zero return zero hash result */
if (!sha_ctx->total_count) {
/*
* SW WAR for zero length SHA operation since
* SE HW can't accept zero length SHA operation.
*/
mode = sha_ctx->op_mode - SE_AES_OP_MODE_SHA1;
memcpy(req->result, zero_vec[mode].digest, zero_vec[mode].size);
return 0;
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
/* read hash result */
tegra_se_read_hash_result(se_dev, req->result,
crypto_ahash_digestsize(tfm), true);
if ((sha_ctx->op_mode == SE_AES_OP_MODE_SHA384) ||
(sha_ctx->op_mode == SE_AES_OP_MODE_SHA512)) {
u32 *result = (u32 *)req->result;
u32 temp, i;
for (i = 0; i < crypto_ahash_digestsize(tfm) / 4; i += 2) {
temp = result[i];
result[i] = result[i + 1];
result[i + 1] = temp;
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static int tegra_se_shash_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
struct tegra_se_sha_context *sha_ctx = crypto_shash_ctx(desc->tfm);
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
struct tegra_se_ll *src_ll;
u32 *temp_virt = NULL;
unsigned long freq = 0;
int err = 0;
dma_addr_t tmp_buf_adr; /* Destination buffer dma address */
switch (crypto_shash_digestsize(desc->tfm)) {
case SHA1_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA1;
freq = se_dev->chipdata->sha1_freq;
break;
case SHA224_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA224;
freq = se_dev->chipdata->sha224_freq;
break;
case SHA256_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA256;
freq = se_dev->chipdata->sha256_freq;
break;
case SHA384_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA384;
freq = se_dev->chipdata->sha384_freq;
break;
case SHA512_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA512;
freq = se_dev->chipdata->sha512_freq;
break;
default:
dev_err(se_dev->dev, "Invalid SHA digest size\n");
return -EINVAL;
}
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
temp_virt = dma_alloc_coherent(se_dev->dev, len,
&tmp_buf_adr, GFP_KERNEL);
if (!temp_virt) {
pr_err("\ndma_alloc_coherent failed\n");
err = -EINVAL;
goto exit;
}
dma_sync_single_for_device(se_dev->dev, tmp_buf_adr, len,
DMA_TO_DEVICE);
/* copy client buffer to local dmaable buffer*/
memcpy(temp_virt, data, len);
/* Prepare linked list for HW */
*se_dev->src_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
src_ll->addr = tmp_buf_adr;
src_ll->data_len = len;
tegra_se_config_algo(se_dev, sha_ctx->op_mode, false, 0);
tegra_se_config_sha(se_dev, len, freq);
err = tegra_se_start_operation(se_dev, 0, false, true);
dma_free_coherent(se_dev->dev, len, temp_virt, tmp_buf_adr);
exit:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return err;
}
static int tegra_se_shash_final(struct shash_desc *desc, u8 *out)
{
struct tegra_se_sha_context *sha_ctx = crypto_shash_ctx(desc->tfm);
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
switch (crypto_shash_digestsize(desc->tfm)) {
case SHA1_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA1;
break;
case SHA224_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA224;
break;
case SHA256_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA256;
break;
case SHA384_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA384;
break;
case SHA512_DIGEST_SIZE:
sha_ctx->op_mode = SE_AES_OP_MODE_SHA512;
break;
default:
dev_err(se_dev->dev, "Invalid SHA digest size\n");
return -EINVAL;
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
/* read hash result */
tegra_se_read_hash_result(se_dev, out,
crypto_shash_digestsize(desc->tfm), true);
if ((sha_ctx->op_mode == SE_AES_OP_MODE_SHA384) ||
(sha_ctx->op_mode == SE_AES_OP_MODE_SHA512)) {
u32 *result = (u32 *)out;
u32 temp, i;
for (i = 0; i < crypto_shash_digestsize(desc->tfm) / 4;
i += 2) {
temp = result[i];
result[i] = result[i + 1];
result[i + 1] = temp;
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static int tegra_se_sha_digest(struct ahash_request *req)
{
return tegra_se_sha_init(req) ?: tegra_se_sha_final(req);
}
static int tegra_se_shash_digest(struct shash_desc *desc, const u8 *data,
unsigned int len, u8 *out)
{
return tegra_se_shash_final(desc, out);
}
static int tegra_se_sha_cra_init(struct crypto_tfm *tfm)
{
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
sizeof(struct tegra_se_sha_context));
return 0;
}
static void tegra_se_sha_cra_exit(struct crypto_tfm *tfm)
{
/* do nothing */
}
static int tegra_se_shash_cra_init(struct crypto_tfm *tfm)
{
unsigned int *hash = crypto_tfm_ctx(tfm);
*hash = 0;
return 0;
}
static void tegra_se_shash_cra_exit(struct crypto_tfm *tfm)
{
/* do nothing */
}
static int tegra_se_aes_cmac_init(struct ahash_request *req)
{
return 0;
}
static int tegra_se_aes_cmac_update(struct ahash_request *req)
{
return 0;
}
static int tegra_se_aes_cmac_final(struct ahash_request *req)
{
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
struct tegra_se_aes_cmac_context *cmac_ctx = crypto_ahash_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_AES];
struct scatterlist *src_sg;
struct tegra_se_ll *src_ll;
struct sg_mapping_iter miter;
u32 num_sgs, blocks_to_process, last_block_bytes = 0, bytes_to_copy = 0;
u8 piv[TEGRA_SE_AES_IV_SIZE];
int ret = 0, i = 0;
bool padding_needed = false;
unsigned long flags;
unsigned int sg_flags = SG_MITER_ATOMIC, total = 0;
u8 *temp_buffer = NULL;
bool use_orig_iv = true;
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
blocks_to_process = req->nbytes / TEGRA_SE_AES_BLOCK_SIZE;
/* num of bytes less than block size */
if ((req->nbytes % TEGRA_SE_AES_BLOCK_SIZE) || !blocks_to_process) {
padding_needed = true;
last_block_bytes = req->nbytes % TEGRA_SE_AES_BLOCK_SIZE;
} else {
/* decrement num of blocks */
blocks_to_process--;
if (blocks_to_process) {
last_block_bytes = req->nbytes -
(blocks_to_process * TEGRA_SE_AES_BLOCK_SIZE);
} else {
last_block_bytes = req->nbytes;
}
}
/* first process all blocks except last block */
if (blocks_to_process) {
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
if (num_sgs > SE_MAX_SRC_SG_COUNT) {
dev_err(se_dev->dev, "num of SG buffers are more\n");
ret = -EDOM;
goto out;
}
*se_dev->src_ll_buf = num_sgs - 1;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
src_sg = req->src;
total = blocks_to_process * TEGRA_SE_AES_BLOCK_SIZE;
ret = tegra_map_sg(se_dev->dev, src_sg, 1, DMA_TO_DEVICE,
src_ll, total);
if (!ret) {
ret = -EINVAL;
goto out;
}
tegra_se_config_algo(se_dev, SE_AES_OP_MODE_CMAC, true,
cmac_ctx->keylen);
/* write zero IV */
memset(piv, 0, TEGRA_SE_AES_IV_SIZE);
tegra_se_write_key_table(piv, TEGRA_SE_AES_IV_SIZE,
cmac_ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_ORGIV);
tegra_se_config_crypto(se_dev, SE_AES_OP_MODE_CMAC, true,
cmac_ctx->slot->slot_num, true);
ret = tegra_se_start_operation(se_dev, blocks_to_process *
TEGRA_SE_AES_BLOCK_SIZE,
false, true);
src_sg = req->src;
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE, total);
if (ret)
goto out;
use_orig_iv = false;
}
/* get the last block bytes from the sg_dma buffer using miter */
src_sg = req->src;
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
sg_flags |= SG_MITER_FROM_SG;
cmac_ctx->buffer = dma_alloc_coherent(se_dev->dev,
TEGRA_SE_AES_BLOCK_SIZE,
&cmac_ctx->dma_addr, GFP_KERNEL);
if (!cmac_ctx->buffer)
goto out;
local_irq_save(flags);
sg_miter_start(&miter, req->src, num_sgs, sg_flags);
total = 0;
temp_buffer = cmac_ctx->buffer;
while (sg_miter_next(&miter) && total < req->nbytes) {
unsigned int len;
len = min(miter.length, (size_t)(req->nbytes - total));
if ((req->nbytes - (total + len)) <= last_block_bytes) {
bytes_to_copy = last_block_bytes -
(req->nbytes - (total + len));
memcpy(temp_buffer, miter.addr + (len - bytes_to_copy),
bytes_to_copy);
last_block_bytes -= bytes_to_copy;
temp_buffer += bytes_to_copy;
}
total += len;
}
sg_miter_stop(&miter);
local_irq_restore(flags);
/* process last block */
if (padding_needed) {
/* pad with 0x80, 0, 0 ... */
last_block_bytes = req->nbytes % TEGRA_SE_AES_BLOCK_SIZE;
cmac_ctx->buffer[last_block_bytes] = 0x80;
for (i = last_block_bytes + 1; i < TEGRA_SE_AES_BLOCK_SIZE; i++)
cmac_ctx->buffer[i] = 0;
/* XOR with K2 */
for (i = 0; i < TEGRA_SE_AES_BLOCK_SIZE; i++)
cmac_ctx->buffer[i] ^= cmac_ctx->K2[i];
} else {
/* XOR with K1 */
for (i = 0; i < TEGRA_SE_AES_BLOCK_SIZE; i++)
cmac_ctx->buffer[i] ^= cmac_ctx->K1[i];
}
*se_dev->src_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
src_ll->addr = cmac_ctx->dma_addr;
src_ll->data_len = TEGRA_SE_AES_BLOCK_SIZE;
if (use_orig_iv) {
/* use zero IV, this is when num of bytes is
* less <= block size
*/
memset(piv, 0, TEGRA_SE_AES_IV_SIZE);
tegra_se_write_key_table(piv, TEGRA_SE_AES_IV_SIZE,
cmac_ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_ORGIV);
}
tegra_se_config_algo(se_dev, SE_AES_OP_MODE_CMAC, true,
cmac_ctx->keylen);
tegra_se_config_crypto(se_dev, SE_AES_OP_MODE_CMAC, true,
cmac_ctx->slot->slot_num, use_orig_iv);
ret = tegra_se_start_operation(se_dev, TEGRA_SE_AES_BLOCK_SIZE,
false, true);
if (ret)
goto out;
tegra_se_read_hash_result(se_dev, req->result,
TEGRA_SE_AES_CMAC_DIGEST_SIZE, false);
out:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
if (cmac_ctx->buffer)
dma_free_coherent(se_dev->dev, TEGRA_SE_AES_BLOCK_SIZE,
cmac_ctx->buffer, cmac_ctx->dma_addr);
return ret;
}
static int tegra_se_aes_cmac_setkey(struct crypto_ahash *tfm, const u8 *key,
unsigned int keylen)
{
struct tegra_se_aes_cmac_context *ctx = crypto_ahash_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_AES];
struct tegra_se_ll *src_ll, *dst_ll;
struct tegra_se_slot *pslot;
u8 piv[TEGRA_SE_AES_IV_SIZE];
u32 *pbuf;
dma_addr_t pbuf_adr;
int ret = 0;
u8 const rb = 0x87;
u8 msb;
if (!ctx) {
dev_err(se_dev->dev, "invalid context");
return -EINVAL;
}
if ((keylen != TEGRA_SE_KEY_128_SIZE) &&
(keylen != TEGRA_SE_KEY_192_SIZE) &&
(keylen != TEGRA_SE_KEY_256_SIZE)) {
dev_err(se_dev->dev, "invalid key size");
return -EINVAL;
}
if (key) {
if (!ctx->slot ||
(ctx->slot && ctx->slot->slot_num == ssk_slot.slot_num)) {
pslot = tegra_se_alloc_key_slot();
if (!pslot) {
dev_err(se_dev->dev, "no free key slot\n");
return -ENOMEM;
}
ctx->slot = pslot;
}
ctx->keylen = keylen;
} else {
tegra_se_free_key_slot(ctx->slot);
ctx->slot = &ssk_slot;
ctx->keylen = AES_KEYSIZE_128;
}
pbuf = dma_alloc_coherent(se_dev->dev, TEGRA_SE_AES_BLOCK_SIZE,
&pbuf_adr, GFP_KERNEL);
if (!pbuf) {
dev_err(se_dev->dev, "can not allocate dma buffer");
tegra_se_free_key_slot(ctx->slot);
return -ENOMEM;
}
memset(pbuf, 0, TEGRA_SE_AES_BLOCK_SIZE);
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
src_ll->addr = pbuf_adr;
src_ll->data_len = TEGRA_SE_AES_BLOCK_SIZE;
dst_ll->addr = pbuf_adr;
dst_ll->data_len = TEGRA_SE_AES_BLOCK_SIZE;
/* load the key */
tegra_se_write_key_table((u8 *)key, keylen, ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_KEY);
/* write zero IV */
memset(piv, 0, TEGRA_SE_AES_IV_SIZE);
/* load IV */
tegra_se_write_key_table(piv, TEGRA_SE_AES_IV_SIZE, ctx->slot->slot_num,
SE_KEY_TABLE_TYPE_ORGIV);
/* config crypto algo */
tegra_se_config_algo(se_dev, SE_AES_OP_MODE_CBC, true, keylen);
tegra_se_config_crypto(se_dev, SE_AES_OP_MODE_CBC, true,
ctx->slot->slot_num, true);
ret = tegra_se_start_operation(se_dev, TEGRA_SE_AES_BLOCK_SIZE,
false, true);
if (ret) {
dev_err(se_dev->dev, "%s: start op failed\n", __func__);
tegra_se_free_key_slot(ctx->slot);
goto out;
}
/* compute K1 subkey */
memcpy(ctx->K1, pbuf, TEGRA_SE_AES_BLOCK_SIZE);
tegra_se_leftshift_onebit(ctx->K1, TEGRA_SE_AES_BLOCK_SIZE, &msb);
if (msb)
ctx->K1[TEGRA_SE_AES_BLOCK_SIZE - 1] ^= rb;
/* compute K2 subkey */
memcpy(ctx->K2, ctx->K1, TEGRA_SE_AES_BLOCK_SIZE);
tegra_se_leftshift_onebit(ctx->K2, TEGRA_SE_AES_BLOCK_SIZE, &msb);
if (msb)
ctx->K2[TEGRA_SE_AES_BLOCK_SIZE - 1] ^= rb;
out:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
if (pbuf)
dma_free_coherent(se_dev->dev, TEGRA_SE_AES_BLOCK_SIZE,
pbuf, pbuf_adr);
return ret;
}
static int tegra_se_aes_cmac_digest(struct ahash_request *req)
{
return tegra_se_aes_cmac_init(req) ?: tegra_se_aes_cmac_final(req);
}
static int tegra_se_aes_cmac_finup(struct ahash_request *req)
{
return 0;
}
static int tegra_se_aes_cmac_cra_init(struct crypto_tfm *tfm)
{
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
sizeof(struct tegra_se_aes_cmac_context));
return 0;
}
static void tegra_se_aes_cmac_cra_exit(struct crypto_tfm *tfm)
{
struct tegra_se_aes_cmac_context *ctx = crypto_tfm_ctx(tfm);
tegra_se_free_key_slot(ctx->slot);
ctx->slot = NULL;
}
/* Security Engine rsa key slot */
struct tegra_se_rsa_slot {
struct list_head node;
u8 slot_num; /* Key slot number */
bool available; /* Tells whether key slot is free to use */
};
/* Security Engine AES RSA context */
struct tegra_se_aes_rsa_context {
struct tegra_se_dev *se_dev; /* Security Engine device */
struct tegra_se_rsa_slot *slot; /* Security Engine rsa key slot */
u32 mod_len;
u32 exp_len;
};
static void tegra_se_rsa_free_key_slot(struct tegra_se_rsa_slot *slot)
{
if (slot) {
spin_lock(&rsa_key_slot_lock);
slot->available = true;
spin_unlock(&rsa_key_slot_lock);
}
}
static struct tegra_se_rsa_slot *tegra_se_alloc_rsa_key_slot(void)
{
struct tegra_se_rsa_slot *slot = NULL;
bool found = false;
spin_lock(&rsa_key_slot_lock);
list_for_each_entry(slot, &rsa_key_slot, node) {
if (slot->available) {
slot->available = false;
found = true;
break;
}
}
spin_unlock(&rsa_key_slot_lock);
return found ? slot : NULL;
}
static int tegra_init_rsa_key_slot(struct tegra_se_dev *se_dev)
{
int i;
se_dev->rsa_slot_list = devm_kzalloc(se_dev->dev,
sizeof(struct tegra_se_rsa_slot) *
TEGRA_SE_RSA_KEYSLOT_COUNT,
GFP_KERNEL);
if (!se_dev->rsa_slot_list)
return -ENOMEM;
spin_lock_init(&rsa_key_slot_lock);
spin_lock(&rsa_key_slot_lock);
for (i = 0; i < TEGRA_SE_RSA_KEYSLOT_COUNT; i++) {
se_dev->rsa_slot_list[i].available = true;
se_dev->rsa_slot_list[i].slot_num = i;
INIT_LIST_HEAD(&se_dev->rsa_slot_list[i].node);
list_add_tail(&se_dev->rsa_slot_list[i].node, &rsa_key_slot);
}
spin_unlock(&rsa_key_slot_lock);
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
static unsigned int tegra_se_rsa_max_size(struct crypto_akcipher *tfm)
#else
static int tegra_se_rsa_max_size(struct crypto_akcipher *tfm)
#endif
{
struct tegra_se_aes_rsa_context *ctx = akcipher_tfm_ctx(tfm);
if (!ctx) {
pr_err("Invalid rsa context\n");
return -EINVAL;
}
return ctx->mod_len;
}
static int tegra_se_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
unsigned int keylen)
{
struct tegra_se_aes_rsa_context *ctx = akcipher_tfm_ctx(tfm);
struct tegra_se_dev *se_dev = se_devices[SE_RSA];
u32 module_key_length, exponent_key_length;
u32 pkt, val, key_size_words, key_word_size = 4;
u32 *pkeydata = (u32 *)key;
s32 i = 0;
struct tegra_se_rsa_slot *pslot;
unsigned long freq = 0;
int err = 0;
if (!ctx || !key) {
dev_err(se_dev->dev, "Invalid context or key\n");
return -EINVAL;
}
/* Allocate rsa key slot */
if (!ctx->slot) {
pslot = tegra_se_alloc_rsa_key_slot();
if (!pslot) {
dev_err(se_dev->dev, "no free key slot\n");
return -ENOMEM;
}
ctx->slot = pslot;
}
module_key_length = (keylen >> 16);
exponent_key_length = (keylen & (0xFFFF));
if (!(((module_key_length / 64) >= 1) &&
((module_key_length / 64) <= 4))) {
tegra_se_rsa_free_key_slot(ctx->slot);
dev_err(se_dev->dev, "Modulus length is not in range\n");
return -EDOM;
}
ctx->mod_len = module_key_length;
ctx->exp_len = exponent_key_length;
freq = se_dev->chipdata->rsa_freq;
if (se_dev->pclk && !se_dev->chipdata->const_freq) {
err = clk_set_rate(se_dev->pclk, freq);
if (err) {
dev_err(se_dev->dev, "clock set_rate failed\n");
tegra_se_rsa_free_key_slot(ctx->slot);
return err;
}
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if (exponent_key_length) {
key_size_words = (exponent_key_length / key_word_size);
/* Write exponent */
if (se_dev->chipdata->rsa_key_rw_op) {
for (i = (key_size_words - 1); i >= 0; i--) {
se_writel(se_dev, *pkeydata++,
SE_RSA_KEYTABLE_DATA);
pkt =
RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_EXP) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEY_OP(RSA_KEY_WRITE) |
SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
}
} else {
for (i = (key_size_words - 1); i >= 0; i--) {
pkt =
RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_EXP) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
se_writel(se_dev, *pkeydata++,
SE_RSA_KEYTABLE_DATA);
}
}
}
if (module_key_length) {
key_size_words = (module_key_length / key_word_size);
/* Write modulus */
if (se_dev->chipdata->rsa_key_rw_op) {
for (i = (key_size_words - 1); i >= 0; i--) {
se_writel(se_dev, *pkeydata++,
SE_RSA_KEYTABLE_DATA);
pkt =
RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_MOD) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEY_OP(RSA_KEY_WRITE) |
SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
}
} else {
for (i = (key_size_words - 1); i >= 0; i--) {
pkt =
RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_MOD) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
se_writel(se_dev, *pkeydata++,
SE_RSA_KEYTABLE_DATA);
}
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static int tegra_se_rsa_op(struct akcipher_request *req)
{
struct crypto_akcipher *tfm = NULL;
struct tegra_se_aes_rsa_context *rsa_ctx = NULL;
struct tegra_se_dev *se_dev = se_devices[SE_RSA];
struct tegra_se_ll *src_ll, *dst_ll;
u32 num_src_sgs, num_dst_sgs;
int ret1 = 0, ret2 = 0;
u32 val = 0;
if (!req) {
dev_err(se_dev->dev, "Invalid rsa request\n");
return -EINVAL;
}
tfm = crypto_akcipher_reqtfm(req);
if (!tfm) {
dev_err(se_dev->dev, "Invalid rsa transform\n");
return -EINVAL;
}
rsa_ctx = akcipher_tfm_ctx(tfm);
if (!rsa_ctx || !rsa_ctx->slot) {
dev_err(se_dev->dev, "Invalid rsa context\n");
return -EINVAL;
}
if ((req->src_len < TEGRA_SE_RSA512_INPUT_SIZE) ||
(req->src_len > TEGRA_SE_RSA2048_INPUT_SIZE)) {
dev_err(se_dev->dev, "rsa request src length not in range\n");
return -EDOM;
}
if ((req->dst_len < TEGRA_SE_RSA512_INPUT_SIZE) ||
(req->dst_len > TEGRA_SE_RSA2048_INPUT_SIZE)) {
dev_err(se_dev->dev, "rsa request dst length not in range\n");
return -EDOM;
}
if (req->src_len != rsa_ctx->mod_len) {
dev_err(se_dev->dev, "Invalid rsa request src length\n");
return -EINVAL;
}
num_src_sgs = tegra_se_count_sgs(req->src, req->src_len);
num_dst_sgs = tegra_se_count_sgs(req->dst, req->dst_len);
if ((num_src_sgs > SE_MAX_SRC_SG_COUNT) ||
(num_dst_sgs > SE_MAX_DST_SG_COUNT)) {
dev_err(se_dev->dev, "num of SG buffers are more\n");
return -EDOM;
}
mutex_lock(&se_hw_lock);
*se_dev->src_ll_buf = num_src_sgs - 1;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
if (req->src == req->dst) {
dst_ll = src_ll;
ret1 = tegra_map_sg(se_dev->dev, req->src, 1, DMA_BIDIRECTIONAL,
src_ll, req->src_len);
if (!ret1) {
mutex_unlock(&se_hw_lock);
return -EINVAL;
}
} else {
*se_dev->dst_ll_buf = num_dst_sgs - 1;
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
ret1 = tegra_map_sg(se_dev->dev, req->src, 1, DMA_TO_DEVICE,
src_ll, req->src_len);
ret2 = tegra_map_sg(se_dev->dev, req->dst, 1, DMA_FROM_DEVICE,
dst_ll, req->dst_len);
if (!ret1 || !ret2) {
mutex_unlock(&se_hw_lock);
return -EINVAL;
}
}
pm_runtime_get_sync(se_dev->dev);
/* Write key length */
se_writel(se_dev, ((rsa_ctx->mod_len / 64) - 1),
SE_RSA_KEY_SIZE_REG_OFFSET);
/* Write exponent size in 32 bytes */
se_writel(se_dev, (rsa_ctx->exp_len / 4),
SE_RSA_EXP_SIZE_REG_OFFSET);
val = SE_CONFIG_ENC_ALG(ALG_RSA) |
SE_CONFIG_DEC_ALG(ALG_NOP) |
SE_CONFIG_DST(DST_MEMORY);
se_writel(se_dev, val, SE_CONFIG_REG_OFFSET);
se_writel(se_dev, RSA_KEY_SLOT(rsa_ctx->slot->slot_num), SE_RSA_CONFIG);
se_writel(se_dev, SE_CRYPTO_INPUT_SEL(INPUT_AHB), SE_CRYPTO_REG_OFFSET);
ret1 = tegra_se_start_operation(se_dev, 256, false, true);
if (ret1)
dev_err(se_dev->dev, "%s start op failed\n", __func__);
if (req->src == req->dst) {
tegra_unmap_sg(se_dev->dev, req->src, DMA_BIDIRECTIONAL,
req->src_len);
} else {
tegra_unmap_sg(se_dev->dev, req->src, DMA_TO_DEVICE,
req->src_len);
tegra_unmap_sg(se_dev->dev, req->dst, DMA_FROM_DEVICE,
req->dst_len);
}
pm_runtime_put_sync(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret1;
}
static void tegra_se_rsa_exit(struct crypto_akcipher *tfm)
{
struct tegra_se_aes_rsa_context *ctx = akcipher_tfm_ctx(tfm);
tegra_se_rsa_free_key_slot(ctx->slot);
ctx->slot = NULL;
}
static inline struct tegra_se_dh_context *tegra_se_dh_get_ctx(
struct crypto_kpp *tfm)
{
return kpp_tfm_ctx(tfm);
}
static int tegra_se_dh_check_params_length(unsigned int p_len)
{
if (p_len < MIN_DH_SZ_BITS) {
pr_err("DH Modulus length not in range\n");
return -EDOM;
}
return 0;
}
static int tegra_se_dh_set_params(struct tegra_se_dh_context *ctx,
struct dh *params)
{
int ret = 0;
ret = tegra_se_dh_check_params_length(params->p_size << 3);
if (ret)
return ret;
ctx->key = (void *)params->key;
ctx->key_size = params->key_size;
if (!ctx->key) {
dev_err(ctx->se_dev->dev, "Invalid DH Key\n");
return -ENODATA;
}
ctx->p = (void *)params->p;
ctx->p_size = params->p_size;
if (!ctx->p) {
dev_err(ctx->se_dev->dev, "Invalid DH Modulus\n");
return -ENODATA;
}
ctx->g = (void *)params->g;
ctx->g_size = params->g_size;
if (!ctx->g) {
dev_err(ctx->se_dev->dev, "Invalid DH generator\n");
return -ENODATA;
}
if (ctx->g_size > ctx->p_size) {
dev_err(ctx->se_dev->dev, "Invalid DH generator size\n");
return -EDOM;
}
return 0;
}
static int tegra_se_dh_setkey(struct crypto_kpp *tfm)
{
struct tegra_se_dh_context *ctx = tegra_se_dh_get_ctx(tfm);
struct tegra_se_dev *se_dev;
u32 module_key_length = 0;
u32 exponent_key_length = 0;
u32 pkt, val;
u32 key_size_words;
u32 key_word_size = 4;
u32 *pkeydata;
int i, err = 0;
struct tegra_se_rsa_slot *pslot;
unsigned long freq = 0;
if (!ctx) {
pr_err("Invalid DH context\n");
return -EINVAL;
}
se_dev = ctx->se_dev;
pkeydata = (u32 *)ctx->key;
/* Allocate rsa key slot */
if (!ctx->slot) {
pslot = tegra_se_alloc_rsa_key_slot();
if (!pslot) {
dev_err(se_dev->dev, "no free key slot\n");
return -ENOMEM;
}
ctx->slot = pslot;
}
module_key_length = ctx->p_size;
exponent_key_length = ctx->key_size;
if (!(((module_key_length / 64) >= 1) &&
((module_key_length / 64) <= 4))) {
tegra_se_rsa_free_key_slot(ctx->slot);
dev_err(se_dev->dev, "DH Modulus length not in rane\n");
return -EDOM;
}
freq = se_dev->chipdata->rsa_freq;
if (se_dev->pclk && !se_dev->chipdata->const_freq) {
err = clk_set_rate(se_dev->pclk, freq);
if (err) {
dev_err(se_dev->dev, "clock set_rate failed.\n");
tegra_se_rsa_free_key_slot(ctx->slot);
return err;
}
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if (exponent_key_length) {
key_size_words = (exponent_key_length / key_word_size);
/* Write exponent */
for (i = (key_size_words - 1); i >= 0; i--) {
pkt = RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_EXP) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
se_writel(se_dev, be32_to_cpu(*pkeydata++),
SE_RSA_KEYTABLE_DATA);
}
}
if (module_key_length) {
pkeydata = (u32 *)ctx->p;
key_size_words = (module_key_length / key_word_size);
/* Write modulus */
for (i = (key_size_words - 1); i >= 0; i--) {
pkt = RSA_KEY_INPUT_MODE(RSA_KEY_INPUT_MODE_REG) |
RSA_KEY_NUM(ctx->slot->slot_num) |
RSA_KEY_TYPE(RSA_KEY_TYPE_MOD) |
RSA_KEY_PKT_WORD_ADDR(i);
val = SE_RSA_KEYTABLE_PKT(pkt);
se_writel(se_dev, val, SE_RSA_KEYTABLE_ADDR);
se_writel(se_dev, be32_to_cpu(*pkeydata++),
SE_RSA_KEYTABLE_DATA);
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static void tegra_se_fix_endianness(struct tegra_se_dev *se_dev,
struct scatterlist *sg, u32 num_sgs,
u32 nbytes, bool be)
{
int j, k;
sg_copy_to_buffer(sg, num_sgs, se_dev->dh_buf1, nbytes);
for (j = (nbytes / 4 - 1), k = 0; j >= 0; j--, k++) {
if (be)
se_dev->dh_buf2[k] = be32_to_cpu(se_dev->dh_buf1[j]);
else
se_dev->dh_buf2[k] = cpu_to_be32(se_dev->dh_buf1[j]);
}
sg_copy_from_buffer(sg, num_sgs, se_dev->dh_buf2, nbytes);
}
static int tegra_se_dh_compute_value(struct kpp_request *req)
{
struct crypto_kpp *tfm = NULL;
struct tegra_se_dh_context *dh_ctx = NULL;
struct tegra_se_dev *se_dev;
struct scatterlist *src_sg;
struct tegra_se_ll *src_ll, *dst_ll;
u32 num_src_sgs, num_dst_sgs;
u8 *base_buff = NULL;
struct scatterlist src;
int err, j;
u32 val, total, zpad_sz;
if (!req) {
pr_err("Invalid DH request\n");
return -EINVAL;
}
tfm = crypto_kpp_reqtfm(req);
if (!tfm) {
pr_err("Invalid DH transform\n");
return -EINVAL;
}
dh_ctx = tegra_se_dh_get_ctx(tfm);
if (!dh_ctx || !dh_ctx->slot) {
pr_err("Invalid DH context\n");
return -EINVAL;
}
se_dev = dh_ctx->se_dev;
if (req->src) {
src_sg = req->src;
total = req->src_len;
} else {
base_buff = (u8 *)devm_kzalloc(se_dev->dev,
dh_ctx->p_size, GFP_KERNEL);
if (!base_buff)
return -ENOMEM;
if (dh_ctx->g_size < dh_ctx->p_size) {
zpad_sz = dh_ctx->p_size - dh_ctx->g_size;
for (j = 0; j < zpad_sz; j++)
base_buff[j] = 0x0;
for (j = zpad_sz; j < dh_ctx->p_size; j++)
base_buff[j] = *(u8 *)(dh_ctx->g++);
dh_ctx->g_size = dh_ctx->p_size;
} else {
memcpy(base_buff, (u8 *)(dh_ctx->g), dh_ctx->g_size);
}
sg_init_one(&src, base_buff, dh_ctx->g_size);
src_sg = &src;
total = dh_ctx->g_size;
}
num_src_sgs = tegra_se_count_sgs(src_sg, total);
num_dst_sgs = tegra_se_count_sgs(req->dst, req->dst_len);
if ((num_src_sgs > SE_MAX_SRC_SG_COUNT) ||
(num_dst_sgs > SE_MAX_DST_SG_COUNT)) {
dev_err(se_dev->dev, "num of SG buffers are more\n");
err = -EDOM;
goto free;
}
tegra_se_fix_endianness(se_dev, src_sg, num_src_sgs, total, true);
*se_dev->src_ll_buf = num_src_sgs - 1;
*se_dev->dst_ll_buf = num_dst_sgs - 1;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
err = tegra_map_sg(se_dev->dev, src_sg, 1, DMA_TO_DEVICE,
src_ll, total);
if (!err) {
err = -EINVAL;
goto free;
}
err = tegra_map_sg(se_dev->dev, req->dst, 1, DMA_FROM_DEVICE,
dst_ll, req->dst_len);
if (!err) {
err = -EINVAL;
goto unmap;
}
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
/* Write key length */
se_writel(se_dev, ((dh_ctx->p_size / 64) - 1),
SE_RSA_KEY_SIZE_REG_OFFSET);
/* Write exponent size in 32 bytes */
se_writel(se_dev, (dh_ctx->key_size / 4),
SE_RSA_EXP_SIZE_REG_OFFSET);
val = SE_CONFIG_ENC_ALG(ALG_RSA) |
SE_CONFIG_DEC_ALG(ALG_NOP) |
SE_CONFIG_DST(DST_MEMORY);
se_writel(se_dev, val, SE_CONFIG_REG_OFFSET);
se_writel(se_dev, RSA_KEY_SLOT(dh_ctx->slot->slot_num), SE_RSA_CONFIG);
se_writel(se_dev, SE_CRYPTO_INPUT_SEL(INPUT_AHB), SE_CRYPTO_REG_OFFSET);
err = tegra_se_start_operation(se_dev, 256, false, true);
if (err) {
dev_err(se_dev->dev,
"tegra_se_aes_rsa_digest:: start op failed\n");
goto exit;
}
tegra_se_fix_endianness(se_dev, req->dst, num_dst_sgs,
req->dst_len, false);
exit:
pm_runtime_put_sync(se_dev->dev);
mutex_unlock(&se_hw_lock);
tegra_unmap_sg(se_dev->dev, req->dst, DMA_FROM_DEVICE, req->dst_len);
unmap:
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE, total);
free:
if (!req->src)
devm_kfree(se_dev->dev, base_buff);
return err;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
static int tegra_se_dh_set_secret(struct crypto_kpp *tfm, const void *buf,
unsigned int len)
#else
static int tegra_se_dh_set_secret(struct crypto_kpp *tfm, void *buf,
unsigned int len)
#endif
{
int ret = 0;
struct tegra_se_dh_context *ctx = tegra_se_dh_get_ctx(tfm);
struct dh params;
ctx->se_dev = sg_tegra_se_dev;
ret = crypto_dh_decode_key(buf, len, &params);
if (ret) {
dev_err(ctx->se_dev->dev, "failed to decode DH input\n");
return ret;
}
ret = tegra_se_dh_set_params(ctx, &params);
if (ret)
return ret;
ret = tegra_se_dh_setkey(tfm);
if (ret)
return ret;
return 0;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
static unsigned int tegra_se_dh_max_size(struct crypto_kpp *tfm)
#else
static int tegra_se_dh_max_size(struct crypto_kpp *tfm)
#endif
{
struct tegra_se_dh_context *ctx = tegra_se_dh_get_ctx(tfm);
return ctx->p_size;
}
static void tegra_se_dh_exit_tfm(struct crypto_kpp *tfm)
{
struct tegra_se_dh_context *ctx = tegra_se_dh_get_ctx(tfm);
tegra_se_rsa_free_key_slot(ctx->slot);
ctx->key = NULL;
ctx->p = NULL;
ctx->g = NULL;
}
static struct kpp_alg dh_algs[] = {
{
.set_secret = tegra_se_dh_set_secret,
.generate_public_key = tegra_se_dh_compute_value,
.compute_shared_secret = tegra_se_dh_compute_value,
.max_size = tegra_se_dh_max_size,
.exit = tegra_se_dh_exit_tfm,
.base = {
.cra_name = "dh",
.cra_driver_name = "tegra-se-dh",
.cra_priority = 300,
.cra_module = THIS_MODULE,
.cra_ctxsize = sizeof(struct tegra_se_dh_context),
}
}
};
static struct rng_alg rng_algs[] = {
{
.generate = tegra_se_rng_drbg_get_random,
.seed = tegra_se_rng_drbg_reset,
.seedsize = TEGRA_SE_RNG_SEED_SIZE,
.base = {
.cra_name = "rng_drbg",
.cra_driver_name = "rng_drbg-aes-tegra",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_RNG,
.cra_ctxsize = sizeof(struct tegra_se_rng_context),
.cra_module = THIS_MODULE,
.cra_init = tegra_se_rng_drbg_init,
.cra_exit = tegra_se_rng_drbg_exit,
}
}
};
static struct crypto_alg aes_algs[] = {
{
.cra_name = "cbc(aes)",
.cra_driver_name = "cbc-aes-tegra",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = TEGRA_SE_AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_aes_context),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_aes_cra_init,
.cra_exit = tegra_se_aes_cra_exit,
.cra_u.ablkcipher = {
.min_keysize = TEGRA_SE_AES_MIN_KEY_SIZE,
.max_keysize = TEGRA_SE_AES_MAX_KEY_SIZE,
.ivsize = TEGRA_SE_AES_IV_SIZE,
.setkey = tegra_se_aes_setkey,
.encrypt = tegra_se_aes_cbc_encrypt,
.decrypt = tegra_se_aes_cbc_decrypt,
}
}, {
.cra_name = "ecb(aes)",
.cra_driver_name = "ecb-aes-tegra",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = TEGRA_SE_AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_aes_context),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_aes_cra_init,
.cra_exit = tegra_se_aes_cra_exit,
.cra_u.ablkcipher = {
.min_keysize = TEGRA_SE_AES_MIN_KEY_SIZE,
.max_keysize = TEGRA_SE_AES_MAX_KEY_SIZE,
.ivsize = TEGRA_SE_AES_IV_SIZE,
.setkey = tegra_se_aes_setkey,
.encrypt = tegra_se_aes_ecb_encrypt,
.decrypt = tegra_se_aes_ecb_decrypt,
}
}, {
.cra_name = "ctr(aes)",
.cra_driver_name = "ctr-aes-tegra",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = TEGRA_SE_AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_aes_context),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_aes_cra_init,
.cra_exit = tegra_se_aes_cra_exit,
.cra_u.ablkcipher = {
.min_keysize = TEGRA_SE_AES_MIN_KEY_SIZE,
.max_keysize = TEGRA_SE_AES_MAX_KEY_SIZE,
.ivsize = TEGRA_SE_AES_IV_SIZE,
.setkey = tegra_se_aes_setkey,
.encrypt = tegra_se_aes_ctr_encrypt,
.decrypt = tegra_se_aes_ctr_decrypt,
.geniv = "eseqiv",
}
}, {
.cra_name = "ofb(aes)",
.cra_driver_name = "ofb-aes-tegra",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
.cra_blocksize = TEGRA_SE_AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_aes_context),
.cra_alignmask = 0,
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_aes_cra_init,
.cra_exit = tegra_se_aes_cra_exit,
.cra_u.ablkcipher = {
.min_keysize = TEGRA_SE_AES_MIN_KEY_SIZE,
.max_keysize = TEGRA_SE_AES_MAX_KEY_SIZE,
.ivsize = TEGRA_SE_AES_IV_SIZE,
.setkey = tegra_se_aes_setkey,
.encrypt = tegra_se_aes_ofb_encrypt,
.decrypt = tegra_se_aes_ofb_decrypt,
.geniv = "eseqiv",
}
},
};
static struct ahash_alg hash_algs[] = {
{
.init = tegra_se_aes_cmac_init,
.update = tegra_se_aes_cmac_update,
.final = tegra_se_aes_cmac_final,
.finup = tegra_se_aes_cmac_finup,
.digest = tegra_se_aes_cmac_digest,
.setkey = tegra_se_aes_cmac_setkey,
.halg.digestsize = TEGRA_SE_AES_CMAC_DIGEST_SIZE,
.halg.statesize = TEGRA_SE_AES_CMAC_STATE_SIZE,
.halg.base = {
.cra_name = "cmac(aes)",
.cra_driver_name = "tegra-se-cmac(aes)",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = TEGRA_SE_AES_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_aes_cmac_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_aes_cmac_cra_init,
.cra_exit = tegra_se_aes_cmac_cra_exit,
}
}, {
.init = tegra_se_sha_init,
.update = tegra_se_sha_update,
.final = tegra_se_sha_final,
.finup = tegra_se_sha_finup,
.digest = tegra_se_sha_digest,
.halg.digestsize = SHA1_DIGEST_SIZE,
.halg.statesize = SHA1_STATE_SIZE,
.halg.base = {
.cra_name = "sha1",
.cra_driver_name = "tegra-se-sha1",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_sha_cra_init,
.cra_exit = tegra_se_sha_cra_exit,
}
}, {
.init = tegra_se_sha_init,
.update = tegra_se_sha_update,
.final = tegra_se_sha_final,
.finup = tegra_se_sha_finup,
.digest = tegra_se_sha_digest,
.halg.digestsize = SHA224_DIGEST_SIZE,
.halg.statesize = SHA224_STATE_SIZE,
.halg.base = {
.cra_name = "sha224",
.cra_driver_name = "tegra-se-sha224",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = SHA224_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_sha_cra_init,
.cra_exit = tegra_se_sha_cra_exit,
}
}, {
.init = tegra_se_sha_init,
.update = tegra_se_sha_update,
.final = tegra_se_sha_final,
.finup = tegra_se_sha_finup,
.digest = tegra_se_sha_digest,
.halg.digestsize = SHA256_DIGEST_SIZE,
.halg.statesize = SHA256_STATE_SIZE,
.halg.base = {
.cra_name = "sha256",
.cra_driver_name = "tegra-se-sha256",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_sha_cra_init,
.cra_exit = tegra_se_sha_cra_exit,
}
}, {
.init = tegra_se_sha_init,
.update = tegra_se_sha_update,
.final = tegra_se_sha_final,
.finup = tegra_se_sha_finup,
.digest = tegra_se_sha_digest,
.halg.digestsize = SHA384_DIGEST_SIZE,
.halg.statesize = SHA384_STATE_SIZE,
.halg.base = {
.cra_name = "sha384",
.cra_driver_name = "tegra-se-sha384",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = SHA384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_sha_cra_init,
.cra_exit = tegra_se_sha_cra_exit,
}
}, {
.init = tegra_se_sha_init,
.update = tegra_se_sha_update,
.final = tegra_se_sha_final,
.finup = tegra_se_sha_finup,
.digest = tegra_se_sha_digest,
.halg.digestsize = SHA512_DIGEST_SIZE,
.halg.statesize = SHA512_STATE_SIZE,
.halg.base = {
.cra_name = "sha512",
.cra_driver_name = "tegra-se-sha512",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_AHASH,
.cra_blocksize = SHA512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_sha_cra_init,
.cra_exit = tegra_se_sha_cra_exit,
}
}
};
static struct shash_alg shash_algs[] = {
{
.init = tegra_se_shash_init,
.update = tegra_se_shash_update,
.final = tegra_se_shash_final,
.digest = tegra_se_shash_digest,
.digestsize = SHA1_DIGEST_SIZE,
.statesize = SHA1_STATE_SIZE,
.base = {
.cra_name = "sha1",
.cra_driver_name = "tegra-se-sha1-shash",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
.cra_blocksize = SHA1_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_shash_cra_init,
.cra_exit = tegra_se_shash_cra_exit,
}
}, {
.init = tegra_se_shash_init,
.update = tegra_se_shash_update,
.final = tegra_se_shash_final,
.digest = tegra_se_shash_digest,
.digestsize = SHA224_DIGEST_SIZE,
.statesize = SHA224_STATE_SIZE,
.base = {
.cra_name = "sha224",
.cra_driver_name = "tegra-se-sha224-shash",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
.cra_blocksize = SHA224_DIGEST_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_shash_cra_init,
.cra_exit = tegra_se_shash_cra_exit,
}
}, {
.init = tegra_se_shash_init,
.update = tegra_se_shash_update,
.final = tegra_se_shash_final,
.digest = tegra_se_shash_digest,
.digestsize = SHA256_DIGEST_SIZE,
.statesize = SHA256_STATE_SIZE,
.base = {
.cra_name = "sha256",
.cra_driver_name = "tegra-se-sha256-shash",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
.cra_blocksize = SHA256_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_shash_cra_init,
.cra_exit = tegra_se_shash_cra_exit,
}
}, {
.init = tegra_se_shash_init,
.update = tegra_se_shash_update,
.final = tegra_se_shash_final,
.digest = tegra_se_shash_digest,
.digestsize = SHA384_DIGEST_SIZE,
.statesize = SHA384_STATE_SIZE,
.base = {
.cra_name = "sha384",
.cra_driver_name = "tegra-se-sha384-shash",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
.cra_blocksize = SHA384_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_shash_cra_init,
.cra_exit = tegra_se_shash_cra_exit,
}
}, {
.init = tegra_se_shash_init,
.update = tegra_se_shash_update,
.final = tegra_se_shash_final,
.digest = tegra_se_shash_digest,
.digestsize = SHA512_DIGEST_SIZE,
.statesize = SHA512_STATE_SIZE,
.base = {
.cra_name = "sha512",
.cra_driver_name = "tegra-se-sha512-shash",
.cra_priority = 300,
.cra_flags = CRYPTO_ALG_TYPE_SHASH,
.cra_blocksize = SHA512_BLOCK_SIZE,
.cra_ctxsize = sizeof(struct tegra_se_sha_context),
.cra_alignmask = 0,
.cra_module = THIS_MODULE,
.cra_init = tegra_se_shash_cra_init,
.cra_exit = tegra_se_shash_cra_exit,
}
}
};
static struct akcipher_alg rsa_alg = {
.encrypt = tegra_se_rsa_op,
.decrypt = tegra_se_rsa_op,
.sign = tegra_se_rsa_op,
.verify = tegra_se_rsa_op,
.set_priv_key = tegra_se_rsa_setkey,
.set_pub_key = tegra_se_rsa_setkey,
.max_size = tegra_se_rsa_max_size,
.exit = tegra_se_rsa_exit,
.base = {
.cra_name = "rsa-pka0",
.cra_driver_name = "tegra-se-pka0-rsa",
.cra_priority = 300,
.cra_ctxsize = sizeof(struct tegra_se_aes_rsa_context),
.cra_module = THIS_MODULE,
}
};
static bool is_algo_supported(struct device_node *node, char *algo)
{
if (of_property_match_string(node, "supported-algos", algo) >= 0)
return true;
else
return false;
}
static bool is_algo_supported_in_hw(struct tegra_se_dev *se_dev,
const char *algo)
{
if (!strcmp(algo, "ansi_cprng")) {
if (se_dev->chipdata->cprng_supported)
return true;
else
return false;
}
if (!strcmp(algo, "drbg")) {
if (se_dev->chipdata->drbg_supported)
return true;
else
return false;
}
if (!strcmp(algo, "rsa-pka0") || !strcmp(algo, "rsa")) {
if (se_dev->chipdata->rsa_supported) {
if (tegra_chip_get_revision() == TEGRA210_REVISION_A01)
return false;
else
return true;
} else {
return false;
}
}
return true;
}
static struct tegra_se_chipdata tegra_se_chipdata = {
.rsa_supported = false,
.cprng_supported = true,
.drbg_supported = false,
.aes_freq = 300000000,
.rng_freq = 300000000,
.sha1_freq = 300000000,
.sha224_freq = 300000000,
.sha256_freq = 300000000,
.sha384_freq = 300000000,
.sha512_freq = 300000000,
.mccif_supported = false,
.rsa_key_rw_op = true,
.aes_keydata_reg_sz = 128,
.ahb_ack = false,
.handle_sc7 = true,
};
static struct tegra_se_chipdata tegra11_se_chipdata = {
.rsa_supported = true,
.cprng_supported = false,
.drbg_supported = true,
.const_freq = false,
.aes_freq = 150000000,
.rng_freq = 150000000,
.sha1_freq = 200000000,
.sha224_freq = 250000000,
.sha256_freq = 250000000,
.sha384_freq = 150000000,
.sha512_freq = 150000000,
.rsa_freq = 350000000,
.mccif_supported = false,
.rsa_key_rw_op = true,
.aes_keydata_reg_sz = 128,
.ahb_ack = false,
.handle_sc7 = true,
};
static struct tegra_se_chipdata tegra21_se_chipdata = {
.rsa_supported = true,
.cprng_supported = false,
.drbg_supported = true,
.const_freq = true,
.aes_freq = 510000000,
.rng_freq = 510000000,
.sha1_freq = 510000000,
.sha224_freq = 510000000,
.sha256_freq = 510000000,
.sha384_freq = 510000000,
.sha512_freq = 510000000,
.rsa_freq = 510000000,
.mccif_supported = true,
.rsa_key_rw_op = false,
.aes_keydata_reg_sz = 32,
.ahb_ack = false,
.handle_sc7 = false,
};
static struct tegra_se_chipdata tegra210b01_se_chipdata = {
.rsa_supported = true,
.cprng_supported = false,
.drbg_supported = true,
.const_freq = true,
.aes_freq = 510000000,
.rng_freq = 510000000,
.sha1_freq = 510000000,
.sha224_freq = 510000000,
.sha256_freq = 510000000,
.sha384_freq = 510000000,
.sha512_freq = 510000000,
.rsa_freq = 510000000,
.mccif_supported = true,
.rsa_key_rw_op = false,
.aes_keydata_reg_sz = 32,
.ahb_ack = true,
.handle_sc7 = false,
};
static const struct of_device_id tegra_se_of_match[] = {
{
.compatible = "nvidia,tegra124-se",
.data = &tegra11_se_chipdata,
},
{
.compatible = "nvidia,tegra210-se",
.data = &tegra21_se_chipdata,
},
{
.compatible = "nvidia,tegra210b01-se",
.data = &tegra210b01_se_chipdata,
}, {
}
};
MODULE_DEVICE_TABLE(of, tegra_se_of_match);
static void tegra_se_fill_se_dev_info(struct tegra_se_dev *se_dev)
{
struct device_node *node = of_node_get(se_dev->dev->of_node);
if (is_algo_supported(node, "aes"))
se_devices[SE_AES] = se_dev;
if (is_algo_supported(node, "drbg"))
se_devices[SE_DRBG] = se_dev;
if (is_algo_supported(node, "sha"))
se_devices[SE_SHA] = se_dev;
if (is_algo_supported(node, "rsa"))
se_devices[SE_RSA] = se_dev;
if (is_algo_supported(node, "cmac"))
se_devices[SE_CMAC] = se_dev;
}
static int tegra_se_probe(struct platform_device *pdev)
{
struct tegra_se_dev *se_dev = NULL;
struct resource *res = NULL;
const struct of_device_id *match;
struct device_node *node;
int err = 0, i = 0, j = 0, k = 0, val;
const char *rsa_name;
se_dev = devm_kzalloc(&pdev->dev, sizeof(*se_dev), GFP_KERNEL);
if (!se_dev)
return -ENOMEM;
if (pdev->dev.of_node) {
match = of_match_device(of_match_ptr(tegra_se_of_match),
&pdev->dev);
if (!match) {
dev_err(&pdev->dev, "Error: No device match found\n");
return -ENODEV;
}
se_dev->chipdata = (struct tegra_se_chipdata *)match->data;
} else {
se_dev->chipdata =
(struct tegra_se_chipdata *)pdev->id_entry->driver_data;
}
if (se_dev->chipdata->ahb_ack) {
val = tegra_ahb_get_master_id(&pdev->dev);
if (val < 0) {
dev_err(&pdev->dev, "Error: AHB master id not found\n");
return -EINVAL;
}
se_dev->ahb_id = val;
}
spin_lock_init(&se_dev->lock);
crypto_init_queue(&se_dev->queue, TEGRA_SE_CRYPTO_QUEUE_LENGTH);
platform_set_drvdata(pdev, se_dev);
se_dev->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
se_dev->io_reg = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(se_dev->io_reg)) {
dev_err(se_dev->dev, "ioremap failed\n");
return PTR_ERR(se_dev->io_reg);
}
se_dev->irq = platform_get_irq(pdev, 0);
if (se_dev->irq < 0) {
dev_err(se_dev->dev, "platform_get_irq failed\n");
return -ENXIO;
}
/* Initialize the clock */
se_dev->pclk = devm_clk_get(&pdev->dev, "se");
if (IS_ERR(se_dev->pclk)) {
dev_err(se_dev->dev, "clock intialization failed (%ld)\n",
PTR_ERR(se_dev->pclk));
return PTR_ERR(se_dev->pclk);
}
if (se_dev->chipdata->const_freq)
err = clk_set_rate(se_dev->pclk, 510000000);
else
err = clk_set_rate(se_dev->pclk, ULONG_MAX);
if (err) {
dev_err(se_dev->dev, "clock set_rate failed.\n");
return err;
}
node = of_node_get(se_dev->dev->of_node);
if (is_algo_supported(node, "aes")) {
err = tegra_init_key_slot(se_dev);
if (err) {
dev_err(se_dev->dev, "init_key_slot failed\n");
return err;
}
err = tegra_init_rsa_key_slot(se_dev);
if (err) {
dev_err(se_dev->dev, "init_rsa_key_slot failed\n");
return err;
}
se_work_q = alloc_workqueue("se_work_q",
WQ_HIGHPRI | WQ_UNBOUND, 16);
if (!se_work_q) {
dev_err(se_dev->dev, "alloc_workqueue failed\n");
return -ENOMEM;
}
}
init_completion(&se_dev->complete);
sg_tegra_se_dev = se_dev;
tegra_pd_add_device(se_dev->dev);
pm_runtime_enable(se_dev->dev);
tegra_se_fill_se_dev_info(se_dev);
tegra_se_key_read_disable_all();
err = devm_request_irq(&pdev->dev, se_dev->irq, tegra_se_irq, 0,
DRIVER_NAME, se_dev);
if (err) {
dev_err(se_dev->dev, "request_irq failed - irq[%d] err[%d]\n",
se_dev->irq, err);
goto fail;
}
se_dev->dev->coherent_dma_mask = DMA_BIT_MASK(32);
err = tegra_se_alloc_ll_buf(se_dev, SE_MAX_SRC_SG_COUNT,
SE_MAX_DST_SG_COUNT);
if (err) {
dev_err(se_dev->dev, "can not allocate ll dma buffer\n");
goto fail;
}
if (is_algo_supported(node, "drbg") &&
(is_algo_supported_in_hw(se_dev, rng_algs[0].base.cra_name))) {
INIT_LIST_HEAD(&rng_algs[0].base.cra_list);
err = crypto_register_rng(&rng_algs[0]);
if (err) {
dev_err(se_dev->dev, "crypto_register_rng failed\n");
goto fail_rng;
}
}
if (is_algo_supported(node, "aes")) {
for (i = 0; i < ARRAY_SIZE(aes_algs); i++) {
INIT_LIST_HEAD(&aes_algs[i].cra_list);
err = crypto_register_alg(&aes_algs[i]);
if (err) {
dev_err(se_dev->dev,
"crypto_register_alg failed for %s\n",
aes_algs[i].cra_name);
goto fail_alg;
}
}
err = crypto_register_ahash(&hash_algs[0]);
if (err) {
dev_err(se_dev->dev,
"crypto_register_sha alg failed index[%d] err: %d\n",
j, err);
goto fail_alg1;
}
}
if (is_algo_supported(node, "sha")) {
for (j = 0; j < ARRAY_SIZE(shash_algs); j++) {
err = crypto_register_shash(&shash_algs[j]);
if (err) {
dev_err(se_dev->dev,
"crypto_register_shash failed for %s\n",
shash_algs[j].base.cra_name);
goto fail_shash;
}
}
for (j = 1; j < ARRAY_SIZE(hash_algs); j++) {
err = crypto_register_ahash(&hash_algs[j]);
if (err) {
dev_err(se_dev->dev,
"crypto_register_ahash failed for %s\n",
hash_algs[j].halg.base.cra_name);
goto fail_ahash;
}
}
}
if (is_algo_supported(node, "rsa")) {
err = of_property_read_u32(node, "pka0-rsa-priority", &val);
if (!err)
rsa_alg.base.cra_priority = val;
err = of_property_read_string(node, "pka0-rsa-name", &rsa_name);
if (!err)
strcpy(rsa_alg.base.cra_name, rsa_name);
if (is_algo_supported_in_hw(se_dev, rsa_alg.base.cra_name)) {
err = crypto_register_akcipher(&rsa_alg);
if (err) {
dev_err(se_dev->dev,
"crypto_register_akcipher failed %d\n", err);
goto fail_akcipher;
}
err = crypto_register_kpp(&dh_algs[0]);
if (err) {
dev_err(se_dev->dev, "crypto_register_kpp failed %d\n",
err);
goto fail_kpp;
}
se_dev->dh_buf1 = (u32 *)devm_kzalloc(se_dev->dev,
TEGRA_SE_RSA2048_INPUT_SIZE,
GFP_KERNEL);
se_dev->dh_buf2 = (u32 *)devm_kzalloc(se_dev->dev,
TEGRA_SE_RSA2048_INPUT_SIZE,
GFP_KERNEL);
if (!se_dev->dh_buf1 || !se_dev->dh_buf2)
goto fail_kpp;
}
}
se_dev->sg_in_buf = dmam_alloc_coherent(
&pdev->dev, DISK_ENCR_BUF_SZ,
&se_dev->sg_in_buf_adr, GFP_KERNEL);
se_dev->sg_out_buf = dmam_alloc_coherent(
&pdev->dev, DISK_ENCR_BUF_SZ, &se_dev->sg_out_buf_adr,
GFP_KERNEL);
#if defined(CONFIG_PM)
if (!se_dev->chipdata->drbg_supported)
se_dev->ctx_save_buf = dma_alloc_coherent(
se_dev->dev, SE_CONTEXT_BUFER_SIZE,
&se_dev->ctx_save_buf_adr, GFP_KERNEL | GFP_DMA);
else
se_dev->ctx_save_buf = dma_alloc_coherent(
se_dev->dev, SE_CONTEXT_DRBG_BUFER_SIZE,
&se_dev->ctx_save_buf_adr, GFP_KERNEL | GFP_DMA);
if (!se_dev->ctx_save_buf) {
dev_err(se_dev->dev, "Context save buffer alloc filed\n");
err = -ENOMEM;
goto fail_ctx_buf;
}
#endif
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if (se_dev->chipdata->drbg_supported &&
(tegra_get_chip_id() != TEGRA114)) {
se_writel(se_dev,
SE_RNG_SRC_CONFIG_RO_ENT_SRC(DRBG_RO_ENT_SRC_ENABLE) |
SE_RNG_SRC_CONFIG_RO_ENT_SRC_LOCK(
DRBG_RO_ENT_SRC_LOCK_ENABLE),
SE_RNG_SRC_CONFIG_REG_OFFSET);
se_dev->chipdata->drbg_src_entropy_clk_enable = true;
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
if (se_dev->chipdata->drbg_src_entropy_clk_enable) {
/* Initialize the entropy clock */
se_dev->enclk = devm_clk_get(&pdev->dev, "entropy");
if (IS_ERR(se_dev->enclk)) {
err = PTR_ERR(se_dev->enclk);
dev_err(se_dev->dev,
"entropy clock init failed(%d)\n", err);
goto fail_clk_get;
}
}
dev_info(se_dev->dev, "%s: complete", __func__);
return 0;
fail_clk_get:
#if defined(CONFIG_PM)
dma_free_coherent(se_dev->dev, !se_dev->chipdata->drbg_supported ?
SE_CONTEXT_BUFER_SIZE : SE_CONTEXT_DRBG_BUFER_SIZE,
se_dev->ctx_save_buf, se_dev->ctx_save_buf_adr);
fail_ctx_buf:
#endif
if (is_algo_supported(node, "rsa")) {
if (is_algo_supported_in_hw(se_dev, rsa_alg.base.cra_name))
crypto_unregister_kpp(&dh_algs[0]);
}
fail_kpp:
if (is_algo_supported(node, "rsa")) {
if (is_algo_supported_in_hw(se_dev, rsa_alg.base.cra_name))
crypto_unregister_akcipher(&rsa_alg);
}
fail_akcipher:
if (is_algo_supported(node, "sha")) {
for (k = 1; k < j; k++)
crypto_unregister_ahash(&hash_algs[k]);
}
fail_ahash:
if (is_algo_supported(node, "sha")) {
for (k = 0; k < j; k++)
crypto_unregister_shash(&shash_algs[k]);
}
fail_shash:
if (is_algo_supported(node, "aes"))
crypto_unregister_ahash(&hash_algs[0]);
fail_alg1:
if (is_algo_supported(node, "aes")) {
for (k = 0; k < i; k++)
crypto_unregister_alg(&aes_algs[k]);
}
fail_alg:
if (is_algo_supported(node, "drbg")) {
if (is_algo_supported_in_hw(se_dev, rng_algs[0].base.cra_name))
crypto_unregister_rng(&rng_algs[0]);
}
fail_rng:
tegra_se_free_ll_buf(se_dev);
fail:
pm_runtime_disable(se_dev->dev);
sg_tegra_se_dev = NULL;
destroy_workqueue(se_work_q);
return err;
}
static int tegra_se_remove(struct platform_device *pdev)
{
struct tegra_se_dev *se_dev = platform_get_drvdata(pdev);
struct device_node *node;
int i;
if (!se_dev) {
pr_err("Device is NULL\n");
return -ENODEV;
}
node = of_node_get(se_dev->dev->of_node);
pm_runtime_disable(se_dev->dev);
cancel_work_sync(&se_work);
if (se_work_q)
destroy_workqueue(se_work_q);
#if defined(CONFIG_PM)
dma_free_coherent(se_dev->dev, !se_dev->chipdata->drbg_supported ?
SE_CONTEXT_BUFER_SIZE : SE_CONTEXT_DRBG_BUFER_SIZE,
se_dev->ctx_save_buf, se_dev->ctx_save_buf_adr);
#endif
if (is_algo_supported(node, "rsa")) {
if (is_algo_supported_in_hw(se_dev, rsa_alg.base.cra_name)) {
crypto_unregister_akcipher(&rsa_alg);
crypto_unregister_kpp(&dh_algs[0]);
}
}
if (is_algo_supported(node, "sha")) {
for (i = 1; i < ARRAY_SIZE(hash_algs); i++)
crypto_unregister_ahash(&hash_algs[i]);
for (i = 0; i < ARRAY_SIZE(shash_algs); i++)
crypto_unregister_shash(&shash_algs[i]);
}
if (is_algo_supported(node, "aes")) {
for (i = 0; i < ARRAY_SIZE(aes_algs); i++)
crypto_unregister_alg(&aes_algs[i]);
crypto_unregister_ahash(&hash_algs[0]);
}
if (is_algo_supported(node, "aes")) {
if (is_algo_supported_in_hw(se_dev, rng_algs[0].base.cra_name))
crypto_unregister_rng(&rng_algs[0]);
}
tegra_se_free_ll_buf(se_dev);
sg_tegra_se_dev = NULL;
return 0;
}
#if defined(CONFIG_PM)
static int tegra_se_generate_rng_key(struct tegra_se_dev *se_dev)
{
int ret = 0;
u32 val = 0;
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
/* Configure algorithm */
val = SE_CONFIG_ENC_ALG(ALG_RNG) | SE_CONFIG_ENC_MODE(MODE_KEY128) |
SE_CONFIG_DST(DST_KEYTAB);
se_writel(se_dev, val, SE_CONFIG_REG_OFFSET);
/* Configure destination key index number */
val = SE_CRYPTO_KEYTABLE_DST_KEY_INDEX(srk_slot.slot_num) |
SE_CRYPTO_KEYTABLE_DST_WORD_QUAD(KEYS_0_3);
se_writel(se_dev, val, SE_CRYPTO_KEYTABLE_DST_REG_OFFSET);
/* Configure crypto */
val = SE_CRYPTO_INPUT_SEL(INPUT_RANDOM) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT) |
SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(ssk_slot.slot_num) |
SE_CRYPTO_IV_SEL(IV_ORIGINAL);
se_writel(se_dev, val, SE_CRYPTO_REG_OFFSET);
ret = tegra_se_start_operation(se_dev, TEGRA_SE_KEY_128_SIZE,
false, true);
return ret;
}
static int tegra_se_generate_srk(struct tegra_se_dev *se_dev)
{
int ret = 0;
u32 val = 0;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if (se_dev->chipdata->drbg_src_entropy_clk_enable) {
/* enable clock for entropy */
ret = clk_prepare_enable(se_dev->enclk);
if (ret) {
dev_err(se_dev->dev, "entropy clock enable failed\n");
goto out;
}
}
ret = tegra_se_generate_rng_key(se_dev);
if (ret)
goto fail;
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
val = SE_CONFIG_ENC_ALG(ALG_RNG) | SE_CONFIG_ENC_MODE(MODE_KEY128) |
SE_CONFIG_DEC_ALG(ALG_NOP) | SE_CONFIG_DST(DST_SRK);
se_writel(se_dev, val, SE_CONFIG_REG_OFFSET);
if (!se_dev->chipdata->drbg_supported)
val = SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT) |
SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(srk_slot.slot_num) |
SE_CRYPTO_IV_SEL(IV_UPDATED);
else
val = SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT) |
SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(srk_slot.slot_num) |
SE_CRYPTO_IV_SEL(IV_UPDATED) |
SE_CRYPTO_INPUT_SEL(INPUT_RANDOM);
se_writel(se_dev, val, SE_CRYPTO_REG_OFFSET);
if (se_dev->chipdata->drbg_supported) {
se_writel(se_dev, SE_RNG_CONFIG_MODE(DRBG_MODE_FORCE_RESEED) |
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY),
SE_RNG_CONFIG_REG_OFFSET);
se_writel(se_dev, RNG_RESEED_INTERVAL,
SE_RNG_RESEED_INTERVAL_REG_OFFSET);
}
ret = tegra_se_start_operation(se_dev, TEGRA_SE_KEY_128_SIZE,
false, true);
fail:
if (se_dev->chipdata->drbg_src_entropy_clk_enable)
clk_disable_unprepare(se_dev->enclk);
out:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_generate_random_data(struct tegra_se_dev *se_dev)
{
struct tegra_se_ll *src_ll, *dst_ll;
int ret = 0;
u32 val;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if (se_dev->chipdata->drbg_src_entropy_clk_enable) {
/* enable clock for entropy */
ret = clk_prepare_enable(se_dev->enclk);
if (ret) {
dev_err(se_dev->dev, "entropy clock enable failed\n");
goto out;
}
}
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
src_ll->addr = se_dev->ctx_save_buf_adr;
src_ll->data_len = SE_CONTEXT_SAVE_RANDOM_DATA_SIZE;
dst_ll->addr = se_dev->ctx_save_buf_adr;
dst_ll->data_len = SE_CONTEXT_SAVE_RANDOM_DATA_SIZE;
tegra_se_config_algo(se_dev, SE_AES_OP_MODE_RNG_X931, true,
TEGRA_SE_KEY_128_SIZE);
/* Configure crypto */
val = SE_CRYPTO_INPUT_SEL(INPUT_RANDOM) |
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT) |
SE_CRYPTO_HASH(HASH_DISABLE) |
SE_CRYPTO_KEY_INDEX(srk_slot.slot_num) |
SE_CRYPTO_IV_SEL(IV_ORIGINAL);
se_writel(se_dev, val, SE_CRYPTO_REG_OFFSET);
if (se_dev->chipdata->drbg_supported)
se_writel(se_dev, SE_RNG_CONFIG_MODE(DRBG_MODE_FORCE_RESEED) |
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY),
SE_RNG_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(
se_dev, SE_CONTEXT_SAVE_RANDOM_DATA_SIZE, false, true);
if (se_dev->chipdata->drbg_src_entropy_clk_enable)
clk_disable_unprepare(se_dev->enclk);
out:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_encrypt_context_data(struct tegra_se_dev *se_dev,
u32 context_offset, u32 data_size)
{
struct tegra_se_ll *src_ll, *dst_ll;
int ret = 0;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf + 1);
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
src_ll->addr = se_dev->ctx_save_buf_adr + context_offset;
src_ll->data_len = data_size;
dst_ll->addr = se_dev->ctx_save_buf_adr + context_offset;
dst_ll->data_len = data_size;
se_writel(se_dev, SE_CONTEXT_SAVE_SRC(MEM),
SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(se_dev, data_size, true, true);
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_sticky_bits_context_save(struct tegra_se_dev *se_dev)
{
struct tegra_se_ll *dst_ll;
int ret = 0, i = 0;
u32 val = 0;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->src_ll_buf = 0;
*se_dev->dst_ll_buf = 0;
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
dst_ll->addr = se_dev->ctx_save_buf_adr +
SE_CONTEXT_SAVE_STICKY_BITS_OFFSET;
dst_ll->data_len = SE_CONTEXT_SAVE_STICKY_BITS_SIZE;
if (!se_dev->chipdata->drbg_supported) {
se_writel(se_dev, SE_CONTEXT_SAVE_SRC(STICKY_BITS),
SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(
se_dev, SE_CONTEXT_SAVE_STICKY_BITS_SIZE, true, true);
} else {
for (i = 0; i < 2; i++) {
val = SE_CONTEXT_SAVE_SRC(STICKY_BITS) |
SE_CONTEXT_SAVE_STICKY_WORD_QUAD(i);
se_writel(se_dev, val,
SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(
se_dev, SE_CONTEXT_SAVE_STICKY_BITS_SIZE,
true, true);
if (ret)
break;
dst_ll->addr += SE_CONTEXT_SAVE_STICKY_BITS_SIZE;
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_keytable_context_save(struct tegra_se_dev *se_dev)
{
struct tegra_se_ll *dst_ll;
int ret = 0, i, j;
u32 val = 0;
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->dst_ll_buf = 0;
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
if (!se_dev->chipdata->drbg_supported)
dst_ll->addr = se_dev->ctx_save_buf_adr +
SE_CONTEXT_SAVE_KEYS_OFFSET;
else
dst_ll->addr = se_dev->ctx_save_buf_adr +
SE11_CONTEXT_SAVE_KEYS_OFFSET;
dst_ll->data_len = TEGRA_SE_KEY_128_SIZE;
for (i = 0; i < TEGRA_SE_KEYSLOT_COUNT; i++) {
for (j = 0; j < 2; j++) {
val = SE_CONTEXT_SAVE_SRC(KEYTABLE) |
SE_CONTEXT_SAVE_KEY_INDEX(i) |
SE_CONTEXT_SAVE_WORD_QUAD(j);
se_writel(se_dev,
val, SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(
se_dev, TEGRA_SE_KEY_128_SIZE, true, true);
if (ret)
break;
dst_ll->addr += TEGRA_SE_KEY_128_SIZE;
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_rsakeytable_context_save(struct tegra_se_dev *se_dev)
{
struct tegra_se_ll *dst_ll;
int ret = 0, word_quad, k, slot;
u32 val = 0, index;
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->dst_ll_buf = 0;
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
dst_ll->addr = se_dev->ctx_save_buf_adr +
SE_CONTEXT_SAVE_RSA_KEYS_OFFSET;
dst_ll->data_len = TEGRA_SE_KEY_128_SIZE;
for (slot = 0; slot < TEGRA_SE_RSA_CONTEXT_SAVE_KEYSLOT_COUNT; slot++) {
/* First the modulus and then the exponent must be
* encrypted and saved. This is repeated for SLOT 0
* and SLOT 1. Hence the order:
* SLOT 0 modulus : RSA_KEY_INDEX : 1
* SLOT 0 exponent : RSA_KEY_INDEX : 0
* SLOT 1 modulus : RSA_KEY_INDEX : 3
* SLOT 1 exponent : RSA_KEY_INDEX : 2
*/
if (slot == 0)
index = 1;
else
index = 3;
/* loop for modulus and exponent */
for (k = 0; k < 2; k++, index--) {
for (word_quad = 0; word_quad < 16; word_quad++) {
val = SE_CONTEXT_SAVE_SRC(RSA_KEYTABLE) |
SE_CONTEXT_SAVE_RSA_KEY_INDEX(index) |
SE_CONTEXT_RSA_WORD_QUAD(word_quad);
se_writel(se_dev, val,
SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(
se_dev, TEGRA_SE_KEY_128_SIZE,
true, true);
if (ret) {
dev_err(se_dev->dev,
"rsa key context save error\n");
break;
}
dst_ll->addr += TEGRA_SE_KEY_128_SIZE;
}
}
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_lp_iv_context_save(struct tegra_se_dev *se_dev,
bool org_iv, u32 context_offset)
{
struct tegra_se_ll *dst_ll;
int ret = 0, i;
u32 val = 0;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
*se_dev->dst_ll_buf = 0;
dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf + 1);
dst_ll->addr = se_dev->ctx_save_buf_adr + context_offset;
dst_ll->data_len = TEGRA_SE_AES_IV_SIZE;
for (i = 0; i < TEGRA_SE_KEYSLOT_COUNT; i++) {
val = SE_CONTEXT_SAVE_SRC(KEYTABLE) |
SE_CONTEXT_SAVE_KEY_INDEX(i) |
(org_iv ? SE_CONTEXT_SAVE_WORD_QUAD(ORIG_IV) :
SE_CONTEXT_SAVE_WORD_QUAD(UPD_IV));
se_writel(se_dev, val, SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(se_dev, TEGRA_SE_AES_IV_SIZE,
true, true);
if (ret)
break;
dst_ll->addr += TEGRA_SE_AES_IV_SIZE;
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int tegra_se_save_SRK(struct tegra_se_dev *se_dev)
{
int ret, val;
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
se_writel(se_dev, SE_CONTEXT_SAVE_SRC(SRK),
SE_CONTEXT_SAVE_CONFIG_REG_OFFSET);
ret = tegra_se_start_operation(se_dev, 0, true, true);
if (ret < 0) {
dev_err(se_dev->dev, "\n LP SRK operation failed\n");
goto out;
}
if ((tegra_get_chip_id() == TEGRA114) &&
se_dev->chipdata->drbg_supported) {
/* clear any pending interrupts */
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
se_writel(se_dev, val, SE_INT_STATUS_REG_OFFSET);
if (!se_dev->polling) {
/* enable interupts */
val = SE_INT_ERROR(INT_ENABLE) |
SE_INT_OP_DONE(INT_ENABLE);
se_writel(se_dev, val, SE_INT_ENABLE_REG_OFFSET);
reinit_completion(&se_dev->complete);
}
val = SE_CONFIG_ENC_ALG(ALG_NOP) |
SE_CONFIG_DEC_ALG(ALG_NOP);
se_writel(se_dev, val, SE_CRYPTO_REG_OFFSET);
se_writel(se_dev, SE_OPERATION(OP_CTX_SAVE),
SE_OPERATION_REG_OFFSET);
if (se_dev->polling) {
/*polling*/
val = se_readl(se_dev, SE_INT_STATUS_REG_OFFSET);
while (!SE_OP_DONE(val, OP_DONE))
val = se_readl(se_dev,
SE_INT_STATUS_REG_OFFSET);
} else {
ret = wait_for_completion_timeout(
&se_dev->complete, msecs_to_jiffies(1000));
if (ret == 0) {
dev_err(se_dev->dev,
"\n LP SRK timed out no interrupt\n");
ret = -ETIMEDOUT;
}
}
}
out:
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return ret;
}
static int se_suspend(struct device *dev, bool polling)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_se_dev *se_dev = platform_get_drvdata(pdev);
int err = 0, i;
unsigned char *dt_buf = NULL;
u8 pdata[SE_CONTEXT_KNOWN_PATTERN_SIZE] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
if (!se_dev) {
pr_err("Device is NULL\n");
return -ENODEV;
}
save_se_device = dev;
if (!se_dev->chipdata->handle_sc7) {
/* SC7 is handled in Secure OS drivers */
return 0;
}
se_dev->polling = polling;
/* Generate SRK */
err = tegra_se_generate_srk(se_dev);
if (err) {
dev_err(se_dev->dev, "\n LP SRK genration failed\n");
goto out;
}
/* Generate random data*/
err = tegra_se_lp_generate_random_data(se_dev);
if (err) {
dev_err(se_dev->dev, "\n LP random pattern generation failed\n");
goto out;
}
/* Encrypt random data */
err = tegra_se_lp_encrypt_context_data(
se_dev, SE_CONTEXT_SAVE_RANDOM_DATA_OFFSET,
SE_CONTEXT_SAVE_RANDOM_DATA_SIZE);
if (err) {
dev_err(se_dev->dev, "\n LP random pattern encryption failed\n");
goto out;
}
/* Sticky bits context save*/
err = tegra_se_lp_sticky_bits_context_save(se_dev);
if (err) {
dev_err(se_dev->dev, "\n LP sticky bits context save failure\n");
goto out;
}
/* Key table context save*/
err = tegra_se_lp_keytable_context_save(se_dev);
if (err) {
dev_err(se_dev->dev, "\n LP key table save failure\n");
goto out;
}
/* Original iv context save*/
err = tegra_se_lp_iv_context_save(se_dev, true,
(se_dev->chipdata->drbg_supported ?
SE11_CONTEXT_ORIGINAL_IV_OFFSET :
SE_CONTEXT_ORIGINAL_IV_OFFSET));
if (err) {
dev_err(se_dev->dev, "\n LP original iv save failure\n");
goto out;
}
/* Updated iv context save*/
err = tegra_se_lp_iv_context_save(se_dev, false,
(se_dev->chipdata->drbg_supported ?
SE11_CONTEXT_UPDATED_IV_OFFSET :
SE_CONTEXT_UPDATED_IV_OFFSET));
if (err) {
dev_err(se_dev->dev, "\n LP updated iv save failure\n");
goto out;
}
if (se_dev->chipdata->drbg_supported) {
/* rsa-key slot table context save*/
err = tegra_se_lp_rsakeytable_context_save(se_dev);
if (err) {
dev_err(se_dev->dev, "\n LP RSA key table save failure\n");
goto out;
}
/* Encrypt known pattern */
dt_buf = (unsigned char *)se_dev->ctx_save_buf;
dt_buf += SE_CONTEXT_SAVE_RSA_KNOWN_PATTERN_OFFSET;
for (i = 0; i < SE_CONTEXT_KNOWN_PATTERN_SIZE; i++)
dt_buf[i] = pdata[i];
err = tegra_se_lp_encrypt_context_data(
se_dev, SE_CONTEXT_SAVE_RSA_KNOWN_PATTERN_OFFSET,
SE_CONTEXT_KNOWN_PATTERN_SIZE);
} else {
/* Encrypt known pattern */
dt_buf = (unsigned char *)se_dev->ctx_save_buf;
dt_buf += SE_CONTEXT_SAVE_KNOWN_PATTERN_OFFSET;
for (i = 0; i < SE_CONTEXT_KNOWN_PATTERN_SIZE; i++)
dt_buf[i] = pdata[i];
err = tegra_se_lp_encrypt_context_data(
se_dev, SE_CONTEXT_SAVE_KNOWN_PATTERN_OFFSET,
SE_CONTEXT_KNOWN_PATTERN_SIZE);
}
if (err) {
dev_err(se_dev->dev, "LP known pattern save failure\n");
goto out;
}
/* Write lp context buffer address into PMC scratch register */
err = tegra_pmc_save_se_context_buffer_address(
page_to_phys(vmalloc_to_page(se_dev->ctx_save_buf)));
if (err != 0) {
dev_info(se_dev->dev, "failed to save SE context buffer address\n");
goto out;
}
/* Saves SRK in secure scratch */
err = tegra_se_save_SRK(se_dev);
if (err < 0) {
dev_err(se_dev->dev, "LP SRK save failure\n");
goto out;
}
out:
/* put the device into runtime suspend state - disable clock */
pm_runtime_put_sync(dev);
return err;
}
EXPORT_SYMBOL(se_suspend);
struct device *get_se_device(void)
{
return save_se_device;
}
EXPORT_SYMBOL(get_se_device);
static int tegra_se_suspend(struct device *dev)
{
int ret = 0;
ret = se_suspend(dev, false);
return ret;
}
static int tegra_se_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct tegra_se_dev *se_dev = platform_get_drvdata(pdev);
if (!se_dev->chipdata->handle_sc7) {
/* SC7 is handled in Secure OS drivers */
return 0;
}
/* pair with tegra_se_suspend, no need to actually enable clock */
pm_runtime_get_noresume(dev);
/* take access to the hw */
mutex_lock(&se_hw_lock);
pm_runtime_get_sync(se_dev->dev);
if ((tegra_get_chip_id() != TEGRA30) &&
(tegra_get_chip_id() != TEGRA114)) {
se_writel(se_dev,
SE_RNG_SRC_CONFIG_RO_ENT_SRC(DRBG_RO_ENT_SRC_ENABLE) |
SE_RNG_SRC_CONFIG_RO_ENT_SRC_LOCK(
DRBG_RO_ENT_SRC_LOCK_ENABLE),
SE_RNG_SRC_CONFIG_REG_OFFSET);
se_dev->chipdata->drbg_src_entropy_clk_enable = true;
}
pm_runtime_put(se_dev->dev);
mutex_unlock(&se_hw_lock);
return 0;
}
static int tegra_se_runtime_suspend(struct device *dev)
{
/*
* do a dummy read, to avoid scenarios where you have unposted writes
* still on the bus, before disabling clocks
*/
se_readl(sg_tegra_se_dev, SE_CONFIG_REG_OFFSET);
clk_disable_unprepare(sg_tegra_se_dev->pclk);
return 0;
}
static int tegra_se_runtime_resume(struct device *dev)
{
clk_prepare_enable(sg_tegra_se_dev->pclk);
return 0;
}
static const struct dev_pm_ops tegra_se_dev_pm_ops = {
.runtime_suspend = tegra_se_runtime_suspend,
.runtime_resume = tegra_se_runtime_resume,
.suspend = tegra_se_suspend,
.resume = tegra_se_resume,
};
#endif /* CONFIG_PM */
static struct platform_device_id tegra_dev_se_devtype[] = {
{
.name = "tegra-se",
.driver_data = (unsigned long)&tegra_se_chipdata,
},
{
.name = "tegra11-se",
.driver_data = (unsigned long)&tegra11_se_chipdata,
},
{
.name = "tegra12-se",
.driver_data = (unsigned long)&tegra11_se_chipdata,
},
{
.name = "tegra21-se",
.driver_data = (unsigned long)&tegra21_se_chipdata,
},
{
}
};
static struct platform_driver tegra_se_driver = {
.probe = tegra_se_probe,
.remove = tegra_se_remove,
.id_table = tegra_dev_se_devtype,
.driver = {
.name = "tegra-se",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(tegra_se_of_match),
#if defined(CONFIG_PM)
.pm = &tegra_se_dev_pm_ops,
#endif
},
};
static int __init tegra_se_module_init(void)
{
return platform_driver_register(&tegra_se_driver);
}
static void __exit tegra_se_module_exit(void)
{
platform_driver_unregister(&tegra_se_driver);
}
module_init(tegra_se_module_init);
module_exit(tegra_se_module_exit);
MODULE_DESCRIPTION("Tegra Crypto algorithm support");
MODULE_AUTHOR("NVIDIA Corporation");
MODULE_LICENSE("GPL");
MODULE_ALIAS("tegra-se");