summaryrefslogtreecommitdiff
path: root/wolfcrypt/src/port/caam/caam_driver.c
diff options
context:
space:
mode:
authorauth12 <[email protected]>2020-07-22 08:34:12 -0700
committerauth12 <[email protected]>2020-07-22 08:34:12 -0700
commit5015ddb9b1eee748efc24056e46f81888c975f7a (patch)
treea810f6ee90f8bfe0e934fdd9142198e6b3862957 /wolfcrypt/src/port/caam/caam_driver.c
downloadwolfssl_windows-5015ddb9b1eee748efc24056e46f81888c975f7a.tar.xz
wolfssl_windows-5015ddb9b1eee748efc24056e46f81888c975f7a.zip
Initial commit
Diffstat (limited to 'wolfcrypt/src/port/caam/caam_driver.c')
-rw-r--r--wolfcrypt/src/port/caam/caam_driver.c1713
1 files changed, 1713 insertions, 0 deletions
diff --git a/wolfcrypt/src/port/caam/caam_driver.c b/wolfcrypt/src/port/caam/caam_driver.c
new file mode 100644
index 0000000..5d44f2d
--- /dev/null
+++ b/wolfcrypt/src/port/caam/caam_driver.c
@@ -0,0 +1,1713 @@
+/* caam_driver.c
+ *
+ * Copyright (C) 2006-2020 wolfSSL Inc.
+ *
+ * This file is part of wolfSSL.
+ *
+ * wolfSSL 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.
+ *
+ * wolfSSL 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-1335, USA
+ */
+
+#if defined(__INTEGRITY) || defined(INTEGRITY)
+
+/* build into Integrity kernel */
+#include <bsp.h>
+#include "wolfssl/wolfcrypt/port/caam/caam_driver.h"
+
+#define CAAM_READ(reg) *(volatile unsigned int*)(reg)
+#define CAAM_WRITE(reg, in) *(volatile unsigned int*)(reg) = (in);
+
+#define DESC_COUNT 1
+#define MAX_BUF 20
+#define BUFFER_COUNT (MAX_BUF * DESC_COUNT)
+
+/* CAAM descriptors can only be 64 unsigned ints */
+#define MAX_DESC_SZ 64
+
+/* 64 byte buffer for when data crosses a page boundary */
+#define ALIGN_BUF 16
+
+/* MAX_CTX is 64 bytes (sha512 digest) + 8 bytes (CAAM length value) */
+#define MAX_CTX 18
+
+#define MIN_READ_REG 0xF2100000
+#define MAX_READ_REG 0XF2110000
+
+struct JobRing {
+ Address JobIn;
+ Address JobOut;
+ Address Desc;
+ Value page; /* page allocation for descriptor to use */
+};
+
+struct buffer {
+ Address data;
+ Address dataSz;
+};
+
+/* CAAM descriptor */
+struct DescStruct {
+ struct IORequestStruct TheIORequest;
+ struct CAAM_DEVICE* caam;
+ struct buffer buf[MAX_BUF]; /* buffers holding data input address */
+ UINT4 desc[MAX_DESC_SZ]; /* max size of 64 word32 */
+ UINT4 aadSzBuf[4]; /* Formatted AAD size for CCM */
+ UINT4 alignBuf[ALIGN_BUF]; /* 64 byte buffer for non page
+ align */
+ UINT4 iv[MAX_CTX]; /* AES IV and also hash state */
+ UINT4 ctxBuf[MAX_CTX]; /* key */
+ Address output; /* address to output buffer */
+ Address ctxOut; /* address to update buffer holding state */
+ Value alignIdx;/* index for align buffer */
+ Value idx; /* index for descriptor buffer */
+ Value headIdx; /* for first portion of descriptor buffer */
+ Value lastIdx; /* for last portion of descriptor buffer */
+ Value outputIdx; /* idx to output buffer in "buf" */
+ Value inputSz; /* size of input buffer */
+ Value ctxSz; /* size of CTX/Key buffer */
+ Value aadSz; /* AAD size for CCM */
+ Value lastFifo;
+ Value type;
+ Value state;
+ Value DescriptorCount;
+ Boolean running; /* True if building/running descriptor is
+ in process */
+};
+
+struct CAAM_DEVICE {
+ struct IODeviceVectorStruct caamVector;
+ struct IODescriptorStruct IODescriptorArray[BUFFER_COUNT];
+ struct DescStruct DescArray[DESC_COUNT];
+ volatile Value InterruptStatus;
+ CALL HandleInterruptCall;
+ struct JobRing ring;
+};
+
+#define DRIVER_NAME "wolfSSL_CAAM_Driver"
+
+static struct CAAM_DEVICE caam;
+
+/******************************************************************************
+ Internal CAAM Job Ring and partition functions
+ ****************************************************************************/
+
+/* flush job ring and reset */
+static Error caamReset(void)
+{
+ int t = 100000; /* time out counter for flushing job ring */
+
+ /* make sure interrupts are masked in JRCFGR0_LS register */
+ CAAM_WRITE(CAAM_BASE | 0x1054, CAAM_READ(CAAM_BASE | 0x1054) | 1);
+
+ /* flush and reset job rings using JRCR0 register */
+ CAAM_WRITE(CAAM_BASE | 0x106C, 1);
+
+ /* check register JRINTR for if halt is in progress */
+ while (t > 0 && ((CAAM_READ(CAAM_BASE | 0x104C) & 0x4) == 0x4)) t--;
+ if (t == 0) {
+ /*unrecoverable failure, the job ring is locked, up hard reset needed*/
+ return NotRestartable;
+ }
+
+ /* now that flush has been done restart the job ring */
+ t = 100000;
+ CAAM_WRITE(CAAM_BASE | 0x106C, 1);
+ while (t > 0 && ((CAAM_READ(CAAM_BASE | 0x106C) & 1) == 1)) t--;
+ if (t == 0) {
+ /*unrecoverable failure, reset bit did not return to 0 */
+ return NotRestartable;
+ }
+
+ /* reset most registers and state machines in CAAM using MCFGR register
+ also reset DMA */
+ CAAM_WRITE(CAAM_BASE | 0x0004, 0x90000000);
+
+ return Success;
+}
+
+/* returns MemoryMapMayNotBeEmpty if page/par is already owned
+ * returns Success on success
+ * all other returns is an error state
+ */
+static Error caamCreatePartition(unsigned char page, unsigned char par)
+{
+ /* check ownership of partition */
+ if ((CAAM_READ(CAAM_BASE | 0x1FBC) & (0x3 << (par * 2))) > 0) {
+ return MemoryMapMayNotBeEmpty;
+ }
+
+ /* set generic all access permissions, gets reset later */
+ CAAM_WRITE(CAAM_BASE | (0x1108 + (par * 16)), 0xF);
+ CAAM_WRITE(CAAM_BASE | (0x110C + (par * 16)), 0xF);
+ CAAM_WRITE(CAAM_BASE | (0x1104 + (par * 16)), 0xFF);
+
+ /* check ownership of page */
+ CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | 0x5);
+ /* wait for inquiry cmd to complete */
+ while ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000C000) > 0 &&
+ (CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) == 0) {
+ }
+ if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x000000C0) == 0xC0) {
+ /* owns the page can dealloc it */
+ CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | 0x2);
+ while ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000C000) > 0 &&
+ (CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) == 0) {}
+ if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) > 0) {
+ /* error while deallocating page */
+ return MemoryMapMayNotBeEmpty; /* PSP set on page or is unavailable */
+ }
+ }
+ else {
+ /* check if owned by someone else */
+ if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x000000C0) != 0) {
+ return MemoryMapMayNotBeEmpty;
+ }
+ }
+
+ /* allocate page to partition */
+ CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | (par << 8) | 0x1);
+ /* wait for alloc cmd to complete */
+ while ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000C000) > 0 &&
+ (CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) == 0) {
+ }
+
+ if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) > 0) {
+ return MemoryOperationNotPerformed;
+ }
+
+ /* double check ownership now of page */
+ CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | 0x5);
+ /* wait for inquiry cmd to complete */
+ while ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000C000) > 0 &&
+ (CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) == 0) {
+ }
+ if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000000F) == 0 ||
+ (CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) > 0) {
+ /* page not owned */
+ return MemoryOperationNotPerformed;
+ }
+
+ return Success;
+}
+
+
+/* Gets the status of a job. Returns Waiting if no output jobs ready to be
+ * read.
+ * If no jobs are done then return Waiting
+ * If jobs are done but does not match desc then return NoActivityReady
+ * Status holds the error values if any */
+static Error caamGetJob(struct CAAM_DEVICE* dev, UINT4* status)
+{
+ UINT4 reg = CAAM_READ(CAAM_BASE | 0x1044); /* JRSTAR0 status */
+ if (status) {
+ *status = 0;
+ }
+
+ /* check for DECO, CCB, and Job Ring error state JRSTAR0 register */
+ if (((reg & 0xF0000000) == 0x20000000) || /* CCB error */
+ ((reg & 0xF0000000) == 0x40000000)|| /* DECO error */
+ ((reg & 0xF0000000) == 0x60000000)) { /* Job Ring error */
+
+ if ((reg & 0x0000000F) > 0) {
+ *status = reg;
+ return Failure;
+ }
+ }
+
+ /* Check number of done jobs in output list */
+ reg = CAAM_READ(CAAM_BASE | 0x103C);
+ if ((reg & 0x000003FF) > 0) {
+ UINT4* out = (UINT4*)(dev->ring.JobOut);
+ if (status) {
+ *status = out[1];
+ }
+
+ if ((dev->ring.Desc ^ 0xF0000000) != out[0]) {
+ db_printf("CAAM job completed vs expected mismatch");
+ return NoActivityReady;
+ }
+
+ if (out[1] > 0) {
+ return Failure;
+ }
+
+ /* increment jobs removed */
+ CAAM_WRITE(CAAM_BASE | 0x1034, 1);
+ }
+ else {
+ /* check if the CAAM is idle and not processing any descriptors */
+ if ((CAAM_READ(CAAM_BASE | 0x0FD4) & 0x00000002) == 2 /* idle */
+ && (CAAM_READ(CAAM_BASE | 0x0FD4) & 0x00000001) == 0) {
+ return NoActivityReady;
+ }
+
+ return Waiting;
+ }
+
+ return Success;
+}
+
+
+/* Initialize CAAM RNG
+ * returns 0 on success */
+static int caamInitRng(struct CAAM_DEVICE* dev)
+{
+ UINT4 reg, status;
+ int ret = 0;
+
+ /* Set up use of the TRNG for seeding wolfSSL HASH-DRBG */
+ CAAM_WRITE(CAAM_RTMCTL, CAAM_PRGM);
+ CAAM_WRITE(CAAM_RTMCTL, CAAM_READ(CAAM_RTMCTL) | 0x40); /* reset */
+
+ /* Set up reading from TRNG */
+ CAAM_WRITE(CAAM_RTMCTL, CAAM_READ(CAAM_RTMCTL) | CAAM_TRNG);
+
+ /* Set up delay for TRNG @TODO Optimizations?
+ * Shift left with RTSDCTL because 0-15 is for sample number
+ * Also setting the max and min frequencies */
+ CAAM_WRITE(CAAM_RTSDCTL, (CAAM_ENT_DLY << 16) | 0x09C4);
+ CAAM_WRITE(CAAM_RTFRQMIN, CAAM_ENT_DLY >> 1); /* 1/2 */
+ CAAM_WRITE(CAAM_RTFRQMAX, CAAM_ENT_DLY << 3); /* up to 8x */
+
+ /* Set back to run mode and clear RTMCL error bit */
+ reg = CAAM_READ(CAAM_RTMCTL) ^ CAAM_PRGM;
+
+ CAAM_WRITE(CAAM_RTMCTL, reg);
+ reg = CAAM_READ(CAAM_RTMCTL);
+ reg |= CAAM_CTLERR;
+ CAAM_WRITE(CAAM_RTMCTL, reg);
+
+ /* check input slot is available and then add */
+ if (CAAM_READ(CAAM_BASE | 0x1014) > 0) {
+ UINT4* in = (UINT4*)dev->ring.JobIn;
+
+ memcpy((unsigned char*)dev->ring.Desc, (unsigned char*)wc_rng_start,
+ sizeof(wc_rng_start));
+
+ in[0] = dev->ring.Desc ^ 0xF0000000; /* physical address */
+ CAAM_WRITE(CAAM_IRJAR0, 0x00000001);
+ }
+ else {
+ return Waiting;
+ }
+
+ do {
+ ret = caamGetJob(dev, &status);
+ /* @TODO use a better way to chill out CPU. */
+ } while (ret == Waiting);
+
+ return ret;
+}
+
+
+static Error caamDoJob(struct DescStruct* desc)
+{
+ Error ret;
+ UINT4 status;
+
+ /* clear and set desc size */
+ desc->desc[0] &= 0xFFFFFF80;
+ desc->desc[0] += desc->idx;
+
+ /* check input slot is available and then add */
+ if (CAAM_READ(CAAM_BASE | 0x1014) > 0) {
+ UINT4* in = (UINT4*)desc->caam->ring.JobIn;
+
+ memcpy((unsigned char*)desc->caam->ring.Desc, (unsigned char*)desc->desc,
+ (desc->idx + 1) * sizeof(UINT4));
+
+ in[0] = desc->caam->ring.Desc ^ 0xF0000000; /* physical address */
+ CAAM_WRITE(CAAM_IRJAR0, 0x00000001);
+ }
+ else {
+ return Waiting;
+ }
+
+ do {
+ ret = caamGetJob(desc->caam, &status);
+ /* @TODO use a better way to chill out CPU. */
+ } while (ret == Waiting);
+
+ if (status != 0 || ret != Success) {
+ #if 0
+ /* Used during testing to print out descriptor */
+ {
+ char msg[2048];
+ char* pt = msg;
+ int z;
+
+ memset(msg, 0, sizeof(msg));
+ for (z = 0; z < desc->idx; z++) {
+ snprintf(pt, sizeof(msg) - (z * 21), "desc[%d] = 0x%8.8x, ",
+ z, desc->desc[z]);
+ pt += 21;
+ }
+ snprintf(pt, sizeof(msg) - (z * 21), "status = 0x%8.8x\n", status);
+ if (desc->buf[0].data != 0) { /* for testing */
+ memcpy((char*)desc->buf[0].data, msg, sizeof(msg));
+ }
+ }
+ #endif
+
+
+ /* try to reset after error */
+ caamReset();
+ return ret;
+ }
+
+ return Success;
+}
+
+
+/* handle input or output buffers
+ * NOTES: if sz == 0 then read all the rest of the buffers available
+ * when align == 1 then there is no alignment constraints
+ *
+ * returns the data size in bytes on success. With failure a negative value is
+ * returned.
+ */
+static int caamAddIO(struct DescStruct* desc, UINT4 options, UINT4 sz,
+ UINT4 align, UINT4* idx)
+{
+ int i, outSz = 0;
+
+ if (align == 0) {
+ return -1; /* programming error */
+ }
+
+ for (i = *idx; i < desc->DescriptorCount; i++) {
+ /* input must be a multiple of "align" bytes */
+ struct buffer* buf = &desc->buf[i];
+ int blocks = buf->dataSz / align;
+ Address data = buf->data;
+ Address dataSz = buf->dataSz;
+
+ if (outSz >= sz && sz != 0) {
+ break;
+ }
+
+ if (dataSz % align > 0) {
+ /* store potential overlap */
+ int tmpSz = dataSz % align;
+ int add = (tmpSz < (align - desc->alignIdx)) ? tmpSz :
+ align - desc->alignIdx;
+ unsigned char* local = (unsigned char*)desc->alignBuf;
+
+ /* if already something in the buffer then add from front */
+ if (desc->alignIdx > 0) {
+ memcpy((unsigned char*)&local[desc->alignIdx],
+ (unsigned char*)data, add);
+ data += add;
+ }
+ else {
+ memcpy((unsigned char*)&local[desc->alignIdx],
+ (unsigned char*)data + (blocks * align), add);
+ }
+ dataSz -= add;
+ desc->alignIdx += add;
+ }
+
+ if (desc->alignIdx == align) {
+ desc->lastFifo = desc->idx;
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return -1;
+ }
+ desc->desc[desc->idx++] = options + desc->alignIdx;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->alignBuf);
+ ASP_FlushCaches((Address)desc->alignBuf, desc->alignIdx);
+ outSz += desc->alignIdx;
+ }
+
+ if (blocks > 0) {
+ desc->lastFifo = desc->idx;
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return -1;
+ }
+ desc->desc[desc->idx++] = options + (blocks * align);
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(data);
+ outSz += (blocks * align);
+
+ /* only one buffer available for align cases so exit here and make
+ a new descriptor after running current one */
+ if (desc->alignIdx == align) {
+ desc->alignIdx = 0;
+ i++; /* start at next buffer */
+ break;
+ }
+ }
+ }
+
+ *idx = i;
+ return outSz;
+}
+
+
+/******************************************************************************
+ IODevice Register Read and Write
+ ****************************************************************************/
+
+static Error caamReadRegister(IODeviceVector ioCaam, Value reg, Value *out)
+{
+ if (reg < MIN_READ_REG || reg > MAX_READ_REG) {
+ return IllegalRegisterNumber;
+ }
+
+ switch (reg) {
+ case CAAM_STATUS:
+ case CAAM_VERSION_MS:
+ case CAAM_VERSION_LS:
+ case CAMM_SUPPORT_MS:
+ case CAMM_SUPPORT_LS:
+ case CAAM_RTMCTL:
+ *out = CAAM_READ(reg);
+ break;
+
+ default:
+ return IllegalRegisterNumber;
+ }
+
+ (void)ioCaam;
+ return Success;
+}
+
+
+static Error caamWriteRegister(IODeviceVector ioCaam, Value reg, Value in)
+{
+ /* Should be no need for writes */
+ return OperationNotAllowedOnTheUniversalIODevice;
+}
+
+
+/******************************************************************************
+ CAAM Blob Operations
+ ****************************************************************************/
+
+/* limit on size due to size of job ring being 64 word32's */
+static Error caamBlob(struct DescStruct* desc)
+{
+ Error err;
+ UINT4 keyType = 0x00000C08; /* default red */
+ UINT4 i = 0;
+ int sz = 0, ret;
+
+ if (desc->idx + 3 > MAX_DESC_SZ) {
+ return Failure;
+ }
+
+ /*default to Red Key type, with offset of 12 and 8 byte load to context 2*/
+ desc->desc[desc->idx++] = (CAAM_LOAD_CTX | CAAM_CLASS2 | CAAM_IMM | keyType);
+
+ /* add key modifier */
+ if (i < desc->DescriptorCount) {
+ UINT4* pt;
+ Address data = desc->buf[i].data;
+ Address dataSz = desc->buf[i].dataSz;
+
+ pt = (UINT4*)data;
+ if (dataSz < 8) { /* expecting 8 bytes for key modifier*/
+ return TooManyBuffers;
+ }
+ desc->desc[desc->idx++] = pt[0];
+ desc->desc[desc->idx++] = pt[1];
+ }
+
+ /* add input */
+ while (sz < desc->inputSz && i < desc->DescriptorCount) {
+ ret = caamAddIO(desc, CAAM_SEQI, desc->inputSz - sz, 1, &i);
+ if (ret < 0) { /* handle error case */
+ return TooManyBuffers;
+ }
+ sz += ret;
+ }
+ desc->outputIdx = i;
+
+ /* add output */
+ if (caamAddIO(desc, CAAM_SEQO, 0, 1, &i) < 0) {
+ return TooManyBuffers;
+ }
+
+ if (desc->idx + 1 > MAX_DESC_SZ) {
+ return Failure;
+ }
+ desc->desc[desc->idx++] = CAAM_OP | CAAM_OPID_BLOB | desc->type;
+
+ if ((err = caamDoJob(desc)) != Success) {
+ return err;
+ }
+
+ /* flush output buffers */
+ for (i = desc->outputIdx; i < desc->DescriptorCount; i++) {
+ ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
+ }
+
+ return Success;
+}
+
+
+/******************************************************************************
+ CAAM AES Operations
+ ****************************************************************************/
+
+/* returns amount written on success and negative value in error case.
+ * Is different from caamAddIO in that it only adds a single input buffer
+ * rather than multiple ones.
+ */
+static int caamAesInput(struct DescStruct* desc, UINT4* idx, int align,
+ UINT4 totalSz)
+{
+ int sz;
+ UINT4 i = *idx;
+
+ /* handle alignment constraints on input */
+ if (desc->alignIdx > 0) {
+ sz = desc->alignIdx;
+
+ /* if there is more input buffers then add part of it */
+ if (i < desc->outputIdx && i < desc->DescriptorCount) {
+ sz = align - desc->alignIdx;
+ sz = (sz <= desc->buf[i].dataSz) ? sz : desc->buf[i].dataSz;
+ memcpy((unsigned char*)(desc->alignBuf) + desc->alignIdx,
+ (unsigned char*)(desc->buf[i].data), sz);
+
+ desc->buf[i].dataSz -= sz;
+ desc->buf[i].data += sz;
+ sz += desc->alignIdx;
+ }
+
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return -1;
+ }
+ ASP_FlushCaches((Address)desc->alignBuf, sz);
+ desc->desc[desc->idx++] = (CAAM_FIFO_L | FIFOL_TYPE_LC1 |
+ CAAM_CLASS1 | FIFOL_TYPE_MSG) + sz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->alignBuf);
+ desc->alignIdx = 0;
+ }
+ else {
+ sz = desc->buf[i].dataSz;
+ if ((totalSz + sz) == desc->inputSz) { /* not an issue on final */
+ align = 1;
+ }
+
+ desc->alignIdx = sz % align;
+ if (desc->alignIdx != 0) {
+ sz -= desc->alignIdx;
+ memcpy((unsigned char*)desc->alignBuf,
+ (unsigned char*)(desc->buf[i].data) + sz,
+ desc->alignIdx);
+ }
+
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return -1;
+ }
+ desc->desc[desc->idx++] = (CAAM_FIFO_L | FIFOL_TYPE_LC1 |
+ CAAM_CLASS1 | FIFOL_TYPE_MSG) + sz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->buf[i].data);
+ i++;
+ }
+
+ *idx = i;
+ return sz;
+}
+
+
+/* returns enum Success on success, all other return values should be
+ * considered an error.
+ *
+ * ofst is the amount of leftover buffer from previous calls
+ * inputSz is the amount of input in bytes that is being matched to output
+ */
+static Error caamAesOutput(struct DescStruct* desc, int* ofst, UINT4 inputSz)
+{
+ int offset = *ofst;
+
+ if (desc->output != 0 && offset > 0 && inputSz > 0) {
+ UINT4 addSz;
+
+ /* handle potential leftovers */
+ addSz = (inputSz >= offset) ? offset : inputSz;
+
+ inputSz -= addSz;
+ desc->desc[desc->idx++] = CAAM_FIFO_S | FIFOS_TYPE_MSG + addSz;
+ if (inputSz > 0) { /* check if expecting more output */
+ desc->desc[desc->idx - 1] |= CAAM_FIFOS_CONT;
+ }
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->output);
+
+ if (addSz == offset) {
+ /* reset */
+ desc->output = 0;
+ offset = 0;
+ }
+ else {
+ offset -= addSz;
+ desc->output += addSz;
+
+ if (offset < 0) {
+ return TransferFailed;
+ }
+ }
+ }
+
+ for (; desc->lastIdx < desc->DescriptorCount; desc->lastIdx++) {
+ struct buffer* buf = &desc->buf[desc->lastIdx];
+
+ if (inputSz > 0) {
+ int tmp;
+
+ if (buf->dataSz <= inputSz) {
+ tmp = buf->dataSz;
+ }
+ else {
+ offset = buf->dataSz - inputSz;
+ tmp = inputSz;
+ desc->output = buf->data + tmp;
+ }
+ inputSz -= tmp;
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_FIFO_S | FIFOS_TYPE_MSG + tmp;
+ if (inputSz > 0) { /* check if expecting more output */
+ desc->desc[desc->idx - 1] |= CAAM_FIFOS_CONT;
+ }
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(buf->data);
+ }
+ else {
+ break;
+ }
+ }
+
+ *ofst = offset;
+ return Success;
+}
+
+
+/* check size of output and get starting buffer for it */
+static Error caamAesOutSz(struct DescStruct* desc, UINT4 i)
+{
+ int sz = 0;
+
+ for (desc->outputIdx = i; desc->outputIdx < desc->DescriptorCount &&
+ sz < desc->inputSz; desc->outputIdx++) {
+ sz += desc->buf[desc->outputIdx].dataSz;
+ }
+ desc->lastIdx = desc->outputIdx;
+
+ /* make certain that output size is same as input */
+ sz = 0;
+ for (; desc->lastIdx < desc->DescriptorCount; desc->lastIdx++) {
+ sz += desc->buf[desc->lastIdx].dataSz;
+ }
+ if (sz != desc->inputSz) {
+ return SizeIsTooLarge;
+ }
+ desc->lastIdx = desc->outputIdx;
+
+ return Success;
+}
+
+
+/* AES operations follow the buffer sequence of KEY -> (IV) -> Input -> Output
+ */
+static Error caamAes(struct DescStruct* desc)
+{
+ struct buffer* ctx[3];
+ struct buffer* iv[3];
+ Value ofst = 0;
+ Error err;
+ UINT4 i, totalSz = 0;
+ int ctxIdx = 0;
+ int ivIdx = 0;
+ int offset = 0;
+ int align = 1;
+ int sz = 0;
+
+ int ctxSz = desc->ctxSz;
+
+ if (desc->state != CAAM_ENC && desc->state != CAAM_DEC) {
+ return IllegalStatusNumber;
+ }
+
+ if (ctxSz != 16 && ctxSz != 24 && ctxSz != 32) {
+ return ArgumentError;
+ }
+
+ /* get key */
+ for (i = 0; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)desc->ctxBuf;
+
+ if (sz < ctxSz && sz < (MAX_CTX * sizeof(UINT4))) {
+ ctx[ctxIdx] = buf;
+ sz += buf->dataSz;
+
+ memcpy((unsigned char*)&local[offset],
+ (unsigned char*)ctx[ctxIdx]->data, ctx[ctxIdx]->dataSz);
+ offset += ctx[ctxIdx]->dataSz;
+ ctxIdx++;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* sanity checks on size of key */
+ if (sz > ctxSz) {
+ return SizeIsTooLarge;
+ }
+ if (ctxSz > (MAX_CTX * sizeof(UINT4)) - 16) {
+ return ArgumentError;
+ }
+
+ /* Flush cache of ctx buffer then :
+ Add KEY Load command 0x0220000X
+ Add address to read key from 0xXXXXXXXX */
+ ASP_FlushCaches((Address)desc->ctxBuf, ctxSz);
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = (CAAM_KEY | CAAM_CLASS1 | CAAM_NWB) + ctxSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->ctxBuf);
+
+ /* get IV if needed by algorithm */
+ switch (desc->type) {
+ case CAAM_AESECB:
+ break;
+
+ case CAAM_AESCTR:
+ ofst = 0x00001000;
+ /* fall through because states are the same only the offset changes */
+
+ case CAAM_AESCBC:
+ {
+ int maxSz = 16; /* default to CBC/CTR max size */
+
+ sz = 0;
+ offset = 0;
+ for (; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)desc->iv;
+
+ if (sz < maxSz) {
+ iv[ivIdx] = buf;
+
+ if (buf->dataSz + sz > maxSz) {
+ return SizeIsTooLarge;
+ }
+
+ sz += buf->dataSz;
+ memcpy((unsigned char*)&local[offset],
+ (unsigned char*)iv[ivIdx]->data, iv[ivIdx]->dataSz);
+ offset += iv[ivIdx]->dataSz;
+ ivIdx++;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (sz != maxSz) {
+ /* invalid IV size */
+ return SizeIsTooLarge;
+ }
+
+ ASP_FlushCaches((Address)desc->iv, maxSz);
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = (CAAM_LOAD_CTX | CAAM_CLASS1 | ofst) + maxSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->iv);
+ }
+ break;
+
+ default:
+ return OperationNotImplemented;
+ }
+
+ /* write operation */
+ if (desc->idx + 1 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_OP | CAAM_CLASS1 | desc->type |
+ CAAM_ALG_UPDATE | desc->state;
+
+ /* find output buffers */
+ if (caamAesOutSz(desc, i) != Success) {
+ return SizeIsTooLarge;
+ }
+
+ /* set alignment constraints */
+ if (desc->type == CAAM_AESCBC || desc->type == CAAM_AESECB) {
+ align = 16;
+ }
+
+ /* indefinite loop for input/output buffers */
+ desc->headIdx = desc->idx;
+ desc->output = 0;
+ offset = 0; /* store left over amount for output buffer */
+ do {
+ desc->idx = desc->headIdx; /* reset for each loop */
+
+ /* add a single input buffer (multiple ones was giving deco watch dog
+ * time out errors on the FIFO load of 1c.
+ * @TODO this could be a place for optimization if more data could be
+ * loaded in at one time */
+ if ((sz = caamAesInput(desc, &i, align, totalSz)) < 0) {
+ return TransferFailed;
+ }
+ totalSz += sz;
+
+ if (caamAesOutput(desc, &offset, sz) != Success) {
+ return TransferFailed;
+ }
+
+ /* store updated IV */
+ if (ivIdx > 0) {
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_STORE_CTX | CAAM_CLASS1 | ofst | 16;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical((Address)desc->iv);
+ }
+
+ if ((err = caamDoJob(desc)) != Success) {
+ return err;
+ }
+ ASP_FlushCaches((Address)desc->iv, 16);
+ } while (desc->lastIdx < desc->DescriptorCount || offset > 0);
+
+ /* flush output buffers */
+ for (i = desc->outputIdx; i < desc->lastIdx; i++) {
+ ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
+ }
+
+ /* handle case with IV */
+ if (ivIdx > 0) {
+ unsigned char* pt = (unsigned char*)desc->iv;
+ ASP_FlushCaches((Address)pt, 16);
+ for (i = 0; i < ivIdx; i++) {
+ memcpy((unsigned char*)iv[i]->data, pt, iv[i]->dataSz);
+ pt += iv[i]->dataSz;
+ ASP_FlushCaches(iv[i]->data, iv[i]->dataSz);
+ }
+ }
+
+ return Success;
+}
+
+
+/******************************************************************************
+ CAAM AEAD Operations
+ ****************************************************************************/
+
+/* AEAD operations follow the buffer sequence of KEY -> (IV or B0 | CTR0) -> (AD)
+ * -> Input -> Output
+ *
+ */
+static Error caamAead(struct DescStruct* desc)
+{
+ struct buffer* ctx[3];
+ struct buffer* iv[3];
+ Value ofst = 0;
+ UINT4 state = CAAM_ALG_INIT;
+ UINT4 totalSz = 0;
+ Error err;
+ UINT4 i;
+ int ctxIdx = 0;
+ int ivIdx = 0;
+ int offset = 0;
+ int sz = 0;
+ int ivSz = 32; /* size of B0 | CTR0 for CCM mode */
+ int ctxSz = desc->ctxSz;
+ int align = 16; /* input should be multiples of 16 bytes unless is final */
+ int opIdx;
+
+ if (desc->state != CAAM_ENC && desc->state != CAAM_DEC) {
+ return IllegalStatusNumber;
+ }
+
+ /* sanity check is valid AES key size */
+ if (ctxSz != 16 && ctxSz != 24 && ctxSz != 32) {
+ return ArgumentError;
+ }
+
+ /* get key */
+ for (i = 0; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)desc->ctxBuf;
+
+ if (sz < ctxSz && sz < (MAX_CTX * sizeof(UINT4))) {
+ ctx[ctxIdx] = buf;
+ sz += buf->dataSz;
+
+ memcpy((unsigned char*)&local[offset],
+ (unsigned char*)ctx[ctxIdx]->data, ctx[ctxIdx]->dataSz);
+ offset += ctx[ctxIdx]->dataSz;
+ ctxIdx++;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* sanity checks on size of key */
+ if (sz > ctxSz) {
+ return SizeIsTooLarge;
+ }
+
+ /* Flush cache of ctx buffer then :
+ Add KEY Load command 0x0220000X
+ Add address to read key from 0xXXXXXXXX */
+ ASP_FlushCaches((Address)desc->ctxBuf, ctxSz);
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = (CAAM_KEY | CAAM_CLASS1 | CAAM_NWB) + ctxSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->ctxBuf);
+
+ desc->headIdx = desc->idx;
+ desc->output = 0;
+ offset = 0; /* store left over amount for output buffer */
+ do {
+ desc->idx = desc->headIdx; /* reset for each loop */
+
+ /* write operation */
+ if (desc->idx + 1 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ opIdx = desc->idx;
+ desc->desc[desc->idx++] = CAAM_OP | CAAM_CLASS1 | state | desc->type |
+ desc->state;
+
+ /* get IV if needed by algorithm */
+ switch (desc->type) {
+ case CAAM_AESCCM:
+ if ((state & CAAM_ALG_INIT) == CAAM_ALG_INIT) {
+ sz = 0;
+ offset = 0;
+ for (; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)desc->iv;
+
+ if (sz < ivSz) {
+ iv[ivIdx] = buf;
+
+ if (buf->dataSz + sz > ivSz) {
+ return SizeIsTooLarge;
+ }
+
+ sz += buf->dataSz;
+ memcpy((unsigned char*)&local[offset],
+ (unsigned char*)iv[ivIdx]->data, iv[ivIdx]->dataSz);
+ offset += iv[ivIdx]->dataSz;
+ ivIdx++;
+ }
+ else {
+ break;
+ }
+ }
+
+ if (sz != ivSz) {
+ /* invalid IV size */
+ return SizeIsTooLarge;
+ }
+ offset = 0;
+ }
+
+ ASP_FlushCaches((Address)desc->iv, ivSz);
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = (CAAM_LOAD_CTX | CAAM_CLASS1 | ofst)
+ + ivSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->iv);
+ break;
+
+ default:
+ return OperationNotImplemented;
+ }
+
+
+ /********* handle AAD -- is only done with Init **********************/
+ if ((state & CAAM_ALG_INIT) == CAAM_ALG_INIT) {
+ if ((desc->type == CAAM_AESCCM) && (desc->aadSz > 0)) {
+ /* set formatted AAD buffer size for CCM */
+ ASP_FlushCaches((Address)desc->aadSzBuf, sizeof(desc->aadSzBuf));
+ desc->desc[desc->idx++] = CAAM_FIFO_L | CAAM_CLASS1 |
+ FIFOL_TYPE_AAD + desc->aadSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->aadSzBuf);
+
+ /* now set aadSz to unformatted version for getting buffers */
+ if (desc->aadSz == 2) {
+ unsigned char* pt = (unsigned char*)desc->aadSzBuf;
+ desc->aadSz = (((UINT4)pt[0] & 0xFF) << 8) |
+ ((UINT4)pt[1] & 0xFF);
+ }
+ else {
+ unsigned char* pt = (unsigned char*)desc->aadSzBuf;
+ desc->aadSz = (((UINT4)pt[2] & 0xFF) << 24) |
+ (((UINT4)pt[3] & 0xFF) << 16) |
+ (((UINT4)pt[4] & 0xFF) << 8) |
+ ((UINT4)pt[5] & 0xFF);
+ }
+ }
+
+ /* get additional data buffers */
+ if (desc->aadSz > 0) {
+ sz = 0;
+ for (; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ if (sz < desc->aadSz) {
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->lastFifo = desc->idx;
+ desc->desc[desc->idx++] = CAAM_FIFO_L | CAAM_CLASS1 |
+ FIFOL_TYPE_AAD + buf->dataSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(buf->data);
+ sz += buf->dataSz;
+ }
+ else {
+ break;
+ }
+ }
+
+ /* flush AAD from FIFO and pad it to 16 byte block */
+ desc->desc[desc->lastFifo] |= FIFOL_TYPE_FC1;
+ }
+
+ /* find output buffers */
+ if (caamAesOutSz(desc, i) != Success) {
+ return SizeIsTooLarge;
+ }
+ }
+
+ /* handle alignment constraints on input */
+ if ((sz = caamAesInput(desc, &i, align, totalSz)) < 0) {
+ return TransferFailed;
+ }
+ totalSz += sz;
+
+ /* handle output buffers */
+ if (caamAesOutput(desc, &offset, sz) != Success) {
+ return TransferFailed;
+ }
+
+ /* store updated IV, if is last then set offset and final for MAC */
+ if ((desc->lastIdx == desc->DescriptorCount) && (offset == 0)) {
+ ivSz = 16;
+ if (desc->state == CAAM_ENC) {
+ ofst = 32 << 8; /* offset is in 15-8 bits */
+ }
+ else {
+ ofst = 0;
+ }
+ desc->desc[opIdx] |= CAAM_ALG_FINAL;
+ }
+ else {
+ /* if not final then store and use ctr and encrypted ctr from
+ context dword 2,3 and 4,5. Also store MAC and AAD info from
+ context dword 6. */
+ ivSz = 56;
+ ofst = 0;
+ }
+
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_STORE_CTX | CAAM_CLASS1 | ofst | ivSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical((Address)desc->iv);
+
+ if ((err = caamDoJob(desc)) != Success) {
+ return err;
+ }
+ state = CAAM_ALG_UPDATE;
+ } while (desc->lastIdx < desc->DescriptorCount || offset > 0);
+
+ /* flush output buffers */
+ for (i = desc->outputIdx; i < desc->lastIdx; i++) {
+ ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
+ }
+
+ /* handle case with IV (This is also the output of MAC with AES-CCM) */
+ if (ivIdx > 0) {
+ unsigned char* pt = (unsigned char*)desc->iv;
+ ASP_FlushCaches((Address)pt, ivSz);
+ for (i = 0; i < ivIdx; i++) {
+ memcpy((unsigned char*)iv[i]->data, pt, iv[i]->dataSz);
+ pt += iv[i]->dataSz;
+ ASP_FlushCaches(iv[i]->data, iv[i]->dataSz);
+ }
+ }
+
+ return Success;
+}
+
+
+/******************************************************************************
+ CAAM SHA Operations
+ ****************************************************************************/
+static int shaSize(struct DescStruct* desc)
+{
+ /* sanity check on dataSz for context */
+ switch (desc->type) {
+ case CAAM_MD5:
+ return CAAM_MD5_CTXSZ;
+
+ case CAAM_SHA:
+ return CAAM_SHA_CTXSZ;
+
+ case CAAM_SHA224:
+ return CAAM_SHA224_CTXSZ;
+
+ case CAAM_SHA256:
+ return CAAM_SHA256_CTXSZ;
+
+ case CAAM_SHA384:
+ return CAAM_SHA384_CTXSZ;
+
+ case CAAM_SHA512:
+ return CAAM_SHA512_CTXSZ;
+
+ default:
+ return 0;
+ }
+}
+
+/* SHA operations
+ * start: the index to start traversing through buffers. It's needed to allow
+ * for HMAC to reuse this code.
+ *
+ * return Success on success. All other return values are considered a fail
+ * case.
+ */
+static Error caamSha(struct DescStruct* desc, int start)
+{
+ struct buffer* ctx[3];
+ Error err;
+ UINT4 i;
+ int sz = 0;
+ int ctxIdx = 0;
+ int offset = 0;
+
+ int ctxSz = shaSize(desc);
+
+ /* get context */
+ for (i = start; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)desc->iv;
+
+ if (sz < ctxSz && sz < (MAX_CTX * sizeof(UINT4))) {
+ ctx[ctxIdx] = buf;
+ sz += buf->dataSz;
+
+ if (ctx[ctxIdx]->dataSz + offset > (MAX_CTX * sizeof(UINT4))) {
+ return SizeIsTooLarge;
+ }
+ memcpy((unsigned char*)&local[offset], (unsigned char*)ctx[ctxIdx]->data,
+ ctx[ctxIdx]->dataSz);
+ offset += ctx[ctxIdx]->dataSz;
+ ctxIdx++;
+ }
+ else {
+ break;
+ }
+ }
+ if (sz > ctxSz || ctxSz > (MAX_CTX * sizeof(UINT4))) {
+ return SizeIsTooLarge;
+ }
+
+ ASP_FlushCaches((Address)desc->iv, ctxSz);
+ /*Manage Context (current digest + 8 byte running message length)*/
+ if ((desc->state & CAAM_ALG_INIT) != CAAM_ALG_INIT) {
+ /* don't load into the class 2 context register on inti.
+ Found that loading in caused context to not get set. */
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = (CAAM_LOAD_CTX | CAAM_CLASS2) + ctxSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical((Address)desc->iv);
+ }
+
+ /* add operation command */
+ desc->desc[desc->idx++] = CAAM_OP | CAAM_CLASS2 | desc->state |
+ desc->type;
+
+ /* Check case where there is no input.
+ In all cases the FIFO Load should be flushed. */
+ if (i == desc->DescriptorCount) {
+ desc->lastFifo = desc->idx;
+ if (desc->idx + 1 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_FIFO_L | CAAM_CLASS2 |
+ FIFOL_TYPE_MSG | CAAM_IMM;
+ }
+
+ /* save index for looping over input */
+ desc->headIdx = desc->idx;
+ do {
+ desc->idx = desc->headIdx; /* reset for each loop */
+ if (i < desc->DescriptorCount) {
+ /* input must be a multiple of 64 bytes unless in final call */
+ if (((desc->state & CAAM_ALG_FINAL) == CAAM_ALG_FINAL)) {
+ if (caamAddIO(desc, (CAAM_FIFO_L | CAAM_CLASS2 |
+ FIFOL_TYPE_MSG), 0, 1, &i) < 0) {
+ return TooManyBuffers;
+ }
+ }
+ else {
+ if (caamAddIO(desc, (CAAM_FIFO_L | CAAM_CLASS2 |
+ FIFOL_TYPE_MSG), 0, 64, &i) < 0) {
+ return TooManyBuffers;
+ }
+ }
+ }
+
+ desc->desc[desc->lastFifo] |= FIFOL_TYPE_LC2;
+
+ /* set context out */
+ if (desc->idx + 2 > MAX_DESC_SZ) {
+ return TransferFailed;
+ }
+ desc->desc[desc->idx++] = CAAM_STORE_CTX | CAAM_CLASS2 + ctxSz;
+ desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->iv);
+
+ if ((err = caamDoJob(desc)) != Success) {
+ return err;
+ }
+ /* flush context output for each loop */
+ ASP_FlushCaches((Address)desc->iv, ctxSz);
+ } while (i < desc->DescriptorCount);
+
+ /* store context to buffers */
+ {
+ unsigned char* pt = (unsigned char*)desc->iv;
+ for (i = 0; i < ctxIdx; i++) {
+ memcpy((unsigned char*)ctx[i]->data, pt, ctx[i]->dataSz);
+ pt += ctx[i]->dataSz;
+ ASP_FlushCaches(ctx[i]->data, ctx[i]->dataSz);
+ }
+ }
+
+ return Success;
+}
+
+
+/******************************************************************************
+ CAAM TRNG Operations
+ ****************************************************************************/
+
+/* If Entropy is not ready then return Waiting */
+static Error caamRng(struct DescStruct* desc)
+{
+ int sz = 0;
+ int i;
+
+ Address reg; /* RTENT reg to read */
+ int ofst = sizeof(UINT4);
+
+
+ /* Check ENT_VAL bit to make sure entropy is ready */
+ if ((CAAM_READ(CAAM_RTMCTL) & CAAM_ENTVAL) !=
+ CAAM_ENTVAL) {
+ return Waiting;
+ }
+
+ /* check state of TRNG */
+ if ((CAAM_READ(CAAM_RTSTATUS) & 0x0000FFFF) > 0) {
+ return Failure;
+ }
+
+ /* read entropy from RTENT registers */
+ reg = CAAM_RTENT0;
+
+ for (i = 0; i < desc->DescriptorCount; i++) {
+ struct buffer* buf = &desc->buf[i];
+ unsigned char* local = (unsigned char*)buf->data;
+ sz = buf->dataSz;
+
+ while (sz > 3 && reg <= CAAM_RTENT11) {
+ *((UINT4*)local) = CAAM_READ(reg);
+ reg += ofst;
+ local += ofst;
+ sz -= ofst;
+ }
+
+ if (reg > CAAM_RTENT11 && sz > 0) {
+ return SizeIsTooLarge;
+ }
+
+ /* handle non word32 size amount left over */
+ if (sz > 0) {
+ UINT4 tmp = CAAM_READ(reg);
+ memcpy(local, (unsigned char*)&tmp, sz);
+ }
+
+ ASP_FlushCaches(buf->data, buf->dataSz);
+ }
+
+
+ /* read RTENT11 to trigger new entropy generation */
+ if (reg != CAAM_RTENT11) {
+ CAAM_READ(CAAM_RTENT11);
+ }
+
+ return Success;
+}
+
+
+/******************************************************************************
+ IODevice Start, Transfer and Finish Buffer
+ ****************************************************************************/
+/* args[0] holds the state such as encrypt/decrypt or init/update/final
+ * args[1] holds the ctx/key size
+ * args[2] holds the input size
+ * args[3] dependent on algo (such as AAD size with AES-CCM) */
+static Error caamTransferStart(IODeviceVector ioCaam,
+ Value type, const volatile Value args[4])
+{
+ struct CAAM_DEVICE* local = (struct CAAM_DEVICE*)ioCaam;
+ struct DescStruct* desc;
+
+ /* currently only one desc is available for use */
+ desc = &local->DescArray[0];
+
+ /* check if the desc is idle before using */
+ if (GetIORequestStatus((IORequest)desc) != IdleIORequest) {
+ return ResourceNotAvailable;
+ }
+
+ desc->idx = 0;
+ desc->output = 0;
+ desc->ctxOut = 0;
+ desc->outputIdx = 0;
+ desc->alignIdx = 0;
+ desc->lastFifo = 0;
+ desc->state = args[0];
+ desc->ctxSz = args[1];
+ desc->inputSz = args[2];
+ desc->aadSz = 0;
+ desc->desc[desc->idx++] = CAAM_HEAD; /* later will put size to header*/
+
+ switch (type) {
+ case CAAM_AESECB:
+ case CAAM_AESCBC:
+ if (desc->inputSz % 16 != 0) {
+ return ArgumentError;
+ }
+ /* fall through to break */
+ case CAAM_AESCTR:
+ break;
+
+ case CAAM_AESCCM:
+ memset((unsigned char*)desc->aadSzBuf, 0, sizeof(desc->aadSzBuf));
+ if (args[3] > 0) {
+ /* encode the length in */
+ if (args[3] <= 0xFEFF) {
+ unsigned char* pt = (unsigned char*)desc->aadSzBuf;
+ desc->aadSz = 2;
+ pt[0] = ((args[3] & 0xFF00) >> 8);
+ pt[1] = (args[3] & 0x00FF);
+ }
+ else if (args[3] <= 0xFFFFFFFF) {
+ unsigned char* pt = (unsigned char*)desc->aadSzBuf;
+ desc->aadSz = 6;
+ pt[0] = 0xFF; pt[1] = 0xFE;
+ pt[2] = ((args[3] & 0xFF000000) >> 24);
+ pt[3] = ((args[3] & 0x00FF0000) >> 16);
+ pt[4] = ((args[3] & 0x0000FF00) >> 8);
+ pt[5] = (args[3] & 0x000000FF);
+ }
+ }
+ break;
+
+ case CAAM_MD5:
+ case CAAM_SHA:
+ case CAAM_SHA224:
+ case CAAM_SHA256:
+ case CAAM_SHA384:
+ case CAAM_SHA512:
+ break;
+
+ case CAAM_BLOB_ENCAP:
+ case CAAM_BLOB_DECAP:
+ break;
+
+ case CAAM_ENTROPY:
+ break;
+
+ default:
+ /* unknown type */
+ return UsageNotSupported;
+ }
+
+ desc->DescriptorCount = 0;
+ desc->type = type;
+ desc->running = true;
+ StartIORequest((IORequest)desc);
+
+ /* For now only require READ permissions */
+ SetIORequestBufferPermissions((IORequest)desc, MEMORY_READ);
+ return Success;
+}
+
+
+static Error caamTransferBuffer(IODeviceVector TheIODeviceVector,
+ IORequest req, IODescriptor NewIODescriptor,
+ Address data, Address dataSz)
+{
+ struct DescStruct* desc = (struct DescStruct*)req;
+ Error err;
+
+ switch (desc->type) {
+ case CAAM_AESECB:
+ case CAAM_AESCTR:
+ case CAAM_AESCBC:
+ case CAAM_AESCCM:
+
+ case CAAM_MD5:
+ case CAAM_SHA:
+ case CAAM_SHA224:
+ case CAAM_SHA256:
+ case CAAM_SHA384:
+ case CAAM_SHA512:
+
+ case CAAM_BLOB_ENCAP:
+ case CAAM_BLOB_DECAP:
+ case CAAM_ENTROPY:
+ { /* set buffer for transfer finish */
+ struct buffer* buf;
+ if (desc->DescriptorCount >= MAX_BUF) {
+ return TooManyBuffers;
+ }
+ buf = &desc->buf[desc->DescriptorCount];
+ buf->data = data;
+ buf->dataSz = dataSz;
+ }
+ err = Success;
+ break;
+
+ default:
+ err = UsageNotSupported;
+ }
+
+ if (err != Success) {
+ desc->running = false;
+ DismissIORequest(req);
+ return err;
+ }
+
+ desc->DescriptorCount++;
+ return Success;
+}
+
+
+static Error caamTransferFinish(IODeviceVector ioCaam, IORequest req)
+{
+ struct DescStruct* desc = (struct DescStruct*)req;
+ Error ret;
+
+ /* construct desc */
+ switch (desc->type) {
+ case CAAM_AESECB:
+ case CAAM_AESCTR:
+ case CAAM_AESCBC:
+ ret = caamAes(desc);
+ break;
+
+ case CAAM_AESCCM:
+ ret = caamAead(desc);
+ break;
+
+ case CAAM_MD5:
+ case CAAM_SHA:
+ case CAAM_SHA224:
+ case CAAM_SHA256:
+ case CAAM_SHA384:
+ case CAAM_SHA512:
+ ret = caamSha(desc, 0);
+ break;
+
+ case CAAM_ENTROPY:
+ ret = caamRng(desc);
+ break;
+
+ case CAAM_BLOB_ENCAP:
+ case CAAM_BLOB_DECAP:
+ ret = caamBlob(desc);
+ break;
+
+ default:
+ ret = UsageNotSupported;
+ }
+
+ desc->running = false;
+ DismissIORequest(req);
+ return ret;
+}
+
+
+/******************************************************************************
+ IODevice Interrupt and Init
+ ****************************************************************************/
+
+static Error caamTransferWrite(IODeviceVector ioCaam,
+ IORequest req, Value dataSz, const volatile Value *data)
+{
+ DismissIORequest(req);
+ return UsageNotSupported;
+}
+
+
+static void caamTransferAbort(IODeviceVector ioCaam, IORequest req)
+{
+ DismissIORequest(req);
+}
+
+
+static void caamTransferRecall(IODeviceVector ioCaam, IODescriptor req)
+{
+
+}
+
+
+static void HandleInterrupt(Address id)
+{
+ struct CAAM_DEVICE* local = (struct CAAM_DEVICE*)id;
+ Value InterruptStatus = INTERRUPT_AtomicWrite(&local->InterruptStatus, 0);
+ int i;
+
+ /* Loop through descriptors and try to dismiss them */
+ for (i = 0; i < DESC_COUNT; i++) {
+ struct DescStruct* desc = &local->DescArray[i];
+ if (InterruptStatus & (1 << i)) {
+ desc->running = false;
+ if (GetIORequestStatus((IORequest)desc) == IORequestSuspended) {
+ ContinueIORequest((IORequest)desc);
+ }
+ else {
+ DismissIORequest((IORequest)desc);
+ }
+ }
+ }
+}
+
+
+static Error caamCreate(IODeviceVector ioCaam)
+{
+ return Success;
+}
+
+
+void InitCAAM(void)
+{
+ /* get IO vector and set it up */
+ IODeviceVector ioCaam = &caam.caamVector;
+ unsigned int reg;
+ int i;
+ Error ret;
+
+
+ ioCaam->Create = &caamCreate;
+ ioCaam->ReadRegister = &caamReadRegister;
+ ioCaam->WriteRegister = &caamWriteRegister;
+
+ ioCaam->TransferStart = &caamTransferStart;
+ ioCaam->TransferBuffer = &caamTransferBuffer;
+ ioCaam->TransferWrite = &caamTransferWrite;
+ ioCaam->TransferFinish = &caamTransferFinish;
+ ioCaam->TransferAbort = &caamTransferAbort;
+ ioCaam->TransferRecall = &caamTransferRecall;
+#ifdef HARDWARE_CACHE_COHERENCY
+ ioCaam->IOSynchronizationNotRequired = 1;
+#endif
+
+ RegisterIODeviceVector(ioCaam, DRIVER_NAME);
+ RequestIOTerminationTask(ioCaam, 10);
+
+ /* Initialize descriptors */
+ for (i = 0; i < BUFFER_COUNT; i++) {
+ InitializeIODescriptor(ioCaam, &caam.IODescriptorArray[i]);
+ }
+
+ /* Initialize Descriptors */
+ for (i = 0; i < DESC_COUNT; i++) {
+ InitializeIORequest(ioCaam, &caam.DescArray[i].TheIORequest,
+ IOREQUEST_STANDARD);
+ caam.DescArray[i].running = false;
+ caam.DescArray[i].caam = &caam;
+ }
+
+
+ /* call interrupt to make IORequests available */
+ caam.InterruptStatus = 0;
+ INTERRUPT_InitCall(&caam.HandleInterruptCall,
+ &HandleInterrupt, "Start up CAAM IORequest");
+
+ /* set clock speed for CAAM. Setting it here to allow for restricting
+ access */
+ #define REGS_CCM_BASE (0xf20c4000)
+ #define HW_CCM_CCGR0_ADDR (0xf20c4068)
+ #define CG(x) (3 << (x*2))
+
+ reg = CG(6) | CG(5) | CG(4);
+ *(volatile unsigned int*)HW_CCM_CCGR0_ADDR =
+ *(volatile unsigned int*)HW_CCM_CCGR0_ADDR | reg;
+
+ /* set up job ring */
+
+ /* @TODO create partition in physical memory for job rings
+ current partition security is set to the default */
+ for (i = 1; i < CAAM_PAGE_MAX; i++) {
+ ret = caamCreatePartition(i, i);
+ if (ret == 0) {
+ break;
+ }
+
+ if (ret != MemoryMapMayNotBeEmpty) {
+ INTERRUPT_Panic();
+ }
+ }
+
+ if (ret != 0) {
+ INTERRUPT_Panic();
+ }
+
+ caam.ring.page = i;
+ caam.ring.JobIn = (CAAM_PAGE + (i << 12));
+ caam.ring.JobOut = caam.ring.JobIn + 16;
+ caam.ring.Desc = caam.ring.JobOut + 16;
+
+ /* set physical address of job rings */
+ CAAM_WRITE(CAAM_IRBAR0, caam.ring.JobIn ^ 0xF0000000);
+ CAAM_WRITE(CAAM_ORBAR0, caam.ring.JobOut ^ 0xF0000000);
+
+ /* Initialize job ring sizes to 1 */
+ CAAM_WRITE(CAAM_IRSR0, 1);
+ CAAM_WRITE(CAAM_ORSR0, 1);
+
+ /* set DECO watchdog to time out and flush jobs that cause the DECO to hang */
+ CAAM_WRITE((CAAM_BASE | 0x0004), CAAM_READ(CAAM_BASE | 0x0004) | 0x40000000);
+
+ /* start up RNG if not already started */
+ if (caamInitRng(&caam) != 0) {
+ INTERRUPT_Panic();
+ }
+}
+
+void (*__ghsentry_bspuserinit_InitCAAM)(void) = &InitCAAM;
+
+#endif /* INTEGRITY */