#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#if defined(__INTEGRITY) || defined(INTEGRITY)
#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)
#define MAX_DESC_SZ 64
#define ALIGN_BUF 16
#define MAX_CTX 18
#define MIN_READ_REG 0xF2100000
#define MAX_READ_REG 0XF2110000
struct JobRing {
Address JobIn;
Address JobOut;
Address Desc;
Value page;
};
struct buffer {
Address data;
Address dataSz;
};
struct DescStruct {
struct IORequestStruct TheIORequest;
struct CAAM_DEVICE* caam;
struct buffer buf[MAX_BUF];
UINT4 desc[MAX_DESC_SZ];
UINT4 aadSzBuf[4];
UINT4 alignBuf[ALIGN_BUF];
UINT4 iv[MAX_CTX];
UINT4 ctxBuf[MAX_CTX];
Address output;
Address ctxOut;
Value alignIdx;
Value idx;
Value headIdx;
Value lastIdx;
Value outputIdx;
Value inputSz;
Value ctxSz;
Value aadSz;
Value lastFifo;
Value type;
Value state;
Value DescriptorCount;
Boolean running;
};
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;
static Error caamReset(void)
{
int t = 100000;
CAAM_WRITE(CAAM_BASE | 0x1054, CAAM_READ(CAAM_BASE | 0x1054) | 1);
CAAM_WRITE(CAAM_BASE | 0x106C, 1);
while (t > 0 && ((CAAM_READ(CAAM_BASE | 0x104C) & 0x4) == 0x4)) t--;
if (t == 0) {
return NotRestartable;
}
t = 100000;
CAAM_WRITE(CAAM_BASE | 0x106C, 1);
while (t > 0 && ((CAAM_READ(CAAM_BASE | 0x106C) & 1) == 1)) t--;
if (t == 0) {
return NotRestartable;
}
CAAM_WRITE(CAAM_BASE | 0x0004, 0x90000000);
return Success;
}
static Error caamCreatePartition(unsigned char page, unsigned char par)
{
if ((CAAM_READ(CAAM_BASE | 0x1FBC) & (0x3 << (par * 2))) > 0) {
return MemoryMapMayNotBeEmpty;
}
CAAM_WRITE(CAAM_BASE | (0x1108 + (par * 16)), 0xF);
CAAM_WRITE(CAAM_BASE | (0x110C + (par * 16)), 0xF);
CAAM_WRITE(CAAM_BASE | (0x1104 + (par * 16)), 0xFF);
CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | 0x5);
while ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x0000C000) > 0 &&
(CAAM_READ(CAAM_BASE | 0x10FC) & 0x00003000) == 0) {
}
if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x000000C0) == 0xC0) {
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) {
return MemoryMapMayNotBeEmpty;
}
}
else {
if ((CAAM_READ(CAAM_BASE | 0x10FC) & 0x000000C0) != 0) {
return MemoryMapMayNotBeEmpty;
}
}
CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | (par << 8) | 0x1);
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;
}
CAAM_WRITE(CAAM_BASE | 0x10F4, (page << 16) | 0x5);
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) {
return MemoryOperationNotPerformed;
}
return Success;
}
static Error caamGetJob(struct CAAM_DEVICE* dev, UINT4* status)
{
UINT4 reg = CAAM_READ(CAAM_BASE | 0x1044);
if (status) {
*status = 0;
}
if (((reg & 0xF0000000) == 0x20000000) ||
((reg & 0xF0000000) == 0x40000000)||
((reg & 0xF0000000) == 0x60000000)) {
if ((reg & 0x0000000F) > 0) {
*status = reg;
return Failure;
}
}
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;
}
CAAM_WRITE(CAAM_BASE | 0x1034, 1);
}
else {
if ((CAAM_READ(CAAM_BASE | 0x0FD4) & 0x00000002) == 2
&& (CAAM_READ(CAAM_BASE | 0x0FD4) & 0x00000001) == 0) {
return NoActivityReady;
}
return Waiting;
}
return Success;
}
static int caamInitRng(struct CAAM_DEVICE* dev)
{
UINT4 reg, status;
int ret = 0;
CAAM_WRITE(CAAM_RTMCTL, CAAM_PRGM);
CAAM_WRITE(CAAM_RTMCTL, CAAM_READ(CAAM_RTMCTL) | 0x40);
CAAM_WRITE(CAAM_RTMCTL, CAAM_READ(CAAM_RTMCTL) | CAAM_TRNG);
CAAM_WRITE(CAAM_RTSDCTL, (CAAM_ENT_DLY << 16) | 0x09C4);
CAAM_WRITE(CAAM_RTFRQMIN, CAAM_ENT_DLY >> 1);
CAAM_WRITE(CAAM_RTFRQMAX, CAAM_ENT_DLY << 3);
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);
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;
CAAM_WRITE(CAAM_IRJAR0, 0x00000001);
}
else {
return Waiting;
}
do {
ret = caamGetJob(dev, &status);
} while (ret == Waiting);
return ret;
}
static Error caamDoJob(struct DescStruct* desc)
{
Error ret;
UINT4 status;
desc->desc[0] &= 0xFFFFFF80;
desc->desc[0] += desc->idx;
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;
CAAM_WRITE(CAAM_IRJAR0, 0x00000001);
}
else {
return Waiting;
}
do {
ret = caamGetJob(desc->caam, &status);
} while (ret == Waiting);
if (status != 0 || ret != Success) {
#if 0 #endif
caamReset();
return ret;
}
return Success;
}
static int caamAddIO(struct DescStruct* desc, UINT4 options, UINT4 sz,
UINT4 align, UINT4* idx)
{
int i, outSz = 0;
if (align == 0) {
return -1;
}
for (i = *idx; i < desc->DescriptorCount; i++) {
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) {
int tmpSz = dataSz % align;
int add = (tmpSz < (align - desc->alignIdx)) ? tmpSz :
align - desc->alignIdx;
unsigned char* local = (unsigned char*)desc->alignBuf;
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);
if (desc->alignIdx == align) {
desc->alignIdx = 0;
i++;
break;
}
}
}
*idx = i;
return outSz;
}
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)
{
return OperationNotAllowedOnTheUniversalIODevice;
}
static Error caamBlob(struct DescStruct* desc)
{
Error err;
UINT4 keyType = 0x00000C08;
UINT4 i = 0;
int sz = 0, ret;
if (desc->idx + 3 > MAX_DESC_SZ) {
return Failure;
}
desc->desc[desc->idx++] = (CAAM_LOAD_CTX | CAAM_CLASS2 | CAAM_IMM | keyType);
if (i < desc->DescriptorCount) {
UINT4* pt;
Address data = desc->buf[i].data;
Address dataSz = desc->buf[i].dataSz;
pt = (UINT4*)data;
if (dataSz < 8) {
return TooManyBuffers;
}
desc->desc[desc->idx++] = pt[0];
desc->desc[desc->idx++] = pt[1];
}
while (sz < desc->inputSz && i < desc->DescriptorCount) {
ret = caamAddIO(desc, CAAM_SEQI, desc->inputSz - sz, 1, &i);
if (ret < 0) {
return TooManyBuffers;
}
sz += ret;
}
desc->outputIdx = i;
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;
}
for (i = desc->outputIdx; i < desc->DescriptorCount; i++) {
ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
}
return Success;
}
static int caamAesInput(struct DescStruct* desc, UINT4* idx, int align,
UINT4 totalSz)
{
int sz;
UINT4 i = *idx;
if (desc->alignIdx > 0) {
sz = desc->alignIdx;
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) {
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;
}
static Error caamAesOutput(struct DescStruct* desc, int* ofst, UINT4 inputSz)
{
int offset = *ofst;
if (desc->output != 0 && offset > 0 && inputSz > 0) {
UINT4 addSz;
addSz = (inputSz >= offset) ? offset : inputSz;
inputSz -= addSz;
desc->desc[desc->idx++] = CAAM_FIFO_S | FIFOS_TYPE_MSG + addSz;
if (inputSz > 0) {
desc->desc[desc->idx - 1] |= CAAM_FIFOS_CONT;
}
desc->desc[desc->idx++] = BSP_VirtualToPhysical(desc->output);
if (addSz == offset) {
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) {
desc->desc[desc->idx - 1] |= CAAM_FIFOS_CONT;
}
desc->desc[desc->idx++] = BSP_VirtualToPhysical(buf->data);
}
else {
break;
}
}
*ofst = offset;
return Success;
}
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;
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;
}
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;
}
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;
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) {
return SizeIsTooLarge;
}
if (ctxSz > (MAX_CTX * sizeof(UINT4)) - 16) {
return ArgumentError;
}
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);
switch (desc->type) {
case CAAM_AESECB:
break;
case CAAM_AESCTR:
ofst = 0x00001000;
case CAAM_AESCBC:
{
int maxSz = 16;
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) {
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;
}
if (desc->idx + 1 > MAX_DESC_SZ) {
return TransferFailed;
}
desc->desc[desc->idx++] = CAAM_OP | CAAM_CLASS1 | desc->type |
CAAM_ALG_UPDATE | desc->state;
if (caamAesOutSz(desc, i) != Success) {
return SizeIsTooLarge;
}
if (desc->type == CAAM_AESCBC || desc->type == CAAM_AESECB) {
align = 16;
}
desc->headIdx = desc->idx;
desc->output = 0;
offset = 0;
do {
desc->idx = desc->headIdx;
if ((sz = caamAesInput(desc, &i, align, totalSz)) < 0) {
return TransferFailed;
}
totalSz += sz;
if (caamAesOutput(desc, &offset, sz) != Success) {
return TransferFailed;
}
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);
for (i = desc->outputIdx; i < desc->lastIdx; i++) {
ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
}
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;
}
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;
int ctxSz = desc->ctxSz;
int align = 16;
int opIdx;
if (desc->state != CAAM_ENC && desc->state != CAAM_DEC) {
return IllegalStatusNumber;
}
if (ctxSz != 16 && ctxSz != 24 && ctxSz != 32) {
return ArgumentError;
}
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;
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) {
return SizeIsTooLarge;
}
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;
do {
desc->idx = desc->headIdx;
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;
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) {
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;
}
if ((state & CAAM_ALG_INIT) == CAAM_ALG_INIT) {
if ((desc->type == CAAM_AESCCM) && (desc->aadSz > 0)) {
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);
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);
}
}
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;
}
}
desc->desc[desc->lastFifo] |= FIFOL_TYPE_FC1;
}
if (caamAesOutSz(desc, i) != Success) {
return SizeIsTooLarge;
}
}
if ((sz = caamAesInput(desc, &i, align, totalSz)) < 0) {
return TransferFailed;
}
totalSz += sz;
if (caamAesOutput(desc, &offset, sz) != Success) {
return TransferFailed;
}
if ((desc->lastIdx == desc->DescriptorCount) && (offset == 0)) {
ivSz = 16;
if (desc->state == CAAM_ENC) {
ofst = 32 << 8;
}
else {
ofst = 0;
}
desc->desc[opIdx] |= CAAM_ALG_FINAL;
}
else {
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);
for (i = desc->outputIdx; i < desc->lastIdx; i++) {
ASP_FlushCaches(desc->buf[i].data, desc->buf[i].dataSz);
}
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;
}
static int shaSize(struct DescStruct* desc)
{
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;
}
}
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);
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);
if ((desc->state & CAAM_ALG_INIT) != CAAM_ALG_INIT) {
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);
}
desc->desc[desc->idx++] = CAAM_OP | CAAM_CLASS2 | desc->state |
desc->type;
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;
}
desc->headIdx = desc->idx;
do {
desc->idx = desc->headIdx;
if (i < desc->DescriptorCount) {
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;
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;
}
ASP_FlushCaches((Address)desc->iv, ctxSz);
} while (i < desc->DescriptorCount);
{
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;
}
static Error caamRng(struct DescStruct* desc)
{
int sz = 0;
int i;
Address reg;
int ofst = sizeof(UINT4);
if ((CAAM_READ(CAAM_RTMCTL) & CAAM_ENTVAL) !=
CAAM_ENTVAL) {
return Waiting;
}
if ((CAAM_READ(CAAM_RTSTATUS) & 0x0000FFFF) > 0) {
return Failure;
}
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;
}
if (sz > 0) {
UINT4 tmp = CAAM_READ(reg);
memcpy(local, (unsigned char*)&tmp, sz);
}
ASP_FlushCaches(buf->data, buf->dataSz);
}
if (reg != CAAM_RTENT11) {
CAAM_READ(CAAM_RTENT11);
}
return Success;
}
static Error caamTransferStart(IODeviceVector ioCaam,
Value type, const volatile Value args[4])
{
struct CAAM_DEVICE* local = (struct CAAM_DEVICE*)ioCaam;
struct DescStruct* desc;
desc = &local->DescArray[0];
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;
switch (type) {
case CAAM_AESECB:
case CAAM_AESCBC:
if (desc->inputSz % 16 != 0) {
return ArgumentError;
}
case CAAM_AESCTR:
break;
case CAAM_AESCCM:
memset((unsigned char*)desc->aadSzBuf, 0, sizeof(desc->aadSzBuf));
if (args[3] > 0) {
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:
return UsageNotSupported;
}
desc->DescriptorCount = 0;
desc->type = type;
desc->running = true;
StartIORequest((IORequest)desc);
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:
{
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;
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;
}
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;
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)
{
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);
for (i = 0; i < BUFFER_COUNT; i++) {
InitializeIODescriptor(ioCaam, &caam.IODescriptorArray[i]);
}
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;
}
caam.InterruptStatus = 0;
INTERRUPT_InitCall(&caam.HandleInterruptCall,
&HandleInterrupt, "Start up CAAM IORequest");
#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;
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;
CAAM_WRITE(CAAM_IRBAR0, caam.ring.JobIn ^ 0xF0000000);
CAAM_WRITE(CAAM_ORBAR0, caam.ring.JobOut ^ 0xF0000000);
CAAM_WRITE(CAAM_IRSR0, 1);
CAAM_WRITE(CAAM_ORSR0, 1);
CAAM_WRITE((CAAM_BASE | 0x0004), CAAM_READ(CAAM_BASE | 0x0004) | 0x40000000);
if (caamInitRng(&caam) != 0) {
INTERRUPT_Panic();
}
}
void (*__ghsentry_bspuserinit_InitCAAM)(void) = &InitCAAM;
#endif