summaryrefslogtreecommitdiff
path: root/drivers/misc
diff options
context:
space:
mode:
authorVarun Wadekar <vwadekar@nvidia.com>2011-02-11 02:02:14 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:43:03 -0800
commit41e361ff01e39494a8b4dce6664a428b22f1dfc3 (patch)
treefae8d1193decd53baa148f7f5027f5f0378ebf32 /drivers/misc
parent8051399a2fe395ef7a6344d242dff13103a3b993 (diff)
misc: tegra-cryptodev: device node to access tegra aes hw
/dev/tegra-crypto node to access tegra aes hw from the user space. currently ecb/cbc/ansi_x9.31 modes are supported Original-Change-Id: I8d9e48d4b139e9c2e26a885773fb2f792fb6ca87 Signed-off-by: Varun Wadekar <vwadekar@nvidia.com> Reviewed-on: http://git-master/r/16046 Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com> Rebase-Id: R1e3e1f059255adb13d39c4332f6cdfb3f3c8c3f0
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig6
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/tegra-cryptodev.c301
-rw-r--r--drivers/misc/tegra-cryptodev.h70
4 files changed, 378 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 40b75d076ae1..9126675fc048 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -514,6 +514,12 @@ config BCM4329_RFKILL
---help---
Adds BCM4329 RFKILL driver for Broadcom BCM4329 chipset
+config TEGRA_CRYPTO_DEV
+ bool "Device node to access tegra aes hardware"
+ ---help---
+ Dev node /dev/tegra-crypto in order to get access to tegra aes
+ hardware from user space
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index fe2e911a0621..b717b7fe7c3f 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -49,3 +49,4 @@ obj-y += carma/
obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_SENSORS_NCT1008) += nct1008.o
obj-$(CONFIG_BCM4329_RFKILL) += bcm4329_rfkill.o
+obj-$(CONFIG_TEGRA_CRYPTO_DEV) += tegra-cryptodev.o
diff --git a/drivers/misc/tegra-cryptodev.c b/drivers/misc/tegra-cryptodev.c
new file mode 100644
index 000000000000..a18f59e858fe
--- /dev/null
+++ b/drivers/misc/tegra-cryptodev.c
@@ -0,0 +1,301 @@
+/*
+ * drivers/misc/tegra-cryptodev.c
+ *
+ * crypto dev node for NVIDIA tegra aes hardware
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/uaccess.h>
+#include <crypto/rng.h>
+
+#include "tegra-cryptodev.h"
+
+#define NBUFS 2
+
+struct tegra_crypto_ctx {
+ struct crypto_ablkcipher *ecb_tfm;
+ struct crypto_ablkcipher *cbc_tfm;
+ struct crypto_rng *rng;
+ u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
+ int use_ssk;
+};
+
+static int alloc_bufs(unsigned long *buf[NBUFS])
+{
+ int i;
+
+ for (i = 0; i < NBUFS; i++) {
+ buf[i] = (void *)__get_free_page(GFP_KERNEL);
+ if (!buf[i])
+ goto err_free_buf;
+ }
+
+ return 0;
+
+err_free_buf:
+ while (i-- > 0)
+ free_page((unsigned long)buf[i]);
+
+ return -ENOMEM;
+}
+
+static void free_bufs(unsigned long *buf[NBUFS])
+{
+ int i;
+
+ for (i = 0; i < NBUFS; i++)
+ free_page((unsigned long)buf[i]);
+}
+
+static int tegra_crypto_dev_open(struct inode *inode, struct file *filp)
+{
+ struct tegra_crypto_ctx *ctx;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(struct tegra_crypto_ctx), GFP_KERNEL);
+ if (!ctx) {
+ pr_err("no memory for context\n");
+ return -ENOMEM;
+ }
+
+ ctx->ecb_tfm = crypto_alloc_ablkcipher("ecb-aes-tegra",
+ CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
+ if (IS_ERR(ctx->ecb_tfm)) {
+ pr_err("Failed to load transform for ecb-aes-tegra: %ld\n",
+ PTR_ERR(ctx->ecb_tfm));
+ ret = PTR_ERR(ctx->ecb_tfm);
+ goto fail_ecb;
+ }
+
+ ctx->cbc_tfm = crypto_alloc_ablkcipher("cbc-aes-tegra",
+ CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC, 0);
+ if (IS_ERR(ctx->cbc_tfm)) {
+ pr_err("Failed to load transform for cbc-aes-tegra: %ld\n",
+ PTR_ERR(ctx->cbc_tfm));
+ ret = PTR_ERR(ctx->cbc_tfm);
+ goto fail_cbc;
+ }
+
+ ctx->rng = crypto_alloc_rng("rng-aes-tegra", CRYPTO_ALG_TYPE_RNG, 0);
+ if (IS_ERR(ctx->rng)) {
+ pr_err("Failed to load transform for tegra rng: %ld\n",
+ PTR_ERR(ctx->rng));
+ ret = PTR_ERR(ctx->rng);
+ goto fail_rng;
+ }
+
+ filp->private_data = ctx;
+ return ret;
+
+fail_rng:
+ crypto_free_ablkcipher(ctx->cbc_tfm);
+
+fail_cbc:
+ crypto_free_ablkcipher(ctx->ecb_tfm);
+
+fail_ecb:
+ kfree(ctx);
+ return ret;
+}
+
+static int tegra_crypto_dev_release(struct inode *inode, struct file *filp)
+{
+ struct tegra_crypto_ctx *ctx = filp->private_data;
+
+ crypto_free_ablkcipher(ctx->ecb_tfm);
+ crypto_free_ablkcipher(ctx->cbc_tfm);
+ crypto_free_rng(ctx->rng);
+ kfree(ctx);
+ filp->private_data = NULL;
+ return 0;
+}
+
+static int process_crypt_req(struct tegra_crypto_ctx *ctx, struct tegra_crypt_req *crypt_req)
+{
+ struct crypto_ablkcipher *tfm;
+ struct ablkcipher_request *req = NULL;
+ struct scatterlist in_sg;
+ struct scatterlist out_sg;
+ unsigned long *xbuf[NBUFS];
+ int ret = 0, size = 0;
+ unsigned long total = 0;
+
+ if (crypt_req->op & TEGRA_CRYPTO_ECB) {
+ req = ablkcipher_request_alloc(ctx->ecb_tfm, GFP_KERNEL);
+ tfm = ctx->ecb_tfm;
+ } else {
+ req = ablkcipher_request_alloc(ctx->cbc_tfm, GFP_KERNEL);
+ tfm = ctx->cbc_tfm;
+ }
+ if (!req) {
+ pr_err("%s: Failed to allocate request\n", __func__);
+ return -ENOMEM;
+ }
+
+ if ((crypt_req->keylen < 0) || (crypt_req->keylen > AES_MAX_KEY_SIZE))
+ return -EINVAL;
+
+ crypto_ablkcipher_clear_flags(tfm, ~0);
+
+ if (!ctx->use_ssk) {
+ ret = crypto_ablkcipher_setkey(tfm, crypt_req->key,
+ crypt_req->keylen);
+ if (ret < 0) {
+ pr_err("setkey failed");
+ goto process_req_out;
+ }
+ }
+
+ ret = alloc_bufs(xbuf);
+ if (ret < 0) {
+ pr_err("alloc_bufs failed");
+ goto process_req_out;
+ }
+
+ total = crypt_req->plaintext_sz;
+ while (total > 0) {
+ size = min(total, PAGE_SIZE);
+ ret = copy_from_user((void *)xbuf[0],
+ (void __user *)crypt_req->plaintext, size);
+ if (ret < 0) {
+ pr_debug("%s: copy_from_user failed (%d)\n", __func__, ret);
+ goto process_req_buf_out;
+ }
+ sg_init_one(&in_sg, xbuf[0], size);
+ sg_init_one(&out_sg, xbuf[1], size);
+ ablkcipher_request_set_crypt(req, &in_sg,
+ &out_sg, size, crypt_req->iv);
+ req->base.complete = NULL;
+ ret = crypt_req->encrypt ?
+ crypto_ablkcipher_encrypt(req) :
+ crypto_ablkcipher_decrypt(req);
+ if (ret < 0) {
+ pr_debug("%scrypt failed (%d)\n",
+ crypt_req->encrypt ? "en" : "de", ret);
+ goto process_req_buf_out;
+ }
+
+ ret = copy_to_user((void __user *)crypt_req->result,
+ (const void *)xbuf[1], size);
+ if (ret < 0)
+ goto process_req_buf_out;
+
+ total -= size;
+ crypt_req->result += size;
+ crypt_req->plaintext += size;
+ }
+
+process_req_buf_out:
+ free_bufs(xbuf);
+process_req_out:
+ ablkcipher_request_free(req);
+
+ return ret;
+}
+
+static long tegra_crypto_dev_ioctl(struct file *filp,
+ unsigned int ioctl_num, unsigned long arg)
+{
+ struct tegra_crypto_ctx *ctx = filp->private_data;
+ struct tegra_crypt_req crypt_req;
+ struct tegra_rng_req rng_req;
+ char rng[TEGRA_CRYPTO_RNG_SIZE];
+ int ret = 0;
+
+ switch (ioctl_num) {
+ case TEGRA_CRYPTO_IOCTL_NEED_SSK:
+ ctx->use_ssk = (int)arg;
+ break;
+ case TEGRA_CRYPTO_IOCTL_PROCESS_REQ:
+ ret = copy_from_user(&crypt_req, (void __user *)arg, sizeof(crypt_req));
+ if (ret < 0) {
+ pr_debug("%s: copy_from_user fail(%d)\n", __func__, ret);
+ break;
+ }
+
+ ret = process_crypt_req(ctx, &crypt_req);
+ break;
+
+ case TEGRA_CRYPTO_IOCTL_SET_SEED:
+ if (copy_from_user(&rng_req, (void __user *)arg, sizeof(rng_req)))
+ return -EFAULT;
+
+ memcpy(ctx->seed, rng_req.seed, TEGRA_CRYPTO_RNG_SEED_SIZE);
+
+ ret = crypto_rng_reset(ctx->rng, ctx->seed,
+ crypto_rng_seedsize(ctx->rng));
+ break;
+ case TEGRA_CRYPTO_IOCTL_GET_RANDOM:
+ if (copy_from_user(&rng_req, (void __user *)arg, sizeof(rng_req)))
+ return -EFAULT;
+
+ ret = crypto_rng_get_bytes(ctx->rng, rng,
+ rng_req.nbytes);
+
+ if (ret != rng_req.nbytes) {
+ pr_debug("rng failed");
+ ret = -ENODATA;
+ goto rng_out;
+ }
+
+ ret = copy_to_user((void __user *)rng_req.rdata,
+ (const void *)rng, rng_req.nbytes);
+ ret = (ret < 0) ? -ENODATA : 0;
+rng_out:
+ break;
+
+
+ default:
+ pr_debug("invalid ioctl code(%d)", ioctl_num);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+struct file_operations tegra_crypto_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_crypto_dev_open,
+ .release = tegra_crypto_dev_release,
+ .unlocked_ioctl = tegra_crypto_dev_ioctl,
+};
+
+struct miscdevice tegra_crypto_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "tegra-crypto",
+ .fops = &tegra_crypto_fops,
+};
+
+static int __init tegra_crypto_dev_init(void)
+{
+ return misc_register(&tegra_crypto_device);
+}
+
+late_initcall(tegra_crypto_dev_init);
+
+MODULE_DESCRIPTION("Tegra AES hw device node.");
+MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_LICENSE("GPLv2");
diff --git a/drivers/misc/tegra-cryptodev.h b/drivers/misc/tegra-cryptodev.h
new file mode 100644
index 000000000000..ed62a52eca03
--- /dev/null
+++ b/drivers/misc/tegra-cryptodev.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __TEGRA_CRYPTODEV_H
+#define __TEGRA_CRYPTODEV_H
+
+#include <crypto/aes.h>
+
+#include <asm-generic/ioctl.h>
+
+/* ioctl arg = 1 if you want to use ssk. arg = 0 to use normal key */
+#define TEGRA_CRYPTO_IOCTL_NEED_SSK _IOWR(0x98, 100, int)
+#define TEGRA_CRYPTO_IOCTL_PROCESS_REQ _IOWR(0x98, 101, int*)
+#define TEGRA_CRYPTO_IOCTL_SET_SEED _IOWR(0x98, 102, int*)
+#define TEGRA_CRYPTO_IOCTL_GET_RANDOM _IOWR(0x98, 103, int*)
+
+#define TEGRA_CRYPTO_MAX_KEY_SIZE AES_MAX_KEY_SIZE
+#define TEGRA_CRYPTO_IV_SIZE AES_BLOCK_SIZE
+#define DEFAULT_RNG_BLK_SZ 16
+
+/* the seed consists of 16 bytes of key + 16 bytes of init vector */
+#define TEGRA_CRYPTO_RNG_SEED_SIZE AES_KEYSIZE_128 + DEFAULT_RNG_BLK_SZ
+#define TEGRA_CRYPTO_RNG_SIZE SZ_16
+
+/* encrypt/decrypt operations */
+#define TEGRA_CRYPTO_ECB BIT(0)
+#define TEGRA_CRYPTO_CBC BIT(1)
+#define TEGRA_CRYPTO_RNG BIT(2)
+
+/* a pointer to this struct needs to be passed to:
+ * TEGRA_CRYPTO_IOCTL_PROCESS_REQ
+ */
+struct tegra_crypt_req {
+ int op; /* e.g. TEGRA_CRYPTO_ECB */
+ bool encrypt;
+ char key[TEGRA_CRYPTO_MAX_KEY_SIZE];
+ int keylen;
+ char iv[TEGRA_CRYPTO_IV_SIZE];
+ int ivlen;
+ u8 *plaintext;
+ int plaintext_sz;
+ u8 *result;
+};
+
+/* pointer to this struct should be passed to:
+ * TEGRA_CRYPTO_IOCTL_SET_SEED
+ * TEGRA_CRYPTO_IOCTL_GET_RANDOM
+ */
+struct tegra_rng_req {
+ u8 seed[TEGRA_CRYPTO_RNG_SEED_SIZE];
+ u8 *rdata; /* random generated data */
+ int nbytes; /* random data length */
+};
+
+#endif