123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912 |
- /*
- * Cryptographic API.
- *
- * Support for DCP cryptographic accelerator.
- *
- * Copyright (c) 2013
- * Author: Tobias Rauter <tobias.rauter@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * Based on tegra-aes.c, dcp.c (from freescale SDK) and sahara.c
- */
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/platform_device.h>
- #include <linux/dma-mapping.h>
- #include <linux/io.h>
- #include <linux/mutex.h>
- #include <linux/interrupt.h>
- #include <linux/completion.h>
- #include <linux/workqueue.h>
- #include <linux/delay.h>
- #include <linux/crypto.h>
- #include <linux/miscdevice.h>
- #include <crypto/scatterwalk.h>
- #include <crypto/aes.h>
- /* IOCTL for DCP OTP Key AES - taken from Freescale's SDK*/
- #define DBS_IOCTL_BASE 'd'
- #define DBS_ENC _IOW(DBS_IOCTL_BASE, 0x00, uint8_t[16])
- #define DBS_DEC _IOW(DBS_IOCTL_BASE, 0x01, uint8_t[16])
- /* DCP channel used for AES */
- #define USED_CHANNEL 1
- /* Ring Buffers' maximum size */
- #define DCP_MAX_PKG 20
- /* Control Register */
- #define DCP_REG_CTRL 0x000
- #define DCP_CTRL_SFRST (1<<31)
- #define DCP_CTRL_CLKGATE (1<<30)
- #define DCP_CTRL_CRYPTO_PRESENT (1<<29)
- #define DCP_CTRL_SHA_PRESENT (1<<28)
- #define DCP_CTRL_GATHER_RES_WRITE (1<<23)
- #define DCP_CTRL_ENABLE_CONTEXT_CACHE (1<<22)
- #define DCP_CTRL_ENABLE_CONTEXT_SWITCH (1<<21)
- #define DCP_CTRL_CH_IRQ_E_0 0x01
- #define DCP_CTRL_CH_IRQ_E_1 0x02
- #define DCP_CTRL_CH_IRQ_E_2 0x04
- #define DCP_CTRL_CH_IRQ_E_3 0x08
- /* Status register */
- #define DCP_REG_STAT 0x010
- #define DCP_STAT_OTP_KEY_READY (1<<28)
- #define DCP_STAT_CUR_CHANNEL(stat) ((stat>>24)&0x0F)
- #define DCP_STAT_READY_CHANNEL(stat) ((stat>>16)&0x0F)
- #define DCP_STAT_IRQ(stat) (stat&0x0F)
- #define DCP_STAT_CHAN_0 (0x01)
- #define DCP_STAT_CHAN_1 (0x02)
- #define DCP_STAT_CHAN_2 (0x04)
- #define DCP_STAT_CHAN_3 (0x08)
- /* Channel Control Register */
- #define DCP_REG_CHAN_CTRL 0x020
- #define DCP_CHAN_CTRL_CH0_IRQ_MERGED (1<<16)
- #define DCP_CHAN_CTRL_HIGH_PRIO_0 (0x0100)
- #define DCP_CHAN_CTRL_HIGH_PRIO_1 (0x0200)
- #define DCP_CHAN_CTRL_HIGH_PRIO_2 (0x0400)
- #define DCP_CHAN_CTRL_HIGH_PRIO_3 (0x0800)
- #define DCP_CHAN_CTRL_ENABLE_0 (0x01)
- #define DCP_CHAN_CTRL_ENABLE_1 (0x02)
- #define DCP_CHAN_CTRL_ENABLE_2 (0x04)
- #define DCP_CHAN_CTRL_ENABLE_3 (0x08)
- /*
- * Channel Registers:
- * The DCP has 4 channels. Each of this channels
- * has 4 registers (command pointer, semaphore, status and options).
- * The address of register REG of channel CHAN is obtained by
- * dcp_chan_reg(REG, CHAN)
- */
- #define DCP_REG_CHAN_PTR 0x00000100
- #define DCP_REG_CHAN_SEMA 0x00000110
- #define DCP_REG_CHAN_STAT 0x00000120
- #define DCP_REG_CHAN_OPT 0x00000130
- #define DCP_CHAN_STAT_NEXT_CHAIN_IS_0 0x010000
- #define DCP_CHAN_STAT_NO_CHAIN 0x020000
- #define DCP_CHAN_STAT_CONTEXT_ERROR 0x030000
- #define DCP_CHAN_STAT_PAYLOAD_ERROR 0x040000
- #define DCP_CHAN_STAT_INVALID_MODE 0x050000
- #define DCP_CHAN_STAT_PAGEFAULT 0x40
- #define DCP_CHAN_STAT_DST 0x20
- #define DCP_CHAN_STAT_SRC 0x10
- #define DCP_CHAN_STAT_PACKET 0x08
- #define DCP_CHAN_STAT_SETUP 0x04
- #define DCP_CHAN_STAT_MISMATCH 0x02
- /* hw packet control*/
- #define DCP_PKT_PAYLOAD_KEY (1<<11)
- #define DCP_PKT_OTP_KEY (1<<10)
- #define DCP_PKT_CIPHER_INIT (1<<9)
- #define DCP_PKG_CIPHER_ENCRYPT (1<<8)
- #define DCP_PKT_CIPHER_ENABLE (1<<5)
- #define DCP_PKT_DECR_SEM (1<<1)
- #define DCP_PKT_CHAIN (1<<2)
- #define DCP_PKT_IRQ 1
- #define DCP_PKT_MODE_CBC (1<<4)
- #define DCP_PKT_KEYSELECT_OTP (0xFF<<8)
- /* cipher flags */
- #define DCP_ENC 0x0001
- #define DCP_DEC 0x0002
- #define DCP_ECB 0x0004
- #define DCP_CBC 0x0008
- #define DCP_CBC_INIT 0x0010
- #define DCP_NEW_KEY 0x0040
- #define DCP_OTP_KEY 0x0080
- #define DCP_AES 0x1000
- /* DCP Flags */
- #define DCP_FLAG_BUSY 0x01
- #define DCP_FLAG_PRODUCING 0x02
- /* clock defines */
- #define CLOCK_ON 1
- #define CLOCK_OFF 0
- struct dcp_dev_req_ctx {
- int mode;
- };
- struct dcp_op {
- unsigned int flags;
- u8 key[AES_KEYSIZE_128];
- int keylen;
- struct ablkcipher_request *req;
- struct crypto_ablkcipher *fallback;
- uint32_t stat;
- uint32_t pkt1;
- uint32_t pkt2;
- struct ablkcipher_walk walk;
- };
- struct dcp_dev {
- struct device *dev;
- void __iomem *dcp_regs_base;
- int dcp_vmi_irq;
- int dcp_irq;
- spinlock_t queue_lock;
- struct crypto_queue queue;
- uint32_t pkt_produced;
- uint32_t pkt_consumed;
- struct dcp_hw_packet *hw_pkg[DCP_MAX_PKG];
- dma_addr_t hw_phys_pkg;
- /* [KEY][IV] Both with 16 Bytes */
- u8 *payload_base;
- dma_addr_t payload_base_dma;
- struct tasklet_struct done_task;
- struct tasklet_struct queue_task;
- struct timer_list watchdog;
- unsigned long flags;
- struct dcp_op *ctx;
- struct miscdevice dcp_bootstream_misc;
- };
- struct dcp_hw_packet {
- uint32_t next;
- uint32_t pkt1;
- uint32_t pkt2;
- uint32_t src;
- uint32_t dst;
- uint32_t size;
- uint32_t payload;
- uint32_t stat;
- };
- static struct dcp_dev *global_dev;
- static inline u32 dcp_chan_reg(u32 reg, int chan)
- {
- return reg + (chan) * 0x40;
- }
- static inline void dcp_write(struct dcp_dev *dev, u32 data, u32 reg)
- {
- writel(data, dev->dcp_regs_base + reg);
- }
- static inline void dcp_set(struct dcp_dev *dev, u32 data, u32 reg)
- {
- writel(data, dev->dcp_regs_base + (reg | 0x04));
- }
- static inline void dcp_clear(struct dcp_dev *dev, u32 data, u32 reg)
- {
- writel(data, dev->dcp_regs_base + (reg | 0x08));
- }
- static inline void dcp_toggle(struct dcp_dev *dev, u32 data, u32 reg)
- {
- writel(data, dev->dcp_regs_base + (reg | 0x0C));
- }
- static inline unsigned int dcp_read(struct dcp_dev *dev, u32 reg)
- {
- return readl(dev->dcp_regs_base + reg);
- }
- static void dcp_dma_unmap(struct dcp_dev *dev, struct dcp_hw_packet *pkt)
- {
- dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE);
- dma_unmap_page(dev->dev, pkt->dst, pkt->size, DMA_FROM_DEVICE);
- dev_dbg(dev->dev, "unmap packet %x", (unsigned int) pkt);
- }
- static int dcp_dma_map(struct dcp_dev *dev,
- struct ablkcipher_walk *walk, struct dcp_hw_packet *pkt)
- {
- dev_dbg(dev->dev, "map packet %x", (unsigned int) pkt);
- /* align to length = 16 */
- pkt->size = walk->nbytes - (walk->nbytes % 16);
- pkt->src = dma_map_page(dev->dev, walk->src.page, walk->src.offset,
- pkt->size, DMA_TO_DEVICE);
- if (pkt->src == 0) {
- dev_err(dev->dev, "Unable to map src");
- return -ENOMEM;
- }
- pkt->dst = dma_map_page(dev->dev, walk->dst.page, walk->dst.offset,
- pkt->size, DMA_FROM_DEVICE);
- if (pkt->dst == 0) {
- dev_err(dev->dev, "Unable to map dst");
- dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE);
- return -ENOMEM;
- }
- return 0;
- }
- static void dcp_op_one(struct dcp_dev *dev, struct dcp_hw_packet *pkt,
- uint8_t last)
- {
- struct dcp_op *ctx = dev->ctx;
- pkt->pkt1 = ctx->pkt1;
- pkt->pkt2 = ctx->pkt2;
- pkt->payload = (u32) dev->payload_base_dma;
- pkt->stat = 0;
- if (ctx->flags & DCP_CBC_INIT) {
- pkt->pkt1 |= DCP_PKT_CIPHER_INIT;
- ctx->flags &= ~DCP_CBC_INIT;
- }
- mod_timer(&dev->watchdog, jiffies + msecs_to_jiffies(500));
- pkt->pkt1 |= DCP_PKT_IRQ;
- if (!last)
- pkt->pkt1 |= DCP_PKT_CHAIN;
- dev->pkt_produced++;
- dcp_write(dev, 1,
- dcp_chan_reg(DCP_REG_CHAN_SEMA, USED_CHANNEL));
- }
- static void dcp_op_proceed(struct dcp_dev *dev)
- {
- struct dcp_op *ctx = dev->ctx;
- struct dcp_hw_packet *pkt;
- while (ctx->walk.nbytes) {
- int err = 0;
- pkt = dev->hw_pkg[dev->pkt_produced % DCP_MAX_PKG];
- err = dcp_dma_map(dev, &ctx->walk, pkt);
- if (err) {
- dev->ctx->stat |= err;
- /* start timer to wait for already set up calls */
- mod_timer(&dev->watchdog,
- jiffies + msecs_to_jiffies(500));
- break;
- }
- err = ctx->walk.nbytes - pkt->size;
- ablkcipher_walk_done(dev->ctx->req, &dev->ctx->walk, err);
- dcp_op_one(dev, pkt, ctx->walk.nbytes == 0);
- /* we have to wait if no space is left in buffer */
- if (dev->pkt_produced - dev->pkt_consumed == DCP_MAX_PKG)
- break;
- }
- clear_bit(DCP_FLAG_PRODUCING, &dev->flags);
- }
- static void dcp_op_start(struct dcp_dev *dev, uint8_t use_walk)
- {
- struct dcp_op *ctx = dev->ctx;
- if (ctx->flags & DCP_NEW_KEY) {
- memcpy(dev->payload_base, ctx->key, ctx->keylen);
- ctx->flags &= ~DCP_NEW_KEY;
- }
- ctx->pkt1 = 0;
- ctx->pkt1 |= DCP_PKT_CIPHER_ENABLE;
- ctx->pkt1 |= DCP_PKT_DECR_SEM;
- if (ctx->flags & DCP_OTP_KEY)
- ctx->pkt1 |= DCP_PKT_OTP_KEY;
- else
- ctx->pkt1 |= DCP_PKT_PAYLOAD_KEY;
- if (ctx->flags & DCP_ENC)
- ctx->pkt1 |= DCP_PKG_CIPHER_ENCRYPT;
- ctx->pkt2 = 0;
- if (ctx->flags & DCP_CBC)
- ctx->pkt2 |= DCP_PKT_MODE_CBC;
- dev->pkt_produced = 0;
- dev->pkt_consumed = 0;
- ctx->stat = 0;
- dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
- dcp_write(dev, (u32) dev->hw_phys_pkg,
- dcp_chan_reg(DCP_REG_CHAN_PTR, USED_CHANNEL));
- set_bit(DCP_FLAG_PRODUCING, &dev->flags);
- if (use_walk) {
- ablkcipher_walk_init(&ctx->walk, ctx->req->dst,
- ctx->req->src, ctx->req->nbytes);
- ablkcipher_walk_phys(ctx->req, &ctx->walk);
- dcp_op_proceed(dev);
- } else {
- dcp_op_one(dev, dev->hw_pkg[0], 1);
- clear_bit(DCP_FLAG_PRODUCING, &dev->flags);
- }
- }
- static void dcp_done_task(unsigned long data)
- {
- struct dcp_dev *dev = (struct dcp_dev *)data;
- struct dcp_hw_packet *last_packet;
- int fin;
- fin = 0;
- for (last_packet = dev->hw_pkg[(dev->pkt_consumed) % DCP_MAX_PKG];
- last_packet->stat == 1;
- last_packet =
- dev->hw_pkg[++(dev->pkt_consumed) % DCP_MAX_PKG]) {
- dcp_dma_unmap(dev, last_packet);
- last_packet->stat = 0;
- fin++;
- }
- /* the last call of this function already consumed this IRQ's packet */
- if (fin == 0)
- return;
- dev_dbg(dev->dev,
- "Packet(s) done with status %x; finished: %d, produced:%d, complete consumed: %d",
- dev->ctx->stat, fin, dev->pkt_produced, dev->pkt_consumed);
- last_packet = dev->hw_pkg[(dev->pkt_consumed - 1) % DCP_MAX_PKG];
- if (!dev->ctx->stat && last_packet->pkt1 & DCP_PKT_CHAIN) {
- if (!test_and_set_bit(DCP_FLAG_PRODUCING, &dev->flags))
- dcp_op_proceed(dev);
- return;
- }
- while (unlikely(dev->pkt_consumed < dev->pkt_produced)) {
- dcp_dma_unmap(dev,
- dev->hw_pkg[dev->pkt_consumed++ % DCP_MAX_PKG]);
- }
- if (dev->ctx->flags & DCP_OTP_KEY) {
- /* we used the miscdevice, no walk to finish */
- clear_bit(DCP_FLAG_BUSY, &dev->flags);
- return;
- }
- ablkcipher_walk_complete(&dev->ctx->walk);
- dev->ctx->req->base.complete(&dev->ctx->req->base,
- dev->ctx->stat);
- dev->ctx->req = NULL;
- /* in case there are other requests in the queue */
- tasklet_schedule(&dev->queue_task);
- }
- static void dcp_watchdog(unsigned long data)
- {
- struct dcp_dev *dev = (struct dcp_dev *)data;
- dev->ctx->stat |= dcp_read(dev,
- dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
- dev_err(dev->dev, "Timeout, Channel status: %x", dev->ctx->stat);
- if (!dev->ctx->stat)
- dev->ctx->stat = -ETIMEDOUT;
- dcp_done_task(data);
- }
- static irqreturn_t dcp_common_irq(int irq, void *context)
- {
- u32 msk;
- struct dcp_dev *dev = (struct dcp_dev *) context;
- del_timer(&dev->watchdog);
- msk = DCP_STAT_IRQ(dcp_read(dev, DCP_REG_STAT));
- dcp_clear(dev, msk, DCP_REG_STAT);
- if (msk == 0)
- return IRQ_NONE;
- dev->ctx->stat |= dcp_read(dev,
- dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
- if (msk & DCP_STAT_CHAN_1)
- tasklet_schedule(&dev->done_task);
- return IRQ_HANDLED;
- }
- static irqreturn_t dcp_vmi_irq(int irq, void *context)
- {
- return dcp_common_irq(irq, context);
- }
- static irqreturn_t dcp_irq(int irq, void *context)
- {
- return dcp_common_irq(irq, context);
- }
- static void dcp_crypt(struct dcp_dev *dev, struct dcp_op *ctx)
- {
- dev->ctx = ctx;
- if ((ctx->flags & DCP_CBC) && ctx->req->info) {
- ctx->flags |= DCP_CBC_INIT;
- memcpy(dev->payload_base + AES_KEYSIZE_128,
- ctx->req->info, AES_KEYSIZE_128);
- }
- dcp_op_start(dev, 1);
- }
- static void dcp_queue_task(unsigned long data)
- {
- struct dcp_dev *dev = (struct dcp_dev *) data;
- struct crypto_async_request *async_req, *backlog;
- struct crypto_ablkcipher *tfm;
- struct dcp_op *ctx;
- struct dcp_dev_req_ctx *rctx;
- struct ablkcipher_request *req;
- unsigned long flags;
- spin_lock_irqsave(&dev->queue_lock, flags);
- backlog = crypto_get_backlog(&dev->queue);
- async_req = crypto_dequeue_request(&dev->queue);
- spin_unlock_irqrestore(&dev->queue_lock, flags);
- if (!async_req)
- goto ret_nothing_done;
- if (backlog)
- backlog->complete(backlog, -EINPROGRESS);
- req = ablkcipher_request_cast(async_req);
- tfm = crypto_ablkcipher_reqtfm(req);
- rctx = ablkcipher_request_ctx(req);
- ctx = crypto_ablkcipher_ctx(tfm);
- if (!req->src || !req->dst)
- goto ret_nothing_done;
- ctx->flags |= rctx->mode;
- ctx->req = req;
- dcp_crypt(dev, ctx);
- return;
- ret_nothing_done:
- clear_bit(DCP_FLAG_BUSY, &dev->flags);
- }
- static int dcp_cra_init(struct crypto_tfm *tfm)
- {
- const char *name = tfm->__crt_alg->cra_name;
- struct dcp_op *ctx = crypto_tfm_ctx(tfm);
- tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_dev_req_ctx);
- ctx->fallback = crypto_alloc_ablkcipher(name, 0,
- CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
- if (IS_ERR(ctx->fallback)) {
- dev_err(global_dev->dev, "Error allocating fallback algo %s\n",
- name);
- return PTR_ERR(ctx->fallback);
- }
- return 0;
- }
- static void dcp_cra_exit(struct crypto_tfm *tfm)
- {
- struct dcp_op *ctx = crypto_tfm_ctx(tfm);
- if (ctx->fallback)
- crypto_free_ablkcipher(ctx->fallback);
- ctx->fallback = NULL;
- }
- /* async interface */
- static int dcp_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
- unsigned int len)
- {
- struct dcp_op *ctx = crypto_ablkcipher_ctx(tfm);
- unsigned int ret = 0;
- ctx->keylen = len;
- ctx->flags = 0;
- if (len == AES_KEYSIZE_128) {
- if (memcmp(ctx->key, key, AES_KEYSIZE_128)) {
- memcpy(ctx->key, key, len);
- ctx->flags |= DCP_NEW_KEY;
- }
- return 0;
- }
- ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
- ctx->fallback->base.crt_flags |=
- (tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);
- ret = crypto_ablkcipher_setkey(ctx->fallback, key, len);
- if (ret) {
- struct crypto_tfm *tfm_aux = crypto_ablkcipher_tfm(tfm);
- tfm_aux->crt_flags &= ~CRYPTO_TFM_RES_MASK;
- tfm_aux->crt_flags |=
- (ctx->fallback->base.crt_flags & CRYPTO_TFM_RES_MASK);
- }
- return ret;
- }
- static int dcp_aes_cbc_crypt(struct ablkcipher_request *req, int mode)
- {
- struct dcp_dev_req_ctx *rctx = ablkcipher_request_ctx(req);
- struct dcp_dev *dev = global_dev;
- unsigned long flags;
- int err = 0;
- if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE))
- return -EINVAL;
- rctx->mode = mode;
- spin_lock_irqsave(&dev->queue_lock, flags);
- err = ablkcipher_enqueue_request(&dev->queue, req);
- spin_unlock_irqrestore(&dev->queue_lock, flags);
- flags = test_and_set_bit(DCP_FLAG_BUSY, &dev->flags);
- if (!(flags & DCP_FLAG_BUSY))
- tasklet_schedule(&dev->queue_task);
- return err;
- }
- static int dcp_aes_cbc_encrypt(struct ablkcipher_request *req)
- {
- struct crypto_tfm *tfm =
- crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
- struct dcp_op *ctx = crypto_ablkcipher_ctx(
- crypto_ablkcipher_reqtfm(req));
- if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
- int err = 0;
- ablkcipher_request_set_tfm(req, ctx->fallback);
- err = crypto_ablkcipher_encrypt(req);
- ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
- return err;
- }
- return dcp_aes_cbc_crypt(req, DCP_AES | DCP_ENC | DCP_CBC);
- }
- static int dcp_aes_cbc_decrypt(struct ablkcipher_request *req)
- {
- struct crypto_tfm *tfm =
- crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
- struct dcp_op *ctx = crypto_ablkcipher_ctx(
- crypto_ablkcipher_reqtfm(req));
- if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
- int err = 0;
- ablkcipher_request_set_tfm(req, ctx->fallback);
- err = crypto_ablkcipher_decrypt(req);
- ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
- return err;
- }
- return dcp_aes_cbc_crypt(req, DCP_AES | DCP_DEC | DCP_CBC);
- }
- static struct crypto_alg algs[] = {
- {
- .cra_name = "cbc(aes)",
- .cra_driver_name = "dcp-cbc-aes",
- .cra_alignmask = 3,
- .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC |
- CRYPTO_ALG_NEED_FALLBACK,
- .cra_blocksize = AES_KEYSIZE_128,
- .cra_type = &crypto_ablkcipher_type,
- .cra_priority = 300,
- .cra_u.ablkcipher = {
- .min_keysize = AES_KEYSIZE_128,
- .max_keysize = AES_KEYSIZE_128,
- .setkey = dcp_aes_setkey,
- .encrypt = dcp_aes_cbc_encrypt,
- .decrypt = dcp_aes_cbc_decrypt,
- .ivsize = AES_KEYSIZE_128,
- }
- },
- };
- /* DCP bootstream verification interface: uses OTP key for crypto */
- static int dcp_bootstream_open(struct inode *inode, struct file *file)
- {
- file->private_data = container_of((file->private_data),
- struct dcp_dev, dcp_bootstream_misc);
- return 0;
- }
- static long dcp_bootstream_ioctl(struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- struct dcp_dev *dev = (struct dcp_dev *) file->private_data;
- void __user *argp = (void __user *)arg;
- int ret;
- if (dev == NULL)
- return -EBADF;
- if (cmd != DBS_ENC && cmd != DBS_DEC)
- return -EINVAL;
- if (copy_from_user(dev->payload_base, argp, 16))
- return -EFAULT;
- if (test_and_set_bit(DCP_FLAG_BUSY, &dev->flags))
- return -EAGAIN;
- dev->ctx = kzalloc(sizeof(struct dcp_op), GFP_KERNEL);
- if (!dev->ctx) {
- dev_err(dev->dev,
- "cannot allocate context for OTP crypto");
- clear_bit(DCP_FLAG_BUSY, &dev->flags);
- return -ENOMEM;
- }
- dev->ctx->flags = DCP_AES | DCP_ECB | DCP_OTP_KEY | DCP_CBC_INIT;
- dev->ctx->flags |= (cmd == DBS_ENC) ? DCP_ENC : DCP_DEC;
- dev->hw_pkg[0]->src = dev->payload_base_dma;
- dev->hw_pkg[0]->dst = dev->payload_base_dma;
- dev->hw_pkg[0]->size = 16;
- dcp_op_start(dev, 0);
- while (test_bit(DCP_FLAG_BUSY, &dev->flags))
- cpu_relax();
- ret = dev->ctx->stat;
- if (!ret && copy_to_user(argp, dev->payload_base, 16))
- ret = -EFAULT;
- kfree(dev->ctx);
- return ret;
- }
- static const struct file_operations dcp_bootstream_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = dcp_bootstream_ioctl,
- .open = dcp_bootstream_open,
- };
- static int dcp_probe(struct platform_device *pdev)
- {
- struct dcp_dev *dev = NULL;
- struct resource *r;
- int i, ret, j;
- dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- global_dev = dev;
- dev->dev = &pdev->dev;
- platform_set_drvdata(pdev, dev);
- r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r) {
- dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
- return -ENXIO;
- }
- dev->dcp_regs_base = devm_ioremap(&pdev->dev, r->start,
- resource_size(r));
- dcp_set(dev, DCP_CTRL_SFRST, DCP_REG_CTRL);
- udelay(10);
- dcp_clear(dev, DCP_CTRL_SFRST | DCP_CTRL_CLKGATE, DCP_REG_CTRL);
- dcp_write(dev, DCP_CTRL_GATHER_RES_WRITE |
- DCP_CTRL_ENABLE_CONTEXT_CACHE | DCP_CTRL_CH_IRQ_E_1,
- DCP_REG_CTRL);
- dcp_write(dev, DCP_CHAN_CTRL_ENABLE_1, DCP_REG_CHAN_CTRL);
- for (i = 0; i < 4; i++)
- dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, i));
- dcp_clear(dev, -1, DCP_REG_STAT);
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!r) {
- dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
- return -EIO;
- }
- dev->dcp_vmi_irq = r->start;
- ret = request_irq(dev->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp", dev);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't request_irq (0)\n");
- return -EIO;
- }
- r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
- if (!r) {
- dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
- ret = -EIO;
- goto err_free_irq0;
- }
- dev->dcp_irq = r->start;
- ret = request_irq(dev->dcp_irq, dcp_irq, 0, "dcp", dev);
- if (ret != 0) {
- dev_err(&pdev->dev, "can't request_irq (1)\n");
- ret = -EIO;
- goto err_free_irq0;
- }
- dev->hw_pkg[0] = dma_alloc_coherent(&pdev->dev,
- DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
- &dev->hw_phys_pkg,
- GFP_KERNEL);
- if (!dev->hw_pkg[0]) {
- dev_err(&pdev->dev, "Could not allocate hw descriptors\n");
- ret = -ENOMEM;
- goto err_free_irq1;
- }
- for (i = 1; i < DCP_MAX_PKG; i++) {
- dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg
- + i * sizeof(struct dcp_hw_packet);
- dev->hw_pkg[i] = dev->hw_pkg[i - 1] + 1;
- }
- dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg;
- dev->payload_base = dma_alloc_coherent(&pdev->dev, 2 * AES_KEYSIZE_128,
- &dev->payload_base_dma, GFP_KERNEL);
- if (!dev->payload_base) {
- dev_err(&pdev->dev, "Could not allocate memory for key\n");
- ret = -ENOMEM;
- goto err_free_hw_packet;
- }
- tasklet_init(&dev->queue_task, dcp_queue_task,
- (unsigned long) dev);
- tasklet_init(&dev->done_task, dcp_done_task,
- (unsigned long) dev);
- spin_lock_init(&dev->queue_lock);
- crypto_init_queue(&dev->queue, 10);
- init_timer(&dev->watchdog);
- dev->watchdog.function = &dcp_watchdog;
- dev->watchdog.data = (unsigned long)dev;
- dev->dcp_bootstream_misc.minor = MISC_DYNAMIC_MINOR,
- dev->dcp_bootstream_misc.name = "dcpboot",
- dev->dcp_bootstream_misc.fops = &dcp_bootstream_fops,
- ret = misc_register(&dev->dcp_bootstream_misc);
- if (ret != 0) {
- dev_err(dev->dev, "Unable to register misc device\n");
- goto err_free_key_iv;
- }
- for (i = 0; i < ARRAY_SIZE(algs); i++) {
- algs[i].cra_priority = 300;
- algs[i].cra_ctxsize = sizeof(struct dcp_op);
- algs[i].cra_module = THIS_MODULE;
- algs[i].cra_init = dcp_cra_init;
- algs[i].cra_exit = dcp_cra_exit;
- if (crypto_register_alg(&algs[i])) {
- dev_err(&pdev->dev, "register algorithm failed\n");
- ret = -ENOMEM;
- goto err_unregister;
- }
- }
- dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
- return 0;
- err_unregister:
- for (j = 0; j < i; j++)
- crypto_unregister_alg(&algs[j]);
- err_free_key_iv:
- dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
- dev->payload_base_dma);
- err_free_hw_packet:
- dma_free_coherent(&pdev->dev, DCP_MAX_PKG *
- sizeof(struct dcp_hw_packet), dev->hw_pkg[0],
- dev->hw_phys_pkg);
- err_free_irq1:
- free_irq(dev->dcp_irq, dev);
- err_free_irq0:
- free_irq(dev->dcp_vmi_irq, dev);
- return ret;
- }
- static int dcp_remove(struct platform_device *pdev)
- {
- struct dcp_dev *dev;
- int j;
- dev = platform_get_drvdata(pdev);
- dma_free_coherent(&pdev->dev,
- DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
- dev->hw_pkg[0], dev->hw_phys_pkg);
- dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
- dev->payload_base_dma);
- free_irq(dev->dcp_irq, dev);
- free_irq(dev->dcp_vmi_irq, dev);
- tasklet_kill(&dev->done_task);
- tasklet_kill(&dev->queue_task);
- for (j = 0; j < ARRAY_SIZE(algs); j++)
- crypto_unregister_alg(&algs[j]);
- misc_deregister(&dev->dcp_bootstream_misc);
- return 0;
- }
- static struct of_device_id fs_dcp_of_match[] = {
- { .compatible = "fsl-dcp"},
- {},
- };
- static struct platform_driver fs_dcp_driver = {
- .probe = dcp_probe,
- .remove = dcp_remove,
- .driver = {
- .name = "fsl-dcp",
- .owner = THIS_MODULE,
- .of_match_table = fs_dcp_of_match
- }
- };
- module_platform_driver(fs_dcp_driver);
- MODULE_AUTHOR("Tobias Rauter <tobias.rauter@gmail.com>");
- MODULE_DESCRIPTION("Freescale DCP Crypto Driver");
- MODULE_LICENSE("GPL");
|