4602 lines
123 KiB
C
4602 lines
123 KiB
C
/*
|
|
* Cryptographic API.
|
|
* drivers/crypto/tegra-se-nvhost.c
|
|
*
|
|
* Support for Tegra Security Engine hardware crypto algorithms.
|
|
*
|
|
* Copyright (c) 2015-2021, 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 <soc/tegra/chip-id.h>
|
|
#include <linux/nvhost.h>
|
|
#include <crypto/scatterwalk.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/tegra_pm_domains.h>
|
|
#include <crypto/internal/kpp.h>
|
|
#include <crypto/kpp.h>
|
|
#include <crypto/dh.h>
|
|
#include <linux/version.h>
|
|
#include <linux/pm_qos.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/platform/tegra/emc_bwmgr.h>
|
|
|
|
#include "tegra-se-nvhost.h"
|
|
#include "t186/hardware_t186.h"
|
|
#include "nvhost_job.h"
|
|
#include "nvhost_channel.h"
|
|
#include "nvhost_acm.h"
|
|
|
|
#define DRIVER_NAME "tegra-se-nvhost"
|
|
#define NV_SE1_CLASS_ID 0x3A
|
|
#define NV_SE2_CLASS_ID 0x3B
|
|
#define NV_SE3_CLASS_ID 0x3C
|
|
#define NV_SE4_CLASS_ID 0x3D
|
|
#define NUM_SE_ALGO 5
|
|
#define MIN_DH_SZ_BITS 1536
|
|
|
|
#define __nvhost_opcode_nonincr(x, y) nvhost_opcode_nonincr((x) / 4, (y))
|
|
#define __nvhost_opcode_incr(x, y) nvhost_opcode_incr((x) / 4, (y))
|
|
|
|
/* 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_CMAC, /* Cipher-based MAC (CMAC) mode */
|
|
SE_AES_OP_MODE_RNG_DRBG, /* Deterministic Random Bit Generator */
|
|
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 */
|
|
SE_AES_OP_MODE_XTS /* XTS mode */
|
|
};
|
|
|
|
/* Security Engine key table type */
|
|
enum tegra_se_key_table_type {
|
|
SE_KEY_TABLE_TYPE_KEY, /* Key */
|
|
SE_KEY_TABLE_TYPE_KEY_IN_MEM, /* Key in Memory */
|
|
SE_KEY_TABLE_TYPE_ORGIV, /* Original IV */
|
|
SE_KEY_TABLE_TYPE_UPDTDIV, /* Updated IV */
|
|
SE_KEY_TABLE_TYPE_XTS_KEY1, /* XTS Key1 */
|
|
SE_KEY_TABLE_TYPE_XTS_KEY2, /* XTS Key2 */
|
|
SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM, /* XTS Key1 in Memory */
|
|
SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM /* XTS Key2 in Memory */
|
|
};
|
|
|
|
struct tegra_se_chipdata {
|
|
unsigned long aes_freq;
|
|
unsigned int cpu_freq_mhz;
|
|
};
|
|
|
|
/* Security Engine Linked List */
|
|
struct tegra_se_ll {
|
|
dma_addr_t addr; /* DMA buffer address */
|
|
u32 data_len; /* Data length in DMA buffer */
|
|
};
|
|
|
|
enum tegra_se_algo {
|
|
SE_DRBG,
|
|
SE_AES,
|
|
SE_CMAC,
|
|
SE_RSA,
|
|
SE_SHA,
|
|
};
|
|
|
|
enum tegra_se_callback {
|
|
NONE,
|
|
AES_CB,
|
|
SHA_CB,
|
|
};
|
|
|
|
struct tegra_se_dev {
|
|
struct platform_device *pdev;
|
|
struct device *dev;
|
|
void __iomem *io_regs; /* se device memory/io */
|
|
void __iomem *pmc_io_reg; /* pmc device memory/io */
|
|
struct mutex lock; /* Protect request queue */
|
|
/* Mutex lock (mtx) is to protect Hardware, as it can be used
|
|
* in parallel by different threads. For example, set_key
|
|
* request can come from a different thread and access HW
|
|
*/
|
|
struct mutex mtx;
|
|
struct clk *pclk; /* Security Engine clock */
|
|
struct clk *enclk; /* Security Engine 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 */
|
|
struct tegra_se_cmdbuf *cmdbuf_addr_list;
|
|
unsigned int cmdbuf_list_entry;
|
|
struct tegra_se_chipdata *chipdata; /* chip specific data */
|
|
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 */
|
|
struct tegra_se_ll *src_ll;
|
|
struct tegra_se_ll *dst_ll;
|
|
struct tegra_se_ll *aes_src_ll;
|
|
struct tegra_se_ll *aes_dst_ll;
|
|
u32 *dh_buf1, *dh_buf2;
|
|
struct ablkcipher_request *reqs[SE_MAX_TASKS_PER_SUBMIT];
|
|
struct ahash_request *sha_req;
|
|
unsigned int req_cnt;
|
|
u32 syncpt_id;
|
|
u32 opcode_addr;
|
|
bool work_q_busy; /* Work queue busy status */
|
|
struct nvhost_channel *channel;
|
|
struct work_struct se_work;
|
|
struct workqueue_struct *se_work_q;
|
|
struct scatterlist sg;
|
|
bool dynamic_mem;
|
|
u32 *total_aes_buf;
|
|
dma_addr_t total_aes_buf_addr;
|
|
void *aes_buf;
|
|
dma_addr_t aes_buf_addr;
|
|
void *aes_bufs[SE_MAX_AESBUF_ALLOC];
|
|
dma_addr_t aes_buf_addrs[SE_MAX_AESBUF_ALLOC];
|
|
atomic_t aes_buf_stat[SE_MAX_AESBUF_ALLOC];
|
|
dma_addr_t aes_addr;
|
|
dma_addr_t aes_cur_addr;
|
|
unsigned int cmdbuf_cnt;
|
|
unsigned int bytes_mapped;
|
|
unsigned int gather_buf_sz;
|
|
unsigned int aesbuf_entry;
|
|
u32 *aes_cmdbuf_cpuvaddr;
|
|
dma_addr_t aes_cmdbuf_iova;
|
|
struct pm_qos_request boost_cpufreq_req;
|
|
/* Lock to protect cpufreq boost status */
|
|
struct mutex boost_cpufreq_lock;
|
|
struct delayed_work restore_cpufreq_work;
|
|
unsigned long cpufreq_last_boosted;
|
|
bool cpufreq_boosted;
|
|
bool ioc;
|
|
bool sha_last;
|
|
bool sha_src_mapped;
|
|
bool sha_dst_mapped;
|
|
};
|
|
|
|
static struct tegra_se_dev *se_devices[NUM_SE_ALGO];
|
|
|
|
/* 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 */
|
|
u32 config;
|
|
u32 crypto_config;
|
|
struct tegra_se_dev *se_dev;
|
|
};
|
|
|
|
struct tegra_se_priv_data {
|
|
struct ablkcipher_request *reqs[SE_MAX_TASKS_PER_SUBMIT];
|
|
struct ahash_request *sha_req;
|
|
struct tegra_se_dev *se_dev;
|
|
unsigned int req_cnt;
|
|
unsigned int bytes_mapped;
|
|
unsigned int gather_buf_sz;
|
|
struct scatterlist sg;
|
|
void *buf;
|
|
bool dynmem;
|
|
bool sha_last;
|
|
bool sha_src_mapped;
|
|
bool sha_dst_mapped;
|
|
dma_addr_t buf_addr;
|
|
dma_addr_t iova;
|
|
unsigned int cmdbuf_node;
|
|
unsigned int aesbuf_entry;
|
|
};
|
|
|
|
/* Security Engine AES context */
|
|
struct tegra_se_aes_context {
|
|
struct tegra_se_dev *se_dev; /* Security Engine device */
|
|
struct ablkcipher_request *req;
|
|
struct tegra_se_slot *slot; /* Security Engine key slot */
|
|
u32 keylen; /* key length in bits */
|
|
u32 op_mode; /* AES operation mode */
|
|
bool is_key_in_mem; /* Whether key is in memory */
|
|
u8 key[64]; /* To store key if is_key_in_mem set */
|
|
};
|
|
|
|
/* Security Engine random number generator context */
|
|
struct tegra_se_rng_context {
|
|
struct tegra_se_dev *se_dev; /* Security Engine device */
|
|
struct ablkcipher_request *req;
|
|
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 */
|
|
};
|
|
|
|
/* Security Engine SHA context */
|
|
struct tegra_se_sha_context {
|
|
struct tegra_se_dev *se_dev; /* Security Engine device */
|
|
u32 op_mode; /* SHA operation mode */
|
|
bool is_first; /* Represents first block */
|
|
u8 *sha_buf[2]; /* Buffer to store residual data */
|
|
dma_addr_t sha_buf_addr[2]; /* DMA address to residual data */
|
|
u32 total_count; /* Total bytes in all the requests */
|
|
u32 residual_bytes; /* Residual byte count */
|
|
u32 blk_size; /* SHA block size */
|
|
};
|
|
|
|
struct tegra_se_sha_zero_length_vector {
|
|
unsigned int size;
|
|
char *digest;
|
|
};
|
|
|
|
/* 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 */
|
|
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 keymem_slot = {
|
|
.slot_num = 14,
|
|
.available = false,
|
|
};
|
|
|
|
static struct tegra_se_slot srk_slot = {
|
|
.slot_num = 0,
|
|
.available = false,
|
|
};
|
|
|
|
static struct tegra_se_slot pre_allocated_slot = {
|
|
.slot_num = 0,
|
|
.available = false,
|
|
};
|
|
|
|
struct tegra_se_cmdbuf {
|
|
atomic_t free;
|
|
u32 *cmdbuf_addr;
|
|
dma_addr_t iova;
|
|
};
|
|
|
|
static LIST_HEAD(key_slot);
|
|
static LIST_HEAD(rsa_key_slot);
|
|
static DEFINE_SPINLOCK(rsa_key_slot_lock);
|
|
static DEFINE_SPINLOCK(key_slot_lock);
|
|
|
|
#define RNG_RESEED_INTERVAL 0x00773594
|
|
|
|
/* create a work for handling the async transfers */
|
|
static void tegra_se_work_handler(struct work_struct *work);
|
|
|
|
static DEFINE_DMA_ATTRS(attrs);
|
|
static int force_reseed_count;
|
|
|
|
#define GET_MSB(x) ((x) >> (8 * sizeof(x) - 1))
|
|
#define BOOST_PERIOD (msecs_to_jiffies(2 * 1000)) /* 2 seconds */
|
|
|
|
static unsigned int boost_cpu_freq;
|
|
module_param(boost_cpu_freq, uint, S_IRUGO | S_IWUSR);
|
|
MODULE_PARM_DESC(boost_cpu_freq, "CPU frequency (in MHz) to boost");
|
|
|
|
static void tegra_se_restore_cpu_freq_fn(struct work_struct *work)
|
|
{
|
|
struct tegra_se_dev *se_dev = container_of(
|
|
work, struct tegra_se_dev, restore_cpufreq_work.work);
|
|
unsigned long delay = BOOST_PERIOD;
|
|
|
|
mutex_lock(&se_dev->boost_cpufreq_lock);
|
|
if (time_is_after_jiffies(se_dev->cpufreq_last_boosted + delay)) {
|
|
schedule_delayed_work(&se_dev->restore_cpufreq_work, delay);
|
|
} else {
|
|
pm_qos_update_request(&se_dev->boost_cpufreq_req,
|
|
PM_QOS_DEFAULT_VALUE);
|
|
se_dev->cpufreq_boosted = false;
|
|
}
|
|
mutex_unlock(&se_dev->boost_cpufreq_lock);
|
|
}
|
|
|
|
static void tegra_se_boost_cpu_freq(struct tegra_se_dev *se_dev)
|
|
{
|
|
unsigned long delay = BOOST_PERIOD;
|
|
s32 cpufreq_hz = boost_cpu_freq * 1000;
|
|
|
|
mutex_lock(&se_dev->boost_cpufreq_lock);
|
|
if (!se_dev->cpufreq_boosted) {
|
|
pm_qos_update_request(&se_dev->boost_cpufreq_req, cpufreq_hz);
|
|
schedule_delayed_work(&se_dev->restore_cpufreq_work, delay);
|
|
se_dev->cpufreq_boosted = true;
|
|
}
|
|
|
|
se_dev->cpufreq_last_boosted = jiffies;
|
|
mutex_unlock(&se_dev->boost_cpufreq_lock);
|
|
}
|
|
|
|
static void tegra_se_boost_cpu_init(struct tegra_se_dev *se_dev)
|
|
{
|
|
boost_cpu_freq = se_dev->chipdata->cpu_freq_mhz;
|
|
|
|
INIT_DELAYED_WORK(&se_dev->restore_cpufreq_work,
|
|
tegra_se_restore_cpu_freq_fn);
|
|
|
|
pm_qos_add_request(&se_dev->boost_cpufreq_req, PM_QOS_CPU_FREQ_MIN,
|
|
PM_QOS_DEFAULT_VALUE);
|
|
|
|
mutex_init(&se_dev->boost_cpufreq_lock);
|
|
}
|
|
|
|
static void tegra_se_boost_cpu_deinit(struct tegra_se_dev *se_dev)
|
|
{
|
|
mutex_destroy(&se_dev->boost_cpufreq_lock);
|
|
pm_qos_remove_request(&se_dev->boost_cpufreq_req);
|
|
cancel_delayed_work_sync(&se_dev->restore_cpufreq_work);
|
|
}
|
|
|
|
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_regs + 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_regs + reg_offset);
|
|
|
|
return val;
|
|
}
|
|
|
|
static int tegra_se_init_cmdbuf_addr(struct tegra_se_dev *se_dev)
|
|
{
|
|
int i = 0;
|
|
|
|
se_dev->cmdbuf_addr_list = devm_kzalloc(
|
|
se_dev->dev, sizeof(struct tegra_se_cmdbuf) *
|
|
SE_MAX_SUBMIT_CHAIN_SZ, GFP_KERNEL);
|
|
if (!se_dev->cmdbuf_addr_list)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < SE_MAX_SUBMIT_CHAIN_SZ; i++) {
|
|
se_dev->cmdbuf_addr_list[i].cmdbuf_addr =
|
|
se_dev->aes_cmdbuf_cpuvaddr + (i * SZ_4K);
|
|
se_dev->cmdbuf_addr_list[i].iova = se_dev->aes_cmdbuf_iova +
|
|
(i * SZ_4K * SE_WORD_SIZE_BYTES);
|
|
atomic_set(&se_dev->cmdbuf_addr_list[i].free, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
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->slot_num != pre_allocated_slot.slot_num)) {
|
|
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;
|
|
|
|
spin_lock_init(&key_slot_lock);
|
|
spin_lock(&key_slot_lock);
|
|
/*
|
|
*To avoid multiple secure engine initializing
|
|
*key-slots.
|
|
*/
|
|
if (key_slot.prev != key_slot.next) {
|
|
spin_unlock(&key_slot_lock);
|
|
return 0;
|
|
}
|
|
spin_unlock(&key_slot_lock);
|
|
|
|
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(&key_slot_lock);
|
|
for (i = 0; i < TEGRA_SE_KEYSLOT_COUNT; i++) {
|
|
/*
|
|
* Slot 0, 14 and 15 are reserved and will not be added to the
|
|
* free slots pool. Slot 0 is used for SRK generation, Slot 14
|
|
* for handling keys which are stored in memories and Slot 15 is
|
|
* is used for SSK operation.
|
|
*/
|
|
if ((i == srk_slot.slot_num) || (i == ssk_slot.slot_num)
|
|
|| (i == keymem_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 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;
|
|
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;
|
|
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 u32 tegra_se_get_config(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_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_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_ENC_ALG(ALG_SHA) |
|
|
SE_CONFIG_ENC_MODE(MODE_SHA1) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
case SE_AES_OP_MODE_SHA224:
|
|
val = SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_ENC_ALG(ALG_SHA) |
|
|
SE_CONFIG_ENC_MODE(MODE_SHA224) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
case SE_AES_OP_MODE_SHA256:
|
|
val = SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_ENC_ALG(ALG_SHA) |
|
|
SE_CONFIG_ENC_MODE(MODE_SHA256) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
case SE_AES_OP_MODE_SHA384:
|
|
val = SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_ENC_ALG(ALG_SHA) |
|
|
SE_CONFIG_ENC_MODE(MODE_SHA384) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
case SE_AES_OP_MODE_SHA512:
|
|
val = SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_ENC_ALG(ALG_SHA) |
|
|
SE_CONFIG_ENC_MODE(MODE_SHA512) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
case SE_AES_OP_MODE_XTS:
|
|
if (encrypt) {
|
|
val = SE_CONFIG_ENC_ALG(ALG_AES_ENC);
|
|
if ((key_len / 2) == TEGRA_SE_KEY_256_SIZE)
|
|
val |= SE_CONFIG_ENC_MODE(MODE_KEY256);
|
|
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 / 2 == TEGRA_SE_KEY_256_SIZE)
|
|
val |= SE_CONFIG_DEC_MODE(MODE_KEY256);
|
|
else
|
|
val |= SE_CONFIG_DEC_MODE(MODE_KEY128);
|
|
val |= SE_CONFIG_ENC_ALG(ALG_NOP);
|
|
}
|
|
val |= SE_CONFIG_DST(DST_MEMORY);
|
|
break;
|
|
default:
|
|
dev_warn(se_dev->dev, "Invalid operation mode\n");
|
|
break;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
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((size_t)sl->length, (size_t)nbytes);
|
|
if (!nbytes)
|
|
break;
|
|
sl = sg_next(sl);
|
|
}
|
|
|
|
return sg_nents;
|
|
}
|
|
|
|
|
|
static void tegra_se_sha_complete_callback(void *priv, int nr_completed)
|
|
{
|
|
struct tegra_se_priv_data *priv_data = priv;
|
|
struct ahash_request *req;
|
|
struct tegra_se_dev *se_dev;
|
|
|
|
se_dev = priv_data->se_dev;
|
|
|
|
req = priv_data->sha_req;
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "Invalid request for callback\n");
|
|
devm_kfree(se_dev->dev, priv_data);
|
|
return;
|
|
}
|
|
|
|
if (priv_data->sha_src_mapped)
|
|
tegra_unmap_sg(se_dev->dev, req->src, DMA_TO_DEVICE,
|
|
priv_data->bytes_mapped);
|
|
|
|
if (priv_data->sha_dst_mapped)
|
|
tegra_unmap_sg(se_dev->dev, &priv_data->sg, DMA_FROM_DEVICE,
|
|
priv_data->bytes_mapped);
|
|
|
|
req->base.complete(&req->base, 0);
|
|
|
|
devm_kfree(se_dev->dev, priv_data);
|
|
}
|
|
|
|
static void tegra_se_aes_complete_callback(void *priv, int nr_completed)
|
|
{
|
|
int i = 0;
|
|
struct tegra_se_priv_data *priv_data = priv;
|
|
struct ablkcipher_request *req;
|
|
struct tegra_se_dev *se_dev;
|
|
void *buf;
|
|
u32 num_sgs;
|
|
|
|
se_dev = priv_data->se_dev;
|
|
atomic_set(&se_dev->cmdbuf_addr_list[priv_data->cmdbuf_node].free, 1);
|
|
|
|
if (!priv_data->req_cnt) {
|
|
devm_kfree(se_dev->dev, priv_data);
|
|
return;
|
|
}
|
|
|
|
if (!se_dev->ioc)
|
|
dma_sync_single_for_cpu(se_dev->dev, priv_data->buf_addr,
|
|
priv_data->gather_buf_sz, DMA_BIDIRECTIONAL);
|
|
|
|
buf = priv_data->buf;
|
|
for (i = 0; i < priv_data->req_cnt; i++) {
|
|
req = priv_data->reqs[i];
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "Invalid request for callback\n");
|
|
if (priv_data->dynmem)
|
|
kfree(priv_data->buf);
|
|
devm_kfree(se_dev->dev, priv_data);
|
|
return;
|
|
}
|
|
|
|
num_sgs = tegra_se_count_sgs(req->dst, req->nbytes);
|
|
if (num_sgs == 1)
|
|
memcpy(sg_virt(req->dst), buf, req->nbytes);
|
|
else
|
|
sg_copy_from_buffer(req->dst, num_sgs, buf,
|
|
req->nbytes);
|
|
|
|
buf += req->nbytes;
|
|
req->base.complete(&req->base, 0);
|
|
}
|
|
|
|
if (!se_dev->ioc)
|
|
dma_unmap_sg(se_dev->dev, &priv_data->sg, 1, DMA_BIDIRECTIONAL);
|
|
|
|
if (unlikely(priv_data->dynmem)) {
|
|
if (se_dev->ioc)
|
|
dma_free_coherent(se_dev->dev, priv_data->gather_buf_sz,
|
|
priv_data->buf, priv_data->buf_addr);
|
|
else
|
|
kfree(priv_data->buf);
|
|
} else {
|
|
atomic_set(&se_dev->aes_buf_stat[priv_data->aesbuf_entry], 1);
|
|
}
|
|
|
|
devm_kfree(se_dev->dev, priv_data);
|
|
}
|
|
|
|
static void se_nvhost_write_method(u32 *buf, u32 op1, u32 op2, u32 *offset)
|
|
{
|
|
int i = 0;
|
|
|
|
buf[i++] = op1;
|
|
buf[i++] = op2;
|
|
*offset = *offset + 2;
|
|
}
|
|
|
|
static int tegra_se_channel_submit_gather(struct tegra_se_dev *se_dev,
|
|
u32 *cpuvaddr, dma_addr_t iova,
|
|
u32 offset, u32 num_words,
|
|
enum tegra_se_callback callback)
|
|
{
|
|
int i = 0;
|
|
struct nvhost_job *job = NULL;
|
|
u32 syncpt_id = 0;
|
|
int err = 0;
|
|
struct tegra_se_priv_data *priv = NULL;
|
|
struct nvhost_device_data *pdata = platform_get_drvdata(se_dev->pdev);
|
|
|
|
if (callback) {
|
|
priv = devm_kzalloc(se_dev->dev,
|
|
sizeof(struct tegra_se_priv_data),
|
|
GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
err = nvhost_module_busy(se_dev->pdev);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "nvhost_module_busy failed for se_dev\n");
|
|
if (priv)
|
|
devm_kfree(se_dev->dev, priv);
|
|
return err;
|
|
}
|
|
|
|
if (!se_dev->channel) {
|
|
err = nvhost_channel_map(pdata, &se_dev->channel, pdata);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "Nvhost Channel map failed\n");
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
job = nvhost_job_alloc(se_dev->channel, 1, 0, 0, 1);
|
|
if (!job) {
|
|
dev_err(se_dev->dev, "Nvhost Job allocation failed\n");
|
|
err = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
|
|
if (!se_dev->syncpt_id) {
|
|
se_dev->syncpt_id = nvhost_get_syncpt_host_managed(
|
|
se_dev->pdev, 0, se_dev->pdev->name);
|
|
if (!se_dev->syncpt_id) {
|
|
dev_err(se_dev->dev, "Cannot get syncpt_id for SE(%s)\n",
|
|
se_dev->pdev->name);
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
syncpt_id = se_dev->syncpt_id;
|
|
|
|
/* initialize job data */
|
|
job->sp->id = syncpt_id;
|
|
job->sp->incrs = 1;
|
|
job->num_syncpts = 1;
|
|
|
|
/* push increment after work has been completed */
|
|
se_nvhost_write_method(&cpuvaddr[num_words], nvhost_opcode_nonincr(
|
|
host1x_uclass_incr_syncpt_r(), 1),
|
|
nvhost_class_host_incr_syncpt(
|
|
host1x_uclass_incr_syncpt_cond_op_done_v(),
|
|
syncpt_id), &num_words);
|
|
|
|
err = nvhost_job_add_client_gather_address(job, num_words,
|
|
pdata->class, iova);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "Nvhost failed to add gather\n");
|
|
goto error;
|
|
}
|
|
|
|
err = nvhost_channel_submit(job);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "Nvhost submit failed\n");
|
|
goto error;
|
|
}
|
|
|
|
if (callback == AES_CB) {
|
|
priv->se_dev = se_dev;
|
|
for (i = 0; i < se_dev->req_cnt; i++)
|
|
priv->reqs[i] = se_dev->reqs[i];
|
|
|
|
if (!se_dev->ioc)
|
|
priv->sg = se_dev->sg;
|
|
|
|
if (unlikely(se_dev->dynamic_mem)) {
|
|
priv->buf = se_dev->aes_buf;
|
|
priv->dynmem = se_dev->dynamic_mem;
|
|
} else {
|
|
priv->buf = se_dev->aes_bufs[se_dev->aesbuf_entry];
|
|
priv->aesbuf_entry = se_dev->aesbuf_entry;
|
|
}
|
|
|
|
priv->buf_addr = se_dev->aes_addr;
|
|
priv->req_cnt = se_dev->req_cnt;
|
|
priv->gather_buf_sz = se_dev->gather_buf_sz;
|
|
priv->cmdbuf_node = se_dev->cmdbuf_list_entry;
|
|
|
|
/* Register callback to be called once
|
|
* syncpt value has been reached
|
|
*/
|
|
err = nvhost_intr_register_fast_notifier(
|
|
se_dev->pdev, job->sp->id, job->sp->fence,
|
|
tegra_se_aes_complete_callback, priv);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"add nvhost interrupt action failed for AES\n");
|
|
goto error;
|
|
}
|
|
} else if (callback == SHA_CB) {
|
|
priv->se_dev = se_dev;
|
|
priv->sha_req = se_dev->sha_req;
|
|
priv->sg = se_dev->sg;
|
|
priv->bytes_mapped = se_dev->bytes_mapped;
|
|
priv->sha_src_mapped = se_dev->sha_src_mapped;
|
|
priv->sha_dst_mapped = se_dev->sha_dst_mapped;
|
|
priv->sha_last = se_dev->sha_last;
|
|
priv->buf_addr = se_dev->dst_ll->addr;
|
|
|
|
err = nvhost_intr_register_fast_notifier(
|
|
se_dev->pdev, job->sp->id, job->sp->fence,
|
|
tegra_se_sha_complete_callback, priv);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"add nvhost interrupt action failed for SHA\n");
|
|
goto error;
|
|
}
|
|
} else {
|
|
/* wait until host1x has processed work */
|
|
nvhost_syncpt_wait_timeout_ext(
|
|
se_dev->pdev, job->sp->id, job->sp->fence,
|
|
(u32)MAX_SCHEDULE_TIMEOUT, NULL, NULL);
|
|
|
|
if (se_dev->cmdbuf_addr_list)
|
|
atomic_set(&se_dev->cmdbuf_addr_list[
|
|
se_dev->cmdbuf_list_entry].free, 1);
|
|
}
|
|
|
|
se_dev->req_cnt = 0;
|
|
se_dev->gather_buf_sz = 0;
|
|
se_dev->cmdbuf_cnt = 0;
|
|
se_dev->bytes_mapped = 0;
|
|
se_dev->sha_src_mapped = false;
|
|
se_dev->sha_dst_mapped = false;
|
|
se_dev->sha_last = false;
|
|
error:
|
|
nvhost_job_put(job);
|
|
job = NULL;
|
|
exit:
|
|
nvhost_module_idle(se_dev->pdev);
|
|
if (err)
|
|
devm_kfree(se_dev->dev, priv);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void tegra_se_send_ctr_seed(struct tegra_se_dev *se_dev, u32 *pdata,
|
|
unsigned int opcode_addr, u32 *cpuvaddr)
|
|
{
|
|
u32 j;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
|
|
i = se_dev->cmdbuf_cnt;
|
|
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(opcode_addr +
|
|
SE_AES_CRYPTO_CTR_SPARE, 1);
|
|
cpuvaddr[i++] = SE_AES_CTR_LITTLE_ENDIAN;
|
|
cpuvaddr[i++] = __nvhost_opcode_incr(opcode_addr +
|
|
SE_AES_CRYPTO_LINEAR_CTR, 4);
|
|
for (j = 0; j < SE_CRYPTO_CTR_REG_COUNT; j++)
|
|
cpuvaddr[i++] = pdata[j];
|
|
|
|
cmdbuf_num_words = i;
|
|
se_dev->cmdbuf_cnt = i;
|
|
}
|
|
|
|
static int tegra_se_send_key_data(struct tegra_se_dev *se_dev, u8 *pdata,
|
|
u32 data_len, u8 slot_num,
|
|
enum tegra_se_key_table_type type,
|
|
unsigned int opcode_addr, u32 *cpuvaddr,
|
|
dma_addr_t iova,
|
|
enum tegra_se_callback callback)
|
|
{
|
|
u32 data_size;
|
|
u32 *pdata_buf = (u32 *)pdata;
|
|
u8 pkt = 0, quad = 0;
|
|
u32 val = 0, j;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
int err = 0;
|
|
|
|
if (!pdata_buf) {
|
|
dev_err(se_dev->dev, "No Key Data available\n");
|
|
return -ENODATA;
|
|
}
|
|
|
|
if ((type == SE_KEY_TABLE_TYPE_KEY) &&
|
|
(slot_num == ssk_slot.slot_num)) {
|
|
dev_err(se_dev->dev, "SSK Key Slot used\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((type == SE_KEY_TABLE_TYPE_ORGIV) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY2) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM))
|
|
quad = QUAD_ORG_IV;
|
|
else if (type == SE_KEY_TABLE_TYPE_UPDTDIV)
|
|
quad = QUAD_UPDTD_IV;
|
|
else if ((type == SE_KEY_TABLE_TYPE_KEY) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY1) ||
|
|
(type == SE_KEY_TABLE_TYPE_KEY_IN_MEM) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM))
|
|
quad = QUAD_KEYS_128;
|
|
|
|
i = se_dev->cmdbuf_cnt;
|
|
|
|
if (!se_dev->cmdbuf_cnt) {
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
opcode_addr + SE_AES_OPERATION_OFFSET, 1);
|
|
cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_OP(OP_DUMMY);
|
|
}
|
|
|
|
data_size = SE_KEYTABLE_QUAD_SIZE_BYTES;
|
|
|
|
do {
|
|
if (type == SE_KEY_TABLE_TYPE_XTS_KEY2 ||
|
|
type == SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM)
|
|
pkt = SE_CRYPTO_KEYIV_PKT_SUBKEY_SEL(SUBKEY_SEL_KEY2);
|
|
else if (type == SE_KEY_TABLE_TYPE_XTS_KEY1 ||
|
|
type == SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM)
|
|
pkt = SE_CRYPTO_KEYIV_PKT_SUBKEY_SEL(SUBKEY_SEL_KEY1);
|
|
|
|
pkt |= (SE_KEYTABLE_SLOT(slot_num) | SE_KEYTABLE_QUAD(quad));
|
|
|
|
for (j = 0; j < data_size; j += 4, data_len -= 4) {
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
opcode_addr +
|
|
SE_AES_CRYPTO_KEYTABLE_ADDR_OFFSET, 1);
|
|
|
|
val = (SE_KEYTABLE_PKT(pkt) | (j / 4));
|
|
cpuvaddr[i++] = val;
|
|
|
|
cpuvaddr[i++] = __nvhost_opcode_incr(
|
|
opcode_addr +
|
|
SE_AES_CRYPTO_KEYTABLE_DATA_OFFSET, 1);
|
|
cpuvaddr[i++] = *pdata_buf++;
|
|
}
|
|
data_size = data_len;
|
|
if ((type == SE_KEY_TABLE_TYPE_KEY) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY1) ||
|
|
(type == SE_KEY_TABLE_TYPE_KEY_IN_MEM) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM))
|
|
quad = QUAD_KEYS_256;
|
|
else if ((type == SE_KEY_TABLE_TYPE_XTS_KEY2) ||
|
|
(type == SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM))
|
|
quad = QUAD_UPDTD_IV;
|
|
|
|
} while (data_len);
|
|
|
|
if ((type != SE_KEY_TABLE_TYPE_ORGIV) &&
|
|
(type != SE_KEY_TABLE_TYPE_UPDTDIV) &&
|
|
(type != SE_KEY_TABLE_TYPE_KEY_IN_MEM) &&
|
|
(type != SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM) &&
|
|
(type != SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM)) {
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
opcode_addr + SE_AES_OPERATION_OFFSET, 1);
|
|
cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_OP(OP_DUMMY);
|
|
}
|
|
|
|
cmdbuf_num_words = i;
|
|
se_dev->cmdbuf_cnt = i;
|
|
|
|
if ((type != SE_KEY_TABLE_TYPE_ORGIV) &&
|
|
(type != SE_KEY_TABLE_TYPE_UPDTDIV) &&
|
|
(type != SE_KEY_TABLE_TYPE_KEY_IN_MEM) &&
|
|
(type != SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM) &&
|
|
(type != SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM))
|
|
err = tegra_se_channel_submit_gather(
|
|
se_dev, cpuvaddr, iova, 0, cmdbuf_num_words, callback);
|
|
|
|
return err;
|
|
}
|
|
|
|
static u32 tegra_se_get_crypto_config(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;
|
|
|
|
switch (mode) {
|
|
case SE_AES_OP_MODE_XTS:
|
|
if (encrypt) {
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_MEMORY) |
|
|
SE_CRYPTO_VCTRAM_SEL(VCTRAM_TWEAK) |
|
|
SE_CRYPTO_XOR_POS(XOR_BOTH) |
|
|
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
|
|
} else {
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_MEMORY) |
|
|
SE_CRYPTO_VCTRAM_SEL(VCTRAM_TWEAK) |
|
|
SE_CRYPTO_XOR_POS(XOR_BOTH) |
|
|
SE_CRYPTO_CORE_SEL(CORE_DECRYPT);
|
|
}
|
|
freq = se_dev->chipdata->aes_freq;
|
|
break;
|
|
case SE_AES_OP_MODE_CMAC:
|
|
case SE_AES_OP_MODE_CBC:
|
|
if (encrypt) {
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_MEMORY) |
|
|
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_MEMORY) |
|
|
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_DRBG:
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_RANDOM) |
|
|
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
|
|
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
|
|
break;
|
|
case SE_AES_OP_MODE_ECB:
|
|
if (encrypt) {
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_MEMORY) |
|
|
SE_CRYPTO_XOR_POS(XOR_BYPASS) |
|
|
SE_CRYPTO_CORE_SEL(CORE_ENCRYPT);
|
|
} else {
|
|
val = SE_CRYPTO_INPUT_SEL(INPUT_MEMORY) |
|
|
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_MEMORY) |
|
|
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_MEMORY) |
|
|
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));
|
|
}
|
|
|
|
/* enable hash for CMAC */
|
|
if (mode == SE_AES_OP_MODE_CMAC)
|
|
val |= SE_CRYPTO_HASH(HASH_ENABLE);
|
|
|
|
if (mode == SE_AES_OP_MODE_RNG_DRBG) {
|
|
/* Make sure engine is powered ON*/
|
|
nvhost_module_busy(se_dev->pdev);
|
|
|
|
if (force_reseed_count <= 0) {
|
|
se_writel(se_dev,
|
|
SE_RNG_CONFIG_MODE(DRBG_MODE_FORCE_RESEED) |
|
|
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY),
|
|
SE_RNG_CONFIG_REG_OFFSET);
|
|
force_reseed_count = RNG_RESEED_INTERVAL;
|
|
} else {
|
|
se_writel(se_dev,
|
|
SE_RNG_CONFIG_MODE(DRBG_MODE_NORMAL) |
|
|
SE_RNG_CONFIG_SRC(DRBG_SRC_ENTROPY),
|
|
SE_RNG_CONFIG_REG_OFFSET);
|
|
}
|
|
--force_reseed_count;
|
|
|
|
se_writel(se_dev, RNG_RESEED_INTERVAL,
|
|
SE_RNG_RESEED_INTERVAL_REG_OFFSET);
|
|
|
|
/* Power off device after register access done */
|
|
nvhost_module_idle(se_dev->pdev);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
static int tegra_se_send_sha_data(struct tegra_se_dev *se_dev,
|
|
struct tegra_se_req_context *req_ctx,
|
|
struct tegra_se_sha_context *sha_ctx,
|
|
u32 count, bool last)
|
|
{
|
|
int err = 0;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
u32 *cmdbuf_cpuvaddr = NULL;
|
|
dma_addr_t cmdbuf_iova = 0;
|
|
struct tegra_se_ll *src_ll = se_dev->src_ll;
|
|
struct tegra_se_ll *dst_ll = se_dev->dst_ll;
|
|
unsigned int total = count, val;
|
|
u64 msg_len;
|
|
|
|
cmdbuf_cpuvaddr = dma_alloc_attrs(se_dev->dev->parent, SZ_4K,
|
|
&cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
if (!cmdbuf_cpuvaddr) {
|
|
dev_err(se_dev->dev, "Failed to allocate cmdbuf\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
while (total) {
|
|
if (src_ll->data_len & SE_BUFF_SIZE_MASK) {
|
|
dma_free_attrs(se_dev->dev->parent, SZ_4K,
|
|
cmdbuf_cpuvaddr, cmdbuf_iova,
|
|
__DMA_ATTR(attrs));
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (total == count) {
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_incr(
|
|
se_dev->opcode_addr +
|
|
SE_SHA_MSG_LENGTH_OFFSET, 8);
|
|
msg_len = (count * 8);
|
|
cmdbuf_cpuvaddr[i++] =
|
|
(sha_ctx->total_count * 8);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(msg_len >> 32);
|
|
cmdbuf_cpuvaddr[i++] = 0;
|
|
cmdbuf_cpuvaddr[i++] = 0;
|
|
|
|
/* If it is not last request, length of message left
|
|
* should be more than input buffer length.
|
|
*/
|
|
if (!last)
|
|
cmdbuf_cpuvaddr[i++] =
|
|
(u32)(msg_len + 8) & 0xFFFFFFFFULL;
|
|
else
|
|
cmdbuf_cpuvaddr[i++] =
|
|
(u32)((msg_len) & 0xFFFFFFFFULL);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(msg_len >> 32);
|
|
cmdbuf_cpuvaddr[i++] = 0;
|
|
cmdbuf_cpuvaddr[i++] = 0;
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_incr(
|
|
se_dev->opcode_addr, 6);
|
|
|
|
cmdbuf_cpuvaddr[i++] = req_ctx->config;
|
|
|
|
if (sha_ctx->is_first)
|
|
cmdbuf_cpuvaddr[i++] =
|
|
SE4_HW_INIT_HASH(HW_INIT_HASH_ENABLE);
|
|
else
|
|
cmdbuf_cpuvaddr[i++] =
|
|
SE4_HW_INIT_HASH(HW_INIT_HASH_DISABLE);
|
|
} else {
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_incr(
|
|
se_dev->opcode_addr +
|
|
SE4_SHA_IN_ADDR_OFFSET, 4);
|
|
}
|
|
cmdbuf_cpuvaddr[i++] = src_ll->addr;
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(src_ll->addr)) |
|
|
SE_ADDR_HI_SZ(src_ll->data_len));
|
|
cmdbuf_cpuvaddr[i++] = dst_ll->addr;
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(dst_ll->addr)) |
|
|
SE_ADDR_HI_SZ(dst_ll->data_len));
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_SHA_OPERATION_OFFSET, 1);
|
|
|
|
val = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
if (total == count) {
|
|
if (total == src_ll->data_len)
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
else
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_FALSE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
} else {
|
|
if (total == src_ll->data_len)
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_RESTART_IN);
|
|
else
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_FALSE) |
|
|
SE_OPERATION_OP(OP_RESTART_IN);
|
|
}
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
total -= src_ll->data_len;
|
|
src_ll++;
|
|
}
|
|
|
|
cmdbuf_num_words = i;
|
|
|
|
err = tegra_se_channel_submit_gather(se_dev, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, 0, cmdbuf_num_words,
|
|
SHA_CB);
|
|
dma_free_attrs(se_dev->dev->parent, SZ_4K, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, __DMA_ATTR(attrs));
|
|
|
|
return err;
|
|
}
|
|
|
|
static void tegra_se_read_cmac_result(struct tegra_se_dev *se_dev, u8 *pdata,
|
|
u32 nbytes, bool swap32)
|
|
{
|
|
u32 *result = (u32 *)pdata;
|
|
u32 i;
|
|
|
|
/* Make SE engine is powered ON */
|
|
nvhost_module_busy(se_dev->pdev);
|
|
|
|
for (i = 0; i < nbytes / 4; i++) {
|
|
result[i] = se_readl(se_dev, SE_CMAC_RESULT_REG_OFFSET +
|
|
(i * sizeof(u32)));
|
|
if (swap32)
|
|
result[i] = be32_to_cpu(result[i]);
|
|
}
|
|
|
|
nvhost_module_idle(se_dev->pdev);
|
|
}
|
|
|
|
static void tegra_se_send_data(struct tegra_se_dev *se_dev,
|
|
struct tegra_se_req_context *req_ctx,
|
|
struct ablkcipher_request *req, u32 nbytes,
|
|
unsigned int opcode_addr, u32 *cpuvaddr)
|
|
{
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
u32 total, val;
|
|
unsigned int restart_op;
|
|
struct tegra_se_ll *src_ll;
|
|
struct tegra_se_ll *dst_ll;
|
|
|
|
if (req) {
|
|
src_ll = se_dev->aes_src_ll;
|
|
dst_ll = se_dev->aes_dst_ll;
|
|
src_ll->addr = se_dev->aes_cur_addr;
|
|
dst_ll->addr = se_dev->aes_cur_addr;
|
|
src_ll->data_len = req->nbytes;
|
|
dst_ll->data_len = req->nbytes;
|
|
} else {
|
|
src_ll = se_dev->src_ll;
|
|
dst_ll = se_dev->dst_ll;
|
|
}
|
|
|
|
i = se_dev->cmdbuf_cnt;
|
|
total = nbytes;
|
|
|
|
/* Create Gather Buffer Command */
|
|
while (total) {
|
|
if (total == nbytes) {
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
opcode_addr +
|
|
SE_AES_CRYPTO_LAST_BLOCK_OFFSET, 1);
|
|
cpuvaddr[i++] = ((nbytes /
|
|
TEGRA_SE_AES_BLOCK_SIZE) - 1);
|
|
|
|
cpuvaddr[i++] = __nvhost_opcode_incr(opcode_addr, 6);
|
|
cpuvaddr[i++] = req_ctx->config;
|
|
cpuvaddr[i++] = req_ctx->crypto_config;
|
|
} else {
|
|
cpuvaddr[i++] = __nvhost_opcode_incr(
|
|
opcode_addr + SE_AES_IN_ADDR_OFFSET, 4);
|
|
}
|
|
|
|
cpuvaddr[i++] = (u32)(src_ll->addr);
|
|
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(src_ll->addr)) |
|
|
SE_ADDR_HI_SZ(src_ll->data_len));
|
|
cpuvaddr[i++] = (u32)(dst_ll->addr);
|
|
cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(dst_ll->addr)) |
|
|
SE_ADDR_HI_SZ(dst_ll->data_len));
|
|
|
|
if (req_ctx->op_mode == SE_AES_OP_MODE_CMAC)
|
|
restart_op = OP_RESTART_IN;
|
|
else if (req_ctx->op_mode == SE_AES_OP_MODE_RNG_DRBG)
|
|
restart_op = OP_RESTART_OUT;
|
|
else
|
|
restart_op = OP_RESTART_INOUT;
|
|
|
|
cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
opcode_addr + SE_AES_OPERATION_OFFSET, 1);
|
|
|
|
val = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
if (total == nbytes) {
|
|
if (total == src_ll->data_len)
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
else
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_FALSE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
} else {
|
|
if (total == src_ll->data_len)
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(restart_op);
|
|
else
|
|
val |= SE_OPERATION_LASTBUF(LASTBUF_FALSE) |
|
|
SE_OPERATION_OP(restart_op);
|
|
}
|
|
cpuvaddr[i++] = val;
|
|
total -= src_ll->data_len;
|
|
src_ll++;
|
|
dst_ll++;
|
|
}
|
|
|
|
cmdbuf_num_words = i;
|
|
se_dev->cmdbuf_cnt = i;
|
|
if (req)
|
|
se_dev->aes_cur_addr += req->nbytes;
|
|
}
|
|
|
|
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 = sg_dma_address(sg);
|
|
se_ll->data_len = min((size_t)sg->length, (size_t)total_loop);
|
|
total_loop -= min((size_t)sg->length, (size_t)total_loop);
|
|
sg = sg_next(sg);
|
|
se_ll++;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_setup_ablk_req(struct tegra_se_dev *se_dev)
|
|
{
|
|
struct ablkcipher_request *req;
|
|
void *buf;
|
|
int i, ret = 0;
|
|
u32 num_sgs;
|
|
unsigned int index = 0;
|
|
|
|
if (unlikely(se_dev->dynamic_mem)) {
|
|
if (se_dev->ioc)
|
|
se_dev->aes_buf = dma_alloc_coherent(
|
|
se_dev->dev, se_dev->gather_buf_sz,
|
|
&se_dev->aes_buf_addr, GFP_KERNEL);
|
|
else
|
|
se_dev->aes_buf = kmalloc(se_dev->gather_buf_sz,
|
|
GFP_KERNEL);
|
|
if (!se_dev->aes_buf)
|
|
return -ENOMEM;
|
|
buf = se_dev->aes_buf;
|
|
} else {
|
|
index = se_dev->aesbuf_entry + 1;
|
|
for (i = 0; i < SE_MAX_AESBUF_TIMEOUT; i++, index++) {
|
|
index = index % SE_MAX_AESBUF_ALLOC;
|
|
if (atomic_read(&se_dev->aes_buf_stat[index])) {
|
|
se_dev->aesbuf_entry = index;
|
|
atomic_set(&se_dev->aes_buf_stat[index], 0);
|
|
break;
|
|
}
|
|
if (i % SE_MAX_AESBUF_ALLOC == 0)
|
|
udelay(SE_WAIT_UDELAY);
|
|
}
|
|
|
|
if (i == SE_MAX_AESBUF_TIMEOUT) {
|
|
pr_err("aes_buffer not available\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
buf = se_dev->aes_bufs[index];
|
|
}
|
|
|
|
for (i = 0; i < se_dev->req_cnt; i++) {
|
|
req = se_dev->reqs[i];
|
|
|
|
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
|
|
|
|
if (num_sgs == 1)
|
|
memcpy(buf, sg_virt(req->src), req->nbytes);
|
|
else
|
|
sg_copy_to_buffer(req->src, num_sgs, buf, req->nbytes);
|
|
buf += req->nbytes;
|
|
}
|
|
|
|
if (se_dev->ioc) {
|
|
if (unlikely(se_dev->dynamic_mem))
|
|
se_dev->aes_addr = se_dev->aes_buf_addr;
|
|
else
|
|
se_dev->aes_addr = se_dev->aes_buf_addrs[index];
|
|
} else {
|
|
if (unlikely(se_dev->dynamic_mem))
|
|
sg_init_one(&se_dev->sg, se_dev->aes_buf,
|
|
se_dev->gather_buf_sz);
|
|
else
|
|
sg_init_one(&se_dev->sg, se_dev->aes_bufs[index],
|
|
se_dev->gather_buf_sz);
|
|
|
|
ret = dma_map_sg(se_dev->dev, &se_dev->sg, 1,
|
|
DMA_BIDIRECTIONAL);
|
|
if (!ret) {
|
|
dev_err(se_dev->dev, "dma_map_sg error\n");
|
|
|
|
if (unlikely(se_dev->dynamic_mem))
|
|
kfree(se_dev->aes_buf);
|
|
else
|
|
atomic_set(&se_dev->aes_buf_stat[index], 1);
|
|
return ret;
|
|
}
|
|
|
|
se_dev->aes_addr = sg_dma_address(&se_dev->sg);
|
|
}
|
|
|
|
se_dev->aes_cur_addr = se_dev->aes_addr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_prepare_cmdbuf(struct tegra_se_dev *se_dev,
|
|
u32 *cpuvaddr, dma_addr_t iova)
|
|
{
|
|
int i, ret = 0;
|
|
struct tegra_se_aes_context *aes_ctx;
|
|
struct ablkcipher_request *req;
|
|
struct tegra_se_req_context *req_ctx;
|
|
struct crypto_ablkcipher *tfm;
|
|
u32 keylen;
|
|
|
|
for (i = 0; i < se_dev->req_cnt; i++) {
|
|
req = se_dev->reqs[i];
|
|
tfm = crypto_ablkcipher_reqtfm(req);
|
|
aes_ctx = crypto_ablkcipher_ctx(tfm);
|
|
/* Ensure there is valid slot info */
|
|
if (!aes_ctx->slot) {
|
|
dev_err(se_dev->dev, "Invalid AES Ctx Slot\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (aes_ctx->is_key_in_mem) {
|
|
if (strcmp(crypto_tfm_alg_name(&tfm->base),
|
|
"xts(aes)")) {
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, aes_ctx->key, aes_ctx->keylen,
|
|
aes_ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_KEY_IN_MEM,
|
|
se_dev->opcode_addr, cpuvaddr, iova,
|
|
AES_CB);
|
|
} else {
|
|
keylen = aes_ctx->keylen / 2;
|
|
ret = tegra_se_send_key_data(se_dev,
|
|
aes_ctx->key, keylen,
|
|
aes_ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_XTS_KEY1_IN_MEM,
|
|
se_dev->opcode_addr, cpuvaddr, iova,
|
|
AES_CB);
|
|
if (ret) {
|
|
dev_err(se_dev->dev, "Error in setting Key\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = tegra_se_send_key_data(se_dev,
|
|
aes_ctx->key + keylen, keylen,
|
|
aes_ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_XTS_KEY2_IN_MEM,
|
|
se_dev->opcode_addr, cpuvaddr, iova,
|
|
AES_CB);
|
|
}
|
|
if (ret) {
|
|
dev_err(se_dev->dev, "Error in setting Key\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
req_ctx = ablkcipher_request_ctx(req);
|
|
|
|
if (req->info) {
|
|
if (req_ctx->op_mode == SE_AES_OP_MODE_CTR ||
|
|
req_ctx->op_mode == SE_AES_OP_MODE_XTS) {
|
|
tegra_se_send_ctr_seed(se_dev, (u32 *)req->info,
|
|
se_dev->opcode_addr,
|
|
cpuvaddr);
|
|
} else {
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, req->info, TEGRA_SE_AES_IV_SIZE,
|
|
aes_ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_UPDTDIV, se_dev->opcode_addr,
|
|
cpuvaddr, iova, AES_CB);
|
|
}
|
|
}
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
req_ctx->config = tegra_se_get_config(se_dev, req_ctx->op_mode,
|
|
req_ctx->encrypt,
|
|
aes_ctx->keylen);
|
|
req_ctx->crypto_config = tegra_se_get_crypto_config(
|
|
se_dev, req_ctx->op_mode,
|
|
req_ctx->encrypt,
|
|
aes_ctx->slot->slot_num, false);
|
|
|
|
tegra_se_send_data(se_dev, req_ctx, req, req->nbytes,
|
|
se_dev->opcode_addr, cpuvaddr);
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_get_free_cmdbuf(struct tegra_se_dev *se_dev)
|
|
{
|
|
int i = 0;
|
|
unsigned int index = se_dev->cmdbuf_list_entry + 1;
|
|
|
|
for (i = 0; i < SE_MAX_CMDBUF_TIMEOUT; i++, index++) {
|
|
index = index % SE_MAX_SUBMIT_CHAIN_SZ;
|
|
if (atomic_read(&se_dev->cmdbuf_addr_list[index].free)) {
|
|
atomic_set(&se_dev->cmdbuf_addr_list[index].free, 0);
|
|
break;
|
|
}
|
|
if (i % SE_MAX_SUBMIT_CHAIN_SZ == 0)
|
|
udelay(SE_WAIT_UDELAY);
|
|
}
|
|
|
|
return (i == SE_MAX_CMDBUF_TIMEOUT) ? -ENOMEM : index;
|
|
}
|
|
|
|
static void tegra_se_process_new_req(struct tegra_se_dev *se_dev)
|
|
{
|
|
struct ablkcipher_request *req;
|
|
u32 *cpuvaddr = NULL;
|
|
dma_addr_t iova = 0;
|
|
unsigned int index = 0;
|
|
int err = 0, i = 0;
|
|
|
|
tegra_se_boost_cpu_freq(se_dev);
|
|
|
|
for (i = 0; i < se_dev->req_cnt; i++) {
|
|
req = se_dev->reqs[i];
|
|
if (req->nbytes != SE_STATIC_MEM_ALLOC_BUFSZ) {
|
|
se_dev->dynamic_mem = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
err = tegra_se_setup_ablk_req(se_dev);
|
|
if (err)
|
|
goto mem_out;
|
|
|
|
err = tegra_se_get_free_cmdbuf(se_dev);
|
|
if (err < 0) {
|
|
dev_err(se_dev->dev, "Couldn't get free cmdbuf\n");
|
|
goto index_out;
|
|
}
|
|
|
|
index = err;
|
|
|
|
cpuvaddr = se_dev->cmdbuf_addr_list[index].cmdbuf_addr;
|
|
iova = se_dev->cmdbuf_addr_list[index].iova;
|
|
se_dev->cmdbuf_list_entry = index;
|
|
|
|
err = tegra_se_prepare_cmdbuf(se_dev, cpuvaddr, iova);
|
|
if (err)
|
|
goto cmdbuf_out;
|
|
|
|
err = tegra_se_channel_submit_gather(se_dev, cpuvaddr, iova, 0,
|
|
se_dev->cmdbuf_cnt, AES_CB);
|
|
if (err)
|
|
goto cmdbuf_out;
|
|
se_dev->dynamic_mem = false;
|
|
|
|
return;
|
|
cmdbuf_out:
|
|
atomic_set(&se_dev->cmdbuf_addr_list[index].free, 1);
|
|
index_out:
|
|
dma_unmap_sg(se_dev->dev, &se_dev->sg, 1, DMA_BIDIRECTIONAL);
|
|
kfree(se_dev->aes_buf);
|
|
mem_out:
|
|
for (i = 0; i < se_dev->req_cnt; i++) {
|
|
req = se_dev->reqs[i];
|
|
req->base.complete(&req->base, err);
|
|
}
|
|
se_dev->req_cnt = 0;
|
|
se_dev->gather_buf_sz = 0;
|
|
se_dev->cmdbuf_cnt = 0;
|
|
se_dev->dynamic_mem = false;
|
|
}
|
|
|
|
static void tegra_se_work_handler(struct work_struct *work)
|
|
{
|
|
struct tegra_se_dev *se_dev = container_of(work, struct tegra_se_dev,
|
|
se_work);
|
|
struct crypto_async_request *async_req = NULL;
|
|
struct crypto_async_request *backlog = NULL;
|
|
struct ablkcipher_request *req;
|
|
bool process_requests;
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
do {
|
|
process_requests = false;
|
|
mutex_lock(&se_dev->lock);
|
|
do {
|
|
backlog = crypto_get_backlog(&se_dev->queue);
|
|
async_req = crypto_dequeue_request(&se_dev->queue);
|
|
if (!async_req)
|
|
se_dev->work_q_busy = false;
|
|
|
|
if (backlog) {
|
|
backlog->complete(backlog, -EINPROGRESS);
|
|
backlog = NULL;
|
|
}
|
|
|
|
if (async_req) {
|
|
req = ablkcipher_request_cast(async_req);
|
|
se_dev->reqs[se_dev->req_cnt] = req;
|
|
se_dev->gather_buf_sz += req->nbytes;
|
|
se_dev->req_cnt++;
|
|
process_requests = true;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (se_dev->queue.qlen &&
|
|
(se_dev->req_cnt < SE_MAX_TASKS_PER_SUBMIT));
|
|
mutex_unlock(&se_dev->lock);
|
|
|
|
if (process_requests)
|
|
tegra_se_process_new_req(se_dev);
|
|
} while (se_dev->work_q_busy);
|
|
mutex_unlock(&se_dev->mtx);
|
|
}
|
|
|
|
static int tegra_se_aes_queue_req(struct tegra_se_dev *se_dev,
|
|
struct ablkcipher_request *req)
|
|
{
|
|
int err = 0;
|
|
|
|
mutex_lock(&se_dev->lock);
|
|
err = ablkcipher_enqueue_request(&se_dev->queue, req);
|
|
|
|
if (!se_dev->work_q_busy) {
|
|
se_dev->work_q_busy = true;
|
|
mutex_unlock(&se_dev->lock);
|
|
queue_work(se_dev->se_work_q, &se_dev->se_work);
|
|
} else {
|
|
mutex_unlock(&se_dev->lock);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tegra_se_aes_xts_encrypt(struct ablkcipher_request *req)
|
|
{
|
|
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
|
|
|
|
req_ctx->se_dev = se_devices[SE_AES];
|
|
if (!req_ctx->se_dev) {
|
|
pr_err("Device is NULL\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
req_ctx->encrypt = true;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_XTS;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, req);
|
|
}
|
|
|
|
static int tegra_se_aes_xts_decrypt(struct ablkcipher_request *req)
|
|
{
|
|
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
|
|
|
|
req_ctx->se_dev = se_devices[SE_AES];
|
|
if (!req_ctx->se_dev) {
|
|
pr_err("Device is NULL\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
req_ctx->encrypt = false;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_XTS;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, req);
|
|
}
|
|
|
|
static int tegra_se_aes_cbc_encrypt(struct ablkcipher_request *req)
|
|
{
|
|
struct tegra_se_req_context *req_ctx = ablkcipher_request_ctx(req);
|
|
|
|
req_ctx->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = true;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_CBC;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = false;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_CBC;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = true;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_ECB;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = false;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_ECB;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = true;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_CTR;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = false;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_CTR;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = true;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_OFB;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, 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->se_dev = se_devices[SE_AES];
|
|
req_ctx->encrypt = false;
|
|
req_ctx->op_mode = SE_AES_OP_MODE_OFB;
|
|
|
|
return tegra_se_aes_queue_req(req_ctx->se_dev, req);
|
|
}
|
|
|
|
static void tegra_se_init_aesbuf(struct tegra_se_dev *se_dev)
|
|
{
|
|
int i;
|
|
void *buf = se_dev->total_aes_buf;
|
|
dma_addr_t buf_addr = se_dev->total_aes_buf_addr;
|
|
|
|
for (i = 0; i < SE_MAX_AESBUF_ALLOC; i++) {
|
|
se_dev->aes_bufs[i] = buf + (i * SE_MAX_GATHER_BUF_SZ);
|
|
if (se_dev->ioc)
|
|
se_dev->aes_buf_addrs[i] = buf_addr +
|
|
(i * SE_MAX_GATHER_BUF_SZ);
|
|
atomic_set(&se_dev->aes_buf_stat[i], 1);
|
|
}
|
|
}
|
|
|
|
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;
|
|
struct tegra_se_slot *pslot;
|
|
u8 *pdata = (u8 *)key;
|
|
int ret = 0;
|
|
unsigned int index = 0;
|
|
u32 *cpuvaddr = NULL;
|
|
dma_addr_t iova = 0;
|
|
|
|
se_dev = se_devices[SE_AES];
|
|
|
|
if (!ctx || !se_dev) {
|
|
pr_err("invalid context or dev");
|
|
return -EINVAL;
|
|
}
|
|
ctx->se_dev = se_dev;
|
|
|
|
if (((keylen & SE_KEY_LEN_MASK) != TEGRA_SE_KEY_128_SIZE) &&
|
|
((keylen & SE_KEY_LEN_MASK) != TEGRA_SE_KEY_192_SIZE) &&
|
|
((keylen & SE_KEY_LEN_MASK) != TEGRA_SE_KEY_256_SIZE) &&
|
|
((keylen & SE_KEY_LEN_MASK) != TEGRA_SE_KEY_512_SIZE)) {
|
|
dev_err(se_dev->dev, "invalid key size");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((keylen >> SE_MAGIC_PATTERN_OFFSET) == SE_STORE_KEY_IN_MEM) {
|
|
ctx->is_key_in_mem = true;
|
|
ctx->keylen = (keylen & SE_KEY_LEN_MASK);
|
|
ctx->slot = &keymem_slot;
|
|
memcpy(ctx->key, key, ctx->keylen);
|
|
return 0;
|
|
}
|
|
ctx->is_key_in_mem = false;
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
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");
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -ENOMEM;
|
|
}
|
|
ctx->slot = pslot;
|
|
}
|
|
ctx->keylen = keylen;
|
|
} else if ((keylen >> SE_MAGIC_PATTERN_OFFSET) == SE_MAGIC_PATTERN) {
|
|
ctx->slot = &pre_allocated_slot;
|
|
spin_lock(&key_slot_lock);
|
|
pre_allocated_slot.slot_num =
|
|
((keylen & SE_SLOT_NUM_MASK) >> SE_SLOT_POSITION);
|
|
spin_unlock(&key_slot_lock);
|
|
ctx->keylen = (keylen & SE_KEY_LEN_MASK);
|
|
goto out;
|
|
} else {
|
|
tegra_se_free_key_slot(ctx->slot);
|
|
ctx->slot = &ssk_slot;
|
|
ctx->keylen = AES_KEYSIZE_128;
|
|
goto out;
|
|
}
|
|
|
|
ret = tegra_se_get_free_cmdbuf(se_dev);
|
|
if (ret < 0) {
|
|
dev_err(se_dev->dev, "Couldn't get free cmdbuf\n");
|
|
goto keyslt_free;
|
|
}
|
|
|
|
index = ret;
|
|
|
|
cpuvaddr = se_dev->cmdbuf_addr_list[index].cmdbuf_addr;
|
|
iova = se_dev->cmdbuf_addr_list[index].iova;
|
|
atomic_set(&se_dev->cmdbuf_addr_list[index].free, 0);
|
|
se_dev->cmdbuf_list_entry = index;
|
|
|
|
/* load the key */
|
|
|
|
if (strcmp(crypto_tfm_alg_name(&tfm->base), "xts(aes)")) {
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, pdata, keylen, ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_KEY, se_dev->opcode_addr, cpuvaddr,
|
|
iova, AES_CB);
|
|
} else {
|
|
keylen = keylen / 2;
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, pdata, keylen, ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_XTS_KEY1, se_dev->opcode_addr,
|
|
cpuvaddr, iova, AES_CB);
|
|
if (ret)
|
|
goto keyslt_free;
|
|
|
|
ret = tegra_se_send_key_data(se_dev, pdata + keylen, keylen,
|
|
ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_XTS_KEY2,
|
|
se_dev->opcode_addr, cpuvaddr,
|
|
iova, AES_CB);
|
|
}
|
|
keyslt_free:
|
|
if (ret)
|
|
tegra_se_free_key_slot(ctx->slot);
|
|
out:
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_aes_cra_init(struct crypto_tfm *tfm)
|
|
{
|
|
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_dev = se_devices[SE_DRBG];
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
|
|
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");
|
|
mutex_unlock(&se_dev->mtx);
|
|
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);
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -ENOMEM;
|
|
}
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
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;
|
|
u8 *rdata_addr;
|
|
int ret = 0, j;
|
|
unsigned int num_blocks, data_len = 0;
|
|
struct tegra_se_req_context *req_ctx =
|
|
devm_kzalloc(se_dev->dev,
|
|
sizeof(struct tegra_se_req_context), GFP_KERNEL);
|
|
if (!req_ctx)
|
|
return -ENOMEM;
|
|
|
|
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;
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
req_ctx->op_mode = SE_AES_OP_MODE_RNG_DRBG;
|
|
|
|
se_dev->src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
|
|
req_ctx->config = tegra_se_get_config(se_dev, req_ctx->op_mode, true,
|
|
TEGRA_SE_KEY_128_SIZE);
|
|
req_ctx->crypto_config = tegra_se_get_crypto_config(se_dev,
|
|
req_ctx->op_mode,
|
|
true, 0, true);
|
|
for (j = 0; j <= num_blocks; j++) {
|
|
se_dev->src_ll->addr = rng_ctx->dt_buf_adr;
|
|
se_dev->src_ll->data_len = TEGRA_SE_RNG_DT_SIZE;
|
|
se_dev->dst_ll->addr = rng_ctx->rng_buf_adr;
|
|
se_dev->dst_ll->data_len = TEGRA_SE_RNG_DT_SIZE;
|
|
|
|
tegra_se_send_data(se_dev, req_ctx, NULL, TEGRA_SE_RNG_DT_SIZE,
|
|
se_dev->opcode_addr,
|
|
se_dev->aes_cmdbuf_cpuvaddr);
|
|
ret = tegra_se_channel_submit_gather(
|
|
se_dev, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, 0, se_dev->cmdbuf_cnt,
|
|
NONE);
|
|
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;
|
|
|
|
mutex_unlock(&se_dev->mtx);
|
|
devm_kfree(se_dev->dev, req_ctx);
|
|
|
|
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 void tegra_se_sha_copy_residual_data(
|
|
struct ahash_request *req, struct tegra_se_sha_context *sha_ctx,
|
|
u32 bytes_to_copy)
|
|
{
|
|
struct sg_mapping_iter miter;
|
|
unsigned int sg_flags, total = 0;
|
|
u32 num_sgs, last_block_bytes = bytes_to_copy;
|
|
unsigned long flags;
|
|
struct scatterlist *src_sg;
|
|
u8 *temp_buffer = NULL;
|
|
|
|
src_sg = req->src;
|
|
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
|
|
sg_flags = SG_MITER_ATOMIC | SG_MITER_FROM_SG;
|
|
sg_miter_start(&miter, req->src, num_sgs, sg_flags);
|
|
local_irq_save(flags);
|
|
|
|
temp_buffer = sha_ctx->sha_buf[0];
|
|
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);
|
|
}
|
|
|
|
static int tegra_se_sha_process_buf(struct ahash_request *req, bool is_last,
|
|
bool process_cur_req)
|
|
{
|
|
struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
|
|
struct tegra_se_sha_context *sha_ctx = crypto_ahash_ctx(tfm);
|
|
struct tegra_se_req_context *req_ctx = ahash_request_ctx(req);
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
struct scatterlist *src_sg = req->src;
|
|
struct tegra_se_ll *src_ll;
|
|
struct tegra_se_ll *dst_ll;
|
|
u32 current_total = 0, num_sgs, bytes_process_in_req = 0, num_blks;
|
|
int err = 0;
|
|
|
|
sg_init_one(&se_dev->sg, req->result, req->nbytes);
|
|
|
|
se_dev->sha_last = is_last;
|
|
|
|
if (is_last) {
|
|
/* Prepare buf for residual and current data */
|
|
/* Fill sgs entries */
|
|
se_dev->src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
src_ll = se_dev->src_ll;
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
dst_ll = se_dev->dst_ll;
|
|
|
|
if (sha_ctx->residual_bytes) {
|
|
src_ll->addr = sha_ctx->sha_buf_addr[0];
|
|
src_ll->data_len = sha_ctx->residual_bytes;
|
|
src_ll++;
|
|
}
|
|
|
|
if (process_cur_req) {
|
|
bytes_process_in_req = req->nbytes;
|
|
err = tegra_map_sg(se_dev->dev, src_sg, 1,
|
|
DMA_TO_DEVICE, src_ll,
|
|
bytes_process_in_req);
|
|
if (!err)
|
|
return -EINVAL;
|
|
|
|
current_total = req->nbytes + sha_ctx->residual_bytes;
|
|
sha_ctx->total_count += current_total;
|
|
|
|
err = tegra_map_sg(se_dev->dev, &se_dev->sg, 1,
|
|
DMA_FROM_DEVICE, dst_ll,
|
|
bytes_process_in_req);
|
|
if (!err)
|
|
return -EINVAL;
|
|
|
|
se_dev->bytes_mapped = bytes_process_in_req;
|
|
se_dev->sha_src_mapped = true;
|
|
se_dev->sha_dst_mapped = true;
|
|
} else {
|
|
current_total = sha_ctx->residual_bytes;
|
|
sha_ctx->total_count += current_total;
|
|
if (!current_total) {
|
|
req->base.complete(&req->base, 0);
|
|
return 0;
|
|
}
|
|
err = tegra_map_sg(se_dev->dev, &se_dev->sg, 1,
|
|
DMA_FROM_DEVICE, dst_ll,
|
|
req->nbytes);
|
|
if (!err)
|
|
return -EINVAL;
|
|
|
|
se_dev->sha_dst_mapped = true;
|
|
}
|
|
} else {
|
|
current_total = req->nbytes + sha_ctx->residual_bytes;
|
|
num_blks = current_total / sha_ctx->blk_size;
|
|
|
|
/* If total bytes is less than or equal to one blk,
|
|
* copy to residual and return.
|
|
*/
|
|
if (num_blks <= 1) {
|
|
num_sgs = tegra_se_count_sgs(req->src, req->nbytes);
|
|
sg_copy_to_buffer(req->src, num_sgs,
|
|
sha_ctx->sha_buf[0] +
|
|
sha_ctx->residual_bytes, req->nbytes);
|
|
sha_ctx->residual_bytes += req->nbytes;
|
|
req->base.complete(&req->base, 0);
|
|
return 0;
|
|
}
|
|
|
|
/* Number of bytes to be processed from given request buffers */
|
|
bytes_process_in_req = (num_blks * sha_ctx->blk_size) -
|
|
sha_ctx->residual_bytes;
|
|
sha_ctx->total_count += bytes_process_in_req;
|
|
|
|
/* Fill sgs entries */
|
|
/* If residual bytes are present copy it to second buffer */
|
|
if (sha_ctx->residual_bytes)
|
|
memcpy(sha_ctx->sha_buf[1], sha_ctx->sha_buf[0],
|
|
sha_ctx->residual_bytes);
|
|
|
|
sha_ctx->total_count += sha_ctx->residual_bytes;
|
|
|
|
se_dev->src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
src_ll = se_dev->src_ll;
|
|
if (sha_ctx->residual_bytes) {
|
|
src_ll->addr = sha_ctx->sha_buf_addr[1];
|
|
src_ll->data_len = sha_ctx->residual_bytes;
|
|
src_ll++;
|
|
}
|
|
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
dst_ll = se_dev->dst_ll;
|
|
|
|
/* Map required bytes to process in given request */
|
|
err = tegra_map_sg(se_dev->dev, src_sg, 1, DMA_TO_DEVICE,
|
|
src_ll, bytes_process_in_req);
|
|
if (!err)
|
|
return -EINVAL;
|
|
|
|
/* Copy residual data */
|
|
sha_ctx->residual_bytes = current_total - (num_blks *
|
|
sha_ctx->blk_size);
|
|
tegra_se_sha_copy_residual_data(req, sha_ctx,
|
|
sha_ctx->residual_bytes);
|
|
|
|
/* Total bytes to be processed */
|
|
current_total = (num_blks * sha_ctx->blk_size);
|
|
|
|
err = tegra_map_sg(se_dev->dev, &se_dev->sg, 1, DMA_FROM_DEVICE,
|
|
dst_ll, bytes_process_in_req);
|
|
if (!err)
|
|
return -EINVAL;
|
|
|
|
se_dev->bytes_mapped = bytes_process_in_req;
|
|
se_dev->sha_src_mapped = true;
|
|
se_dev->sha_dst_mapped = true;
|
|
}
|
|
|
|
req_ctx->config = tegra_se_get_config(se_dev, sha_ctx->op_mode,
|
|
false, 0);
|
|
err = tegra_se_send_sha_data(se_dev, req_ctx, sha_ctx,
|
|
current_total, is_last);
|
|
if (err) {
|
|
if (se_dev->sha_src_mapped)
|
|
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE,
|
|
bytes_process_in_req);
|
|
if (se_dev->sha_dst_mapped)
|
|
tegra_unmap_sg(se_dev->dev, &se_dev->sg,
|
|
DMA_FROM_DEVICE, bytes_process_in_req);
|
|
return err;
|
|
}
|
|
sha_ctx->is_first = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_sha_op(struct ahash_request *req, bool is_last,
|
|
bool process_cur_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];
|
|
u32 mode;
|
|
int ret;
|
|
|
|
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",
|
|
}
|
|
};
|
|
|
|
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 the request length is zero, */
|
|
if (!req->nbytes) {
|
|
if (sha_ctx->total_count) {
|
|
req->base.complete(&req->base, 0);
|
|
return 0; /* allow empty packets */
|
|
} else { /* total_count equals zero */
|
|
if (is_last) {
|
|
/* If this is the last packet 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);
|
|
req->base.complete(&req->base, 0);
|
|
return 0;
|
|
} else {
|
|
req->base.complete(&req->base, 0);
|
|
return 0; /* allow empty first packet */
|
|
}
|
|
}
|
|
}
|
|
mutex_lock(&se_dev->mtx);
|
|
|
|
ret = tegra_se_sha_process_buf(req, is_last, process_cur_req);
|
|
if (ret) {
|
|
mutex_unlock(&se_dev->mtx);
|
|
return ret;
|
|
}
|
|
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_sha_init(struct ahash_request *req)
|
|
{
|
|
struct tegra_se_req_context *req_ctx;
|
|
struct crypto_ahash *tfm = NULL;
|
|
struct tegra_se_sha_context *sha_ctx;
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "SHA request not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
tfm = crypto_ahash_reqtfm(req);
|
|
if (!tfm) {
|
|
dev_err(se_dev->dev, "SHA transform not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sha_ctx = crypto_ahash_ctx(tfm);
|
|
if (!sha_ctx) {
|
|
dev_err(se_dev->dev, "SHA context not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
req_ctx = ahash_request_ctx(req);
|
|
if (!req_ctx) {
|
|
dev_err(se_dev->dev, "Request context not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
|
|
sha_ctx->total_count = 0;
|
|
sha_ctx->is_first = true;
|
|
sha_ctx->blk_size = crypto_tfm_alg_blocksize(crypto_ahash_tfm(tfm));
|
|
sha_ctx->residual_bytes = 0;
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_sha_update(struct ahash_request *req)
|
|
{
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
int ret = 0;
|
|
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "SHA request not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
se_dev->sha_req = req;
|
|
|
|
ret = tegra_se_sha_op(req, false, false);
|
|
if (ret)
|
|
dev_err(se_dev->dev, "tegra_se_sha_update failed - %d\n", ret);
|
|
else
|
|
ret = -EBUSY;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_sha_finup(struct ahash_request *req)
|
|
{
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
int ret = 0;
|
|
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "SHA request not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
se_dev->sha_req = req;
|
|
|
|
ret = tegra_se_sha_op(req, true, true);
|
|
if (ret)
|
|
dev_err(se_dev->dev, "tegra_se_sha_finup failed - %d\n", ret);
|
|
else
|
|
ret = -EBUSY;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_sha_final(struct ahash_request *req)
|
|
{
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
int ret = 0;
|
|
|
|
if (!req) {
|
|
dev_err(se_dev->dev, "SHA request not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
se_dev->sha_req = req;
|
|
|
|
/* Do not process data in given request */
|
|
ret = tegra_se_sha_op(req, true, false);
|
|
if (ret)
|
|
dev_err(se_dev->dev, "tegra_se_sha_final failed - %d\n", ret);
|
|
else
|
|
ret = -EBUSY;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_sha_digest(struct ahash_request *req)
|
|
{
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
int ret = 0;
|
|
|
|
ret = tegra_se_sha_init(req);
|
|
if (ret)
|
|
return ret;
|
|
|
|
se_dev->sha_req = req;
|
|
|
|
ret = tegra_se_sha_op(req, true, true);
|
|
if (ret)
|
|
dev_err(se_dev->dev, "tegra_se_sha_digest failed - %d\n", ret);
|
|
else
|
|
ret = -EBUSY;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int tegra_se_sha_export(struct ahash_request *req, void *out)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_sha_import(struct ahash_request *req, const void *in)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int tegra_se_sha_cra_init(struct crypto_tfm *tfm)
|
|
{
|
|
struct tegra_se_sha_context *sha_ctx;
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
|
|
crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm),
|
|
sizeof(struct tegra_se_sha_context));
|
|
sha_ctx = crypto_tfm_ctx(tfm);
|
|
if (!sha_ctx) {
|
|
dev_err(se_dev->dev, "SHA context not valid\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
sha_ctx->sha_buf[0] = dma_alloc_coherent(
|
|
se_dev->dev, (TEGRA_SE_SHA_MAX_BLOCK_SIZE * 2),
|
|
&sha_ctx->sha_buf_addr[0], GFP_KERNEL);
|
|
if (!sha_ctx->sha_buf[0]) {
|
|
dev_err(se_dev->dev, "Cannot allocate memory to sha_buf[0]\n");
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -ENOMEM;
|
|
}
|
|
sha_ctx->sha_buf[1] = dma_alloc_coherent(
|
|
se_dev->dev, (TEGRA_SE_SHA_MAX_BLOCK_SIZE * 2),
|
|
&sha_ctx->sha_buf_addr[1], GFP_KERNEL);
|
|
if (!sha_ctx->sha_buf[1]) {
|
|
dma_free_coherent(
|
|
se_dev->dev, (TEGRA_SE_SHA_MAX_BLOCK_SIZE * 2),
|
|
sha_ctx->sha_buf[0], sha_ctx->sha_buf_addr[0]);
|
|
sha_ctx->sha_buf[0] = NULL;
|
|
dev_err(se_dev->dev, "Cannot allocate memory to sha_buf[1]\n");
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -ENOMEM;
|
|
}
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tegra_se_sha_cra_exit(struct crypto_tfm *tfm)
|
|
{
|
|
struct tegra_se_sha_context *sha_ctx = crypto_tfm_ctx(tfm);
|
|
struct tegra_se_dev *se_dev = se_devices[SE_SHA];
|
|
int i;
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
for (i = 0; i < 2; i++) {
|
|
/* dma_free_coherent does not panic if addr is NULL */
|
|
dma_free_coherent(
|
|
se_dev->dev, (TEGRA_SE_SHA_MAX_BLOCK_SIZE * 2),
|
|
sha_ctx->sha_buf[i], sha_ctx->sha_buf_addr[i]);
|
|
sha_ctx->sha_buf[i] = NULL;
|
|
}
|
|
mutex_unlock(&se_dev->mtx);
|
|
}
|
|
|
|
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_req_context *req_ctx = ahash_request_ctx(req);
|
|
struct tegra_se_dev *se_dev;
|
|
struct scatterlist *src_sg;
|
|
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];
|
|
unsigned int total;
|
|
int ret = 0, i = 0;
|
|
bool padding_needed = false;
|
|
unsigned long flags;
|
|
unsigned int sg_flags = SG_MITER_ATOMIC;
|
|
u8 *temp_buffer = NULL;
|
|
bool use_orig_iv = true;
|
|
|
|
se_dev = se_devices[SE_CMAC];
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
|
|
req_ctx->op_mode = SE_AES_OP_MODE_CMAC;
|
|
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 = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
|
|
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,
|
|
se_dev->src_ll, total);
|
|
if (!ret) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
req_ctx->config = tegra_se_get_config(se_dev, req_ctx->op_mode,
|
|
true, cmac_ctx->keylen);
|
|
/* write zero IV */
|
|
memset(piv, 0, TEGRA_SE_AES_IV_SIZE);
|
|
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, piv, TEGRA_SE_AES_IV_SIZE,
|
|
cmac_ctx->slot->slot_num, SE_KEY_TABLE_TYPE_ORGIV,
|
|
se_dev->opcode_addr, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, NONE);
|
|
if (ret)
|
|
goto out;
|
|
|
|
req_ctx->crypto_config = tegra_se_get_crypto_config(
|
|
se_dev, req_ctx->op_mode, true,
|
|
cmac_ctx->slot->slot_num, true);
|
|
|
|
tegra_se_send_data(se_dev, req_ctx, NULL, total,
|
|
se_dev->opcode_addr,
|
|
se_dev->aes_cmdbuf_cpuvaddr);
|
|
ret = tegra_se_channel_submit_gather(
|
|
se_dev, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, 0, se_dev->cmdbuf_cnt, NONE);
|
|
if (ret)
|
|
goto out;
|
|
|
|
tegra_se_read_cmac_result(se_dev, piv,
|
|
TEGRA_SE_AES_CMAC_DIGEST_SIZE, false);
|
|
src_sg = req->src;
|
|
tegra_unmap_sg(se_dev->dev, src_sg, DMA_TO_DEVICE, total);
|
|
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;
|
|
sg_miter_start(&miter, req->src, num_sgs, sg_flags);
|
|
total = 0;
|
|
cmac_ctx->buffer = dma_alloc_coherent(se_dev->dev,
|
|
TEGRA_SE_AES_BLOCK_SIZE,
|
|
&cmac_ctx->dma_addr, GFP_KERNEL);
|
|
if (!cmac_ctx->buffer) {
|
|
ret = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
local_irq_save(flags);
|
|
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 = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
|
|
se_dev->src_ll->addr = cmac_ctx->dma_addr;
|
|
se_dev->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);
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, piv, TEGRA_SE_AES_IV_SIZE,
|
|
cmac_ctx->slot->slot_num, SE_KEY_TABLE_TYPE_ORGIV,
|
|
se_dev->opcode_addr, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, NONE);
|
|
} else {
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, piv, TEGRA_SE_AES_IV_SIZE,
|
|
cmac_ctx->slot->slot_num, SE_KEY_TABLE_TYPE_UPDTDIV,
|
|
se_dev->opcode_addr, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, NONE);
|
|
}
|
|
|
|
if (ret)
|
|
goto out;
|
|
|
|
req_ctx->config = tegra_se_get_config(se_dev, req_ctx->op_mode,
|
|
true, cmac_ctx->keylen);
|
|
req_ctx->crypto_config = tegra_se_get_crypto_config(
|
|
se_dev, req_ctx->op_mode, true,
|
|
cmac_ctx->slot->slot_num, use_orig_iv);
|
|
|
|
tegra_se_send_data(se_dev, req_ctx, NULL, TEGRA_SE_AES_BLOCK_SIZE,
|
|
se_dev->opcode_addr, se_dev->aes_cmdbuf_cpuvaddr);
|
|
ret = tegra_se_channel_submit_gather(
|
|
se_dev, se_dev->aes_cmdbuf_cpuvaddr,
|
|
se_dev->aes_cmdbuf_iova, 0, se_dev->cmdbuf_cnt, NONE);
|
|
if (ret)
|
|
goto out;
|
|
tegra_se_read_cmac_result(se_dev, req->result,
|
|
TEGRA_SE_AES_CMAC_DIGEST_SIZE, false);
|
|
out:
|
|
if (cmac_ctx->buffer)
|
|
dma_free_coherent(se_dev->dev, TEGRA_SE_AES_BLOCK_SIZE,
|
|
cmac_ctx->buffer, cmac_ctx->dma_addr);
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
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_req_context *req_ctx = NULL;
|
|
struct tegra_se_dev *se_dev;
|
|
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;
|
|
|
|
se_dev = se_devices[SE_CMAC];
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
|
|
if (!ctx) {
|
|
dev_err(se_dev->dev, "invalid context");
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -EINVAL;
|
|
}
|
|
|
|
req_ctx = devm_kzalloc(se_dev->dev, sizeof(struct tegra_se_req_context),
|
|
GFP_KERNEL);
|
|
if (!req_ctx) {
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -ENOMEM;
|
|
}
|
|
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");
|
|
ret = -EINVAL;
|
|
goto free_ctx;
|
|
}
|
|
|
|
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");
|
|
ret = -ENOMEM;
|
|
goto free_ctx;
|
|
}
|
|
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");
|
|
ret = -ENOMEM;
|
|
goto keyslt_free;
|
|
}
|
|
memset(pbuf, 0, TEGRA_SE_AES_BLOCK_SIZE);
|
|
|
|
se_dev->src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
|
|
se_dev->src_ll->addr = pbuf_adr;
|
|
se_dev->src_ll->data_len = TEGRA_SE_AES_BLOCK_SIZE;
|
|
se_dev->dst_ll->addr = pbuf_adr;
|
|
se_dev->dst_ll->data_len = TEGRA_SE_AES_BLOCK_SIZE;
|
|
|
|
/* load the key */
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, (u8 *)key, keylen, ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_KEY, se_dev->opcode_addr,
|
|
se_dev->aes_cmdbuf_cpuvaddr, se_dev->aes_cmdbuf_iova, NONE);
|
|
if (ret) {
|
|
dev_err(se_dev->dev,
|
|
"tegra_se_send_key_data for loading cmac key failed\n");
|
|
goto out;
|
|
}
|
|
|
|
/* write zero IV */
|
|
memset(piv, 0, TEGRA_SE_AES_IV_SIZE);
|
|
|
|
/* load IV */
|
|
ret = tegra_se_send_key_data(
|
|
se_dev, piv, TEGRA_SE_AES_IV_SIZE, ctx->slot->slot_num,
|
|
SE_KEY_TABLE_TYPE_ORGIV, se_dev->opcode_addr,
|
|
se_dev->aes_cmdbuf_cpuvaddr, se_dev->aes_cmdbuf_iova, NONE);
|
|
if (ret) {
|
|
dev_err(se_dev->dev,
|
|
"tegra_se_send_key_data for loading cmac iv failed\n");
|
|
goto out;
|
|
}
|
|
|
|
/* config crypto algo */
|
|
req_ctx->config = tegra_se_get_config(se_dev, SE_AES_OP_MODE_CBC,
|
|
true, keylen);
|
|
req_ctx->crypto_config = tegra_se_get_crypto_config(
|
|
se_dev, SE_AES_OP_MODE_CBC, true,
|
|
ctx->slot->slot_num, true);
|
|
|
|
tegra_se_send_data(se_dev, req_ctx, NULL, TEGRA_SE_AES_BLOCK_SIZE,
|
|
se_dev->opcode_addr, se_dev->aes_cmdbuf_cpuvaddr);
|
|
ret = tegra_se_channel_submit_gather(
|
|
se_dev, se_dev->aes_cmdbuf_cpuvaddr, se_dev->aes_cmdbuf_iova,
|
|
0, se_dev->cmdbuf_cnt, NONE);
|
|
if (ret) {
|
|
dev_err(se_dev->dev,
|
|
"tegra_se_aes_cmac_setkey:: start op failed\n");
|
|
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:
|
|
if (pbuf)
|
|
dma_free_coherent(se_dev->dev, TEGRA_SE_AES_BLOCK_SIZE,
|
|
pbuf, pbuf_adr);
|
|
keyslt_free:
|
|
if (ret)
|
|
tegra_se_free_key_slot(ctx->slot);
|
|
free_ctx:
|
|
devm_kfree(se_dev->dev, req_ctx);
|
|
mutex_unlock(&se_dev->mtx);
|
|
|
|
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("No RSA context\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ctx->mod_len;
|
|
}
|
|
|
|
static int tegra_se_send_rsa_data(struct tegra_se_dev *se_dev,
|
|
struct tegra_se_aes_rsa_context *rsa_ctx)
|
|
{
|
|
u32 *cmdbuf_cpuvaddr = NULL;
|
|
dma_addr_t cmdbuf_iova = 0;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
int err = 0;
|
|
u32 val = 0;
|
|
|
|
cmdbuf_cpuvaddr = dma_alloc_attrs(se_dev->dev->parent, SZ_4K,
|
|
&cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
if (!cmdbuf_cpuvaddr) {
|
|
dev_err(se_dev->dev, "Failed to allocate memory for cmdbuf\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
|
|
val = SE_CONFIG_ENC_ALG(ALG_RSA) |
|
|
SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_incr(se_dev->opcode_addr, 8);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = RSA_KEY_SLOT(rsa_ctx->slot->slot_num);
|
|
cmdbuf_cpuvaddr[i++] = (rsa_ctx->mod_len / 64) - 1;
|
|
cmdbuf_cpuvaddr[i++] = (rsa_ctx->exp_len / 4);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(se_dev->src_ll->addr);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(se_dev->src_ll->addr)) |
|
|
SE_ADDR_HI_SZ(se_dev->src_ll->data_len));
|
|
cmdbuf_cpuvaddr[i++] = (u32)(se_dev->dst_ll->addr);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(se_dev->dst_ll->addr)) |
|
|
SE_ADDR_HI_SZ(se_dev->dst_ll->data_len));
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
|
|
cmdbuf_num_words = i;
|
|
|
|
err = tegra_se_channel_submit_gather(se_dev, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, 0, cmdbuf_num_words,
|
|
NONE);
|
|
|
|
dma_free_attrs(se_dev->dev->parent, SZ_4K, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, __DMA_ATTR(attrs));
|
|
|
|
return err;
|
|
}
|
|
|
|
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;
|
|
u32 module_key_length = 0;
|
|
u32 exponent_key_length = 0;
|
|
u32 pkt, val;
|
|
u32 key_size_words;
|
|
u32 key_word_size = 4;
|
|
u32 *pkeydata = (u32 *)key;
|
|
s32 j = 0;
|
|
struct tegra_se_rsa_slot *pslot;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
u32 *cmdbuf_cpuvaddr = NULL;
|
|
dma_addr_t cmdbuf_iova = 0;
|
|
int err = 0;
|
|
int timeout = 0;
|
|
|
|
se_dev = se_devices[SE_RSA];
|
|
|
|
if (!ctx || !key) {
|
|
dev_err(se_dev->dev, "No RSA context or Key\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Allocate rsa key slot */
|
|
if (!ctx->slot) {
|
|
for (timeout = 0; timeout < SE_KEYSLOT_TIMEOUT; timeout++) {
|
|
pslot = tegra_se_alloc_rsa_key_slot();
|
|
if (!pslot) {
|
|
mdelay(SE_KEYSLOT_MDELAY);
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pslot) {
|
|
dev_err(se_dev->dev, "no free key slot\n");
|
|
return -ENOMEM;
|
|
} else {
|
|
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, "Invalid RSA modulus length\n");
|
|
return -EDOM;
|
|
}
|
|
|
|
ctx->mod_len = module_key_length;
|
|
ctx->exp_len = exponent_key_length;
|
|
|
|
cmdbuf_cpuvaddr = dma_alloc_attrs(se_dev->dev->parent, SZ_64K,
|
|
&cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
if (!cmdbuf_cpuvaddr) {
|
|
tegra_se_rsa_free_key_slot(ctx->slot);
|
|
dev_err(se_dev->dev, "Failed to allocate memory for cmdbuf\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
|
|
if (exponent_key_length) {
|
|
key_size_words = (exponent_key_length / key_word_size);
|
|
/* Write exponent */
|
|
for (j = (key_size_words - 1); j >= 0; j--) {
|
|
pkt = RSA_KEY_NUM(ctx->slot->slot_num) |
|
|
RSA_KEY_TYPE(RSA_KEY_TYPE_EXP) |
|
|
RSA_KEY_PKT_WORD_ADDR(j);
|
|
val = SE_RSA_KEYTABLE_PKT(pkt);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_ADDR_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_DATA_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = *pkeydata++;
|
|
}
|
|
}
|
|
|
|
if (module_key_length) {
|
|
key_size_words = (module_key_length / key_word_size);
|
|
/* Write modulus */
|
|
for (j = (key_size_words - 1); j >= 0; j--) {
|
|
pkt = RSA_KEY_NUM(ctx->slot->slot_num) |
|
|
RSA_KEY_TYPE(RSA_KEY_TYPE_MOD) |
|
|
RSA_KEY_PKT_WORD_ADDR(j);
|
|
val = SE_RSA_KEYTABLE_PKT(pkt);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_ADDR_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_DATA_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = *pkeydata++;
|
|
}
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_DUMMY);
|
|
cmdbuf_num_words = i;
|
|
|
|
mutex_lock(&se_dev->mtx);
|
|
err = tegra_se_channel_submit_gather(se_dev, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, 0, cmdbuf_num_words,
|
|
NONE);
|
|
mutex_unlock(&se_dev->mtx);
|
|
if (err)
|
|
tegra_se_rsa_free_key_slot(ctx->slot);
|
|
dma_free_attrs(se_dev->dev->parent, SZ_64K, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, __DMA_ATTR(attrs));
|
|
|
|
return err;
|
|
}
|
|
|
|
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;
|
|
u32 num_src_sgs, num_dst_sgs;
|
|
int ret1 = 0, ret2 = 0;
|
|
|
|
se_dev = se_devices[SE_RSA];
|
|
|
|
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 src input 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 dst input length not in range\n");
|
|
return -EDOM;
|
|
}
|
|
|
|
if (req->src_len != rsa_ctx->mod_len) {
|
|
dev_err(se_dev->dev, "Invalid RSA src input 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_dev->mtx);
|
|
se_dev->src_ll = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
|
|
if (req->src == req->dst) {
|
|
se_dev->dst_ll = se_dev->src_ll;
|
|
ret1 = tegra_map_sg(se_dev->dev, req->src, 1, DMA_BIDIRECTIONAL,
|
|
se_dev->src_ll, req->src_len);
|
|
if (!ret1) {
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
ret1 = tegra_map_sg(se_dev->dev, req->src, 1, DMA_TO_DEVICE,
|
|
se_dev->src_ll, req->src_len);
|
|
ret2 = tegra_map_sg(se_dev->dev, req->dst, 1, DMA_FROM_DEVICE,
|
|
se_dev->dst_ll, req->dst_len);
|
|
if (!ret1 || !ret2) {
|
|
mutex_unlock(&se_dev->mtx);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
ret1 = tegra_se_send_rsa_data(se_dev, rsa_ctx);
|
|
if (ret1)
|
|
dev_err(se_dev->dev, "RSA send data failed err = %d\n", ret1);
|
|
|
|
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);
|
|
}
|
|
|
|
mutex_unlock(&se_dev->mtx);
|
|
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, *cmdbuf_cpuvaddr = NULL;
|
|
struct tegra_se_rsa_slot *pslot;
|
|
u32 cmdbuf_num_words = 0;
|
|
dma_addr_t cmdbuf_iova = 0;
|
|
int i = 0, err, j;
|
|
|
|
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 range\n");
|
|
return -EDOM;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr = dma_alloc_attrs(se_dev->dev->parent, SZ_64K,
|
|
&cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
if (!cmdbuf_cpuvaddr) {
|
|
tegra_se_rsa_free_key_slot(ctx->slot);
|
|
dev_err(se_dev->dev, "Failed to allocate cmdbuf\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
|
|
if (exponent_key_length) {
|
|
key_size_words = (exponent_key_length / key_word_size);
|
|
/* Write exponent */
|
|
for (j = (key_size_words - 1); j >= 0; j--) {
|
|
pkt = RSA_KEY_NUM(ctx->slot->slot_num) |
|
|
RSA_KEY_TYPE(RSA_KEY_TYPE_EXP) |
|
|
RSA_KEY_PKT_WORD_ADDR(j);
|
|
val = SE_RSA_KEYTABLE_PKT(pkt);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_ADDR_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_DATA_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = be32_to_cpu(*pkeydata++);
|
|
}
|
|
}
|
|
|
|
if (module_key_length) {
|
|
pkeydata = (u32 *)ctx->p;
|
|
key_size_words = (module_key_length / key_word_size);
|
|
/* Write modulus */
|
|
for (j = (key_size_words - 1); j >= 0; j--) {
|
|
pkt = RSA_KEY_NUM(ctx->slot->slot_num) |
|
|
RSA_KEY_TYPE(RSA_KEY_TYPE_MOD) |
|
|
RSA_KEY_PKT_WORD_ADDR(j);
|
|
val = SE_RSA_KEYTABLE_PKT(pkt);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_ADDR_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr +
|
|
SE_RSA_KEYTABLE_DATA_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = be32_to_cpu(*pkeydata++);
|
|
}
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_DUMMY);
|
|
cmdbuf_num_words = i;
|
|
|
|
err = tegra_se_channel_submit_gather(se_dev, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, 0, cmdbuf_num_words,
|
|
NONE);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "%s: channel_submit failed\n", __func__);
|
|
tegra_se_rsa_free_key_slot(ctx->slot);
|
|
}
|
|
dma_free_attrs(se_dev->dev->parent, SZ_64K, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, __DMA_ATTR(attrs));
|
|
|
|
return err;
|
|
}
|
|
|
|
static void tegra_se_fix_endianness(struct tegra_se_dev *se_dev,
|
|
struct scatterlist *sg, u32 num_sgs,
|
|
unsigned int 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;
|
|
u32 num_src_sgs, num_dst_sgs;
|
|
u8 *base_buff = NULL;
|
|
struct scatterlist src;
|
|
u32 *cmdbuf_cpuvaddr = NULL;
|
|
dma_addr_t cmdbuf_iova = 0;
|
|
u32 cmdbuf_num_words = 0, i = 0;
|
|
int err, j;
|
|
unsigned int total, zpad_sz;
|
|
u32 val;
|
|
|
|
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 = (struct tegra_se_ll *)(se_dev->src_ll_buf);
|
|
se_dev->dst_ll = (struct tegra_se_ll *)(se_dev->dst_ll_buf);
|
|
|
|
err = tegra_map_sg(se_dev->dev, src_sg, 1, DMA_TO_DEVICE,
|
|
se_dev->src_ll, total);
|
|
if (!err) {
|
|
err = -EINVAL;
|
|
goto free;
|
|
}
|
|
err = tegra_map_sg(se_dev->dev, req->dst, 1, DMA_FROM_DEVICE,
|
|
se_dev->dst_ll, req->dst_len);
|
|
if (!err) {
|
|
err = -EINVAL;
|
|
goto unmap_src;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr = dma_alloc_attrs(se_dev->dev->parent, SZ_4K,
|
|
&cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
if (!cmdbuf_cpuvaddr) {
|
|
dev_err(se_dev->dev, "%s: dma_alloc_attrs failed\n", __func__);
|
|
err = -ENOMEM;
|
|
goto unmap_dst;
|
|
}
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE);
|
|
|
|
val = SE_CONFIG_ENC_ALG(ALG_RSA) | SE_CONFIG_DEC_ALG(ALG_NOP) |
|
|
SE_CONFIG_DST(DST_MEMORY);
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_incr(se_dev->opcode_addr, 8);
|
|
cmdbuf_cpuvaddr[i++] = val;
|
|
cmdbuf_cpuvaddr[i++] = RSA_KEY_SLOT(dh_ctx->slot->slot_num);
|
|
cmdbuf_cpuvaddr[i++] = (dh_ctx->p_size / 64) - 1;
|
|
cmdbuf_cpuvaddr[i++] = (dh_ctx->key_size / 4);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(se_dev->src_ll->addr);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(se_dev->src_ll->addr)) |
|
|
SE_ADDR_HI_SZ(se_dev->src_ll->data_len));
|
|
cmdbuf_cpuvaddr[i++] = (u32)(se_dev->dst_ll->addr);
|
|
cmdbuf_cpuvaddr[i++] = (u32)(SE_ADDR_HI_MSB(MSB(se_dev->dst_ll->addr)) |
|
|
SE_ADDR_HI_SZ(se_dev->dst_ll->data_len));
|
|
|
|
cmdbuf_cpuvaddr[i++] = __nvhost_opcode_nonincr(
|
|
se_dev->opcode_addr + SE_RSA_OPERATION_OFFSET, 1);
|
|
cmdbuf_cpuvaddr[i++] = SE_OPERATION_WRSTALL(WRSTALL_TRUE) |
|
|
SE_OPERATION_LASTBUF(LASTBUF_TRUE) |
|
|
SE_OPERATION_OP(OP_START);
|
|
|
|
cmdbuf_num_words = i;
|
|
|
|
err = tegra_se_channel_submit_gather(
|
|
se_dev, cmdbuf_cpuvaddr, cmdbuf_iova, 0,
|
|
cmdbuf_num_words, NONE);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "%s: channel_submit failed\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
tegra_se_fix_endianness(se_dev, req->dst, num_dst_sgs,
|
|
req->dst_len, false);
|
|
exit:
|
|
dma_free_attrs(se_dev->dev->parent, SZ_4K, cmdbuf_cpuvaddr,
|
|
cmdbuf_iova, __DMA_ATTR(attrs));
|
|
unmap_dst:
|
|
tegra_unmap_sg(se_dev->dev, req->dst, DMA_FROM_DEVICE, req->dst_len);
|
|
unmap_src:
|
|
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 = se_devices[SE_RSA];
|
|
|
|
ret = crypto_dh_decode_key(buf, len, ¶ms);
|
|
if (ret) {
|
|
dev_err(ctx->se_dev->dev, "failed to decode DH input\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = tegra_se_dh_set_params(ctx, ¶ms);
|
|
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 = 100,
|
|
.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 = "xts(aes)",
|
|
.cra_driver_name = "xts-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_xts_encrypt,
|
|
.decrypt = tegra_se_aes_xts_decrypt,
|
|
}
|
|
}, {
|
|
.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,
|
|
.export = tegra_se_sha_export,
|
|
.import = tegra_se_sha_import,
|
|
.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,
|
|
.export = tegra_se_sha_export,
|
|
.import = tegra_se_sha_import,
|
|
.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,
|
|
.export = tegra_se_sha_export,
|
|
.import = tegra_se_sha_import,
|
|
.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,
|
|
.export = tegra_se_sha_export,
|
|
.import = tegra_se_sha_import,
|
|
.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,
|
|
.export = tegra_se_sha_export,
|
|
.import = tegra_se_sha_import,
|
|
.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 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 int tegra_se_nvhost_prepare_poweroff(struct platform_device *pdev)
|
|
{
|
|
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
|
|
struct tegra_se_dev *se_dev = pdata->private_data;
|
|
|
|
if (se_dev->channel) {
|
|
nvhost_syncpt_put_ref_ext(se_dev->pdev, se_dev->syncpt_id);
|
|
nvhost_putchannel(se_dev->channel, 1);
|
|
se_dev->channel = NULL;
|
|
|
|
/* syncpt will be released along with channel */
|
|
se_dev->syncpt_id = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct tegra_se_chipdata tegra18_se_chipdata = {
|
|
.aes_freq = 600000000,
|
|
.cpu_freq_mhz = 2400,
|
|
};
|
|
|
|
static struct nvhost_device_data nvhost_se1_info = {
|
|
.clocks = {{"se", 600000000},
|
|
{"emc", UINT_MAX,
|
|
NVHOST_MODULE_ID_EXTERNAL_MEMORY_CONTROLLER,
|
|
0, TEGRA_BWMGR_SET_EMC_FLOOR}, {} },
|
|
.can_powergate = true,
|
|
.autosuspend_delay = 500,
|
|
.class = NV_SE1_CLASS_ID,
|
|
.private_data = &tegra18_se_chipdata,
|
|
.serialize = 1,
|
|
.push_work_done = 1,
|
|
.vm_regs = {{SE_STREAMID_REG_OFFSET, true} },
|
|
.kernel_only = true,
|
|
.bwmgr_client_id = TEGRA_BWMGR_CLIENT_SE1,
|
|
.prepare_poweroff = tegra_se_nvhost_prepare_poweroff,
|
|
};
|
|
|
|
static struct nvhost_device_data nvhost_se2_info = {
|
|
.clocks = {{"se", 600000000},
|
|
{"emc", UINT_MAX,
|
|
NVHOST_MODULE_ID_EXTERNAL_MEMORY_CONTROLLER,
|
|
0, TEGRA_BWMGR_SET_EMC_FLOOR}, {} },
|
|
.can_powergate = true,
|
|
.autosuspend_delay = 500,
|
|
.class = NV_SE2_CLASS_ID,
|
|
.private_data = &tegra18_se_chipdata,
|
|
.serialize = 1,
|
|
.push_work_done = 1,
|
|
.vm_regs = {{SE_STREAMID_REG_OFFSET, true} },
|
|
.kernel_only = true,
|
|
.bwmgr_client_id = TEGRA_BWMGR_CLIENT_SE2,
|
|
.prepare_poweroff = tegra_se_nvhost_prepare_poweroff,
|
|
};
|
|
|
|
static struct nvhost_device_data nvhost_se3_info = {
|
|
.clocks = {{"se", 600000000},
|
|
{"emc", UINT_MAX,
|
|
NVHOST_MODULE_ID_EXTERNAL_MEMORY_CONTROLLER,
|
|
0, TEGRA_BWMGR_SET_EMC_FLOOR}, {} },
|
|
.can_powergate = true,
|
|
.autosuspend_delay = 500,
|
|
.class = NV_SE3_CLASS_ID,
|
|
.private_data = &tegra18_se_chipdata,
|
|
.serialize = 1,
|
|
.push_work_done = 1,
|
|
.vm_regs = {{SE_STREAMID_REG_OFFSET, true} },
|
|
.kernel_only = true,
|
|
.bwmgr_client_id = TEGRA_BWMGR_CLIENT_SE3,
|
|
.prepare_poweroff = tegra_se_nvhost_prepare_poweroff,
|
|
};
|
|
|
|
static struct nvhost_device_data nvhost_se4_info = {
|
|
.clocks = {{"se", 600000000},
|
|
{"emc", UINT_MAX,
|
|
NVHOST_MODULE_ID_EXTERNAL_MEMORY_CONTROLLER,
|
|
0, TEGRA_BWMGR_SET_EMC_FLOOR}, {} },
|
|
.can_powergate = true,
|
|
.autosuspend_delay = 500,
|
|
.class = NV_SE4_CLASS_ID,
|
|
.private_data = &tegra18_se_chipdata,
|
|
.serialize = 1,
|
|
.push_work_done = 1,
|
|
.vm_regs = {{SE_STREAMID_REG_OFFSET, true} },
|
|
.kernel_only = true,
|
|
.bwmgr_client_id = TEGRA_BWMGR_CLIENT_SE4,
|
|
.prepare_poweroff = tegra_se_nvhost_prepare_poweroff,
|
|
};
|
|
|
|
static const struct of_device_id tegra_se_of_match[] = {
|
|
{
|
|
.compatible = "nvidia,tegra186-se1-nvhost",
|
|
.data = &nvhost_se1_info,
|
|
}, {
|
|
.compatible = "nvidia,tegra186-se2-nvhost",
|
|
.data = &nvhost_se2_info,
|
|
}, {
|
|
.compatible = "nvidia,tegra186-se3-nvhost",
|
|
.data = &nvhost_se3_info,
|
|
}, {
|
|
.compatible = "nvidia,tegra186-se4-nvhost",
|
|
.data = &nvhost_se4_info,
|
|
}, {}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, tegra_se_of_match);
|
|
|
|
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 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 nvhost_device_data *pdata = NULL;
|
|
const struct of_device_id *match;
|
|
int err = 0, i = 0;
|
|
unsigned int val;
|
|
struct device_node *node = NULL;
|
|
const char *rsa_name;
|
|
|
|
se_dev = devm_kzalloc(&pdev->dev, sizeof(struct tegra_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;
|
|
}
|
|
pdata = (struct nvhost_device_data *)match->data;
|
|
} else {
|
|
pdata =
|
|
(struct nvhost_device_data *)pdev->id_entry->driver_data;
|
|
}
|
|
|
|
mutex_init(&se_dev->lock);
|
|
crypto_init_queue(&se_dev->queue, TEGRA_SE_CRYPTO_QUEUE_LENGTH);
|
|
|
|
se_dev->dev = &pdev->dev;
|
|
se_dev->pdev = pdev;
|
|
|
|
mutex_init(&pdata->lock);
|
|
pdata->pdev = pdev;
|
|
|
|
/* store chipdata inside se_dev and store se_dev into private_data */
|
|
se_dev->chipdata = pdata->private_data;
|
|
pdata->private_data = se_dev;
|
|
|
|
/* store the pdata into drvdata */
|
|
platform_set_drvdata(pdev, pdata);
|
|
|
|
err = nvhost_client_device_get_resources(pdev);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"nvhost_client_device_get_resources failed for SE(%s)\n",
|
|
pdev->name);
|
|
return err;
|
|
}
|
|
|
|
err = nvhost_module_init(pdev);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"nvhost_module_init failed for SE(%s)\n", pdev->name);
|
|
return err;
|
|
}
|
|
|
|
err = nvhost_client_device_init(pdev);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"nvhost_client_device_init failed for SE(%s)\n",
|
|
pdev->name);
|
|
return err;
|
|
}
|
|
|
|
err = nvhost_channel_map(pdata, &se_dev->channel, pdata);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "Nvhost Channel map failed\n");
|
|
return err;
|
|
}
|
|
|
|
se_dev->io_regs = pdata->aperture[0];
|
|
|
|
node = of_node_get(se_dev->dev->of_node);
|
|
|
|
se_dev->ioc = of_property_read_bool(node, "nvidia,io-coherent");
|
|
|
|
err = of_property_read_u32(node, "opcode_addr", &se_dev->opcode_addr);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "Missing opcode_addr property\n");
|
|
return err;
|
|
}
|
|
|
|
if (!of_property_count_strings(node, "supported-algos"))
|
|
return -ENOTSUPP;
|
|
|
|
tegra_se_fill_se_dev_info(se_dev);
|
|
|
|
if (is_algo_supported(node, "aes") || is_algo_supported(node, "drbg")) {
|
|
err = tegra_init_key_slot(se_dev);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "init_key_slot failed\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
if (is_algo_supported(node, "rsa")) {
|
|
err = tegra_init_rsa_key_slot(se_dev);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "init_rsa_key_slot failed\n");
|
|
return err;
|
|
}
|
|
}
|
|
|
|
mutex_init(&se_dev->mtx);
|
|
INIT_WORK(&se_dev->se_work, tegra_se_work_handler);
|
|
se_dev->se_work_q = alloc_workqueue("se_work_q",
|
|
WQ_HIGHPRI | WQ_UNBOUND, 1);
|
|
if (!se_dev->se_work_q) {
|
|
dev_err(se_dev->dev, "alloc_workqueue failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
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 ll_alloc_fail;
|
|
}
|
|
|
|
if (is_algo_supported(node, "drbg") || is_algo_supported(node, "aes") ||
|
|
is_algo_supported(node, "cmac")) {
|
|
se_dev->aes_cmdbuf_cpuvaddr =
|
|
dma_alloc_attrs(se_dev->dev->parent,
|
|
SZ_16K * SE_MAX_SUBMIT_CHAIN_SZ,
|
|
&se_dev->aes_cmdbuf_iova, GFP_KERNEL,
|
|
__DMA_ATTR(attrs));
|
|
|
|
if (!se_dev->aes_cmdbuf_cpuvaddr)
|
|
goto ll_alloc_fail;
|
|
|
|
err = tegra_se_init_cmdbuf_addr(se_dev);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "failed to init cmdbuf addr\n");
|
|
goto dma_free;
|
|
}
|
|
}
|
|
|
|
se_dev->aes_src_ll =
|
|
devm_kzalloc(&pdev->dev, sizeof(struct tegra_se_ll),
|
|
GFP_KERNEL);
|
|
se_dev->aes_dst_ll =
|
|
devm_kzalloc(&pdev->dev, sizeof(struct tegra_se_ll),
|
|
GFP_KERNEL);
|
|
if (!se_dev->aes_src_ll || !se_dev->aes_dst_ll) {
|
|
dev_err(se_dev->dev, "Linked list memory allocation failed\n");
|
|
goto aes_ll_buf_alloc_fail;
|
|
}
|
|
|
|
if (se_dev->ioc)
|
|
se_dev->total_aes_buf = dma_alloc_coherent(
|
|
se_dev->dev, SE_MAX_MEM_ALLOC,
|
|
&se_dev->total_aes_buf_addr,
|
|
GFP_KERNEL);
|
|
else
|
|
se_dev->total_aes_buf = kzalloc(SE_MAX_MEM_ALLOC, GFP_KERNEL);
|
|
|
|
if (!se_dev->total_aes_buf) {
|
|
err = -ENOMEM;
|
|
goto aes_buf_alloc_fail;
|
|
}
|
|
|
|
tegra_se_init_aesbuf(se_dev);
|
|
|
|
se_dev->syncpt_id = nvhost_get_syncpt_host_managed(se_dev->pdev,
|
|
0, pdev->name);
|
|
if (!se_dev->syncpt_id) {
|
|
err = -EINVAL;
|
|
dev_err(se_dev->dev, "Cannot get syncpt_id for SE(%s)\n",
|
|
pdev->name);
|
|
goto reg_fail;
|
|
}
|
|
|
|
/* Algo registrations */
|
|
if (is_algo_supported(node, "drbg")) {
|
|
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 reg_fail;
|
|
}
|
|
}
|
|
|
|
if (is_algo_supported(node, "xts")) {
|
|
INIT_LIST_HEAD(&aes_algs[0].cra_list);
|
|
err = crypto_register_alg(&aes_algs[0]);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"crypto_register_alg xts failed\n");
|
|
goto reg_fail;
|
|
}
|
|
}
|
|
if (is_algo_supported(node, "aes")) {
|
|
|
|
for (i = 1; 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 %s failed\n",
|
|
aes_algs[i].cra_name);
|
|
goto reg_fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_algo_supported(node, "cmac")) {
|
|
err = crypto_register_ahash(&hash_algs[0]);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"crypto_register_ahash cmac failed\n");
|
|
goto reg_fail;
|
|
}
|
|
}
|
|
|
|
if (is_algo_supported(node, "sha")) {
|
|
for (i = 1; i < 6; i++) {
|
|
err = crypto_register_ahash(&hash_algs[i]);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"crypto_register_ahash %s failed\n",
|
|
hash_algs[i].halg.base.cra_name);
|
|
goto reg_fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
strncpy(rsa_alg.base.cra_name, rsa_name,
|
|
sizeof(rsa_alg.base.cra_name) - 1);
|
|
|
|
if (is_algo_supported(node, "rsa")) {
|
|
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 reg_fail;
|
|
|
|
err = crypto_register_akcipher(&rsa_alg);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "crypto_register_akcipher fail");
|
|
goto reg_fail;
|
|
}
|
|
err = crypto_register_kpp(&dh_algs[0]);
|
|
if (err) {
|
|
dev_err(se_dev->dev, "crypto_register_kpp fail");
|
|
goto reg_fail;
|
|
}
|
|
}
|
|
|
|
if (is_algo_supported(node, "drbg")) {
|
|
/* Make sure engine is powered ON with clk enabled */
|
|
err = nvhost_module_busy(pdev);
|
|
if (err) {
|
|
dev_err(se_dev->dev,
|
|
"nvhost_module_busy failed for se_dev\n");
|
|
goto reg_fail;
|
|
}
|
|
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);
|
|
/* Power OFF after SE register update */
|
|
nvhost_module_idle(pdev);
|
|
}
|
|
|
|
tegra_se_boost_cpu_init(se_dev);
|
|
|
|
dev_info(se_dev->dev, "%s: complete", __func__);
|
|
|
|
return 0;
|
|
reg_fail:
|
|
nvhost_syncpt_put_ref_ext(se_dev->pdev, se_dev->syncpt_id);
|
|
aes_buf_alloc_fail:
|
|
kfree(se_dev->total_aes_buf);
|
|
aes_ll_buf_alloc_fail:
|
|
kfree(se_dev->aes_src_ll);
|
|
kfree(se_dev->aes_dst_ll);
|
|
dma_free:
|
|
dma_free_attrs(se_dev->dev->parent, SZ_16K * SE_MAX_SUBMIT_CHAIN_SZ,
|
|
se_dev->aes_cmdbuf_cpuvaddr, se_dev->aes_cmdbuf_iova,
|
|
__DMA_ATTR(attrs));
|
|
ll_alloc_fail:
|
|
if (se_dev->se_work_q)
|
|
destroy_workqueue(se_dev->se_work_q);
|
|
tegra_se_free_ll_buf(se_dev);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int tegra_se_remove(struct platform_device *pdev)
|
|
{
|
|
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
|
|
struct tegra_se_dev *se_dev = pdata->private_data;
|
|
struct device_node *node;
|
|
int i;
|
|
|
|
if (!se_dev) {
|
|
pr_err("Device is NULL\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
tegra_se_boost_cpu_deinit(se_dev);
|
|
|
|
if (se_dev->aes_cmdbuf_cpuvaddr)
|
|
dma_free_attrs(
|
|
se_dev->dev->parent, SZ_16K * SE_MAX_SUBMIT_CHAIN_SZ,
|
|
se_dev->aes_cmdbuf_cpuvaddr, se_dev->aes_cmdbuf_iova,
|
|
__DMA_ATTR(attrs));
|
|
|
|
node = of_node_get(se_dev->dev->of_node);
|
|
if (is_algo_supported(node, "drbg"))
|
|
crypto_unregister_rng(&rng_algs[0]);
|
|
|
|
if (is_algo_supported(node, "xts"))
|
|
crypto_unregister_alg(&aes_algs[0]);
|
|
|
|
if (is_algo_supported(node, "aes")) {
|
|
crypto_unregister_alg(&aes_algs[1]);
|
|
for (i = 2; i < ARRAY_SIZE(aes_algs); i++)
|
|
crypto_unregister_alg(&aes_algs[i]);
|
|
}
|
|
|
|
if (is_algo_supported(node, "cmac"))
|
|
crypto_unregister_ahash(&hash_algs[0]);
|
|
|
|
if (is_algo_supported(node, "sha")) {
|
|
for (i = 1; i < 6; i++)
|
|
crypto_unregister_ahash(&hash_algs[i]);
|
|
}
|
|
|
|
if (is_algo_supported(node, "rsa")) {
|
|
crypto_unregister_akcipher(&rsa_alg);
|
|
crypto_unregister_kpp(&dh_algs[0]);
|
|
}
|
|
|
|
tegra_se_free_ll_buf(se_dev);
|
|
kfree(se_dev->total_aes_buf);
|
|
|
|
cancel_work_sync(&se_dev->se_work);
|
|
if (se_dev->se_work_q)
|
|
destroy_workqueue(se_dev->se_work_q);
|
|
|
|
mutex_destroy(&se_dev->mtx);
|
|
nvhost_client_device_release(pdev);
|
|
mutex_destroy(&pdata->lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver tegra_se_driver = {
|
|
.probe = tegra_se_probe,
|
|
.remove = tegra_se_remove,
|
|
.driver = {
|
|
.name = "tegra-se-nvhost",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(tegra_se_of_match),
|
|
.pm = &nvhost_module_pm_ops,
|
|
.suppress_bind_attrs = true,
|
|
},
|
|
};
|
|
|
|
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 using Host1x Interface");
|
|
MODULE_AUTHOR("NVIDIA Corporation");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("tegra-se-nvhost");
|