iBoot/drivers/apple/cdma/cdma_aes.c

300 lines
9.5 KiB
C

/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <drivers/aes.h>
#include <drivers/dma.h>
#include <platform.h>
#include <platform/int.h>
#include <platform/soc/hwdmachannels.h>
#include <platform/soc/hwisr.h>
#include <platform/soc/hwclocks.h>
#include <sys/task.h>
#include "cdma.h"
#define CDMA_AES_UID_MASK (1 << 0)
#define CDMA_AES_UID_PLUS_MASK (1 << 1) // v4+
static int dma_setup_channel(u_int32_t cmd_dir, int dma_channel,
u_int32_t mem_addr, u_int32_t dev_addr, u_int32_t length,
u_int32_t word_size, u_int32_t burst_size, bool filter);
static void dma_wait_channel_halted(int dma_channel);
static void cdma_dump_aes_channel(int channel);
static struct cdma_command cdma_aes_command[4] __attribute__((aligned(32)));
void
platform_disable_keys(u_int32_t gid, u_int32_t uid)
{
u_int32_t keydis = ((gid << 1) | (uid & CDMA_AES_UID_MASK)) & 0x0000FFFF;
cdma_clock_enable(DMA_MEMORY_TX, true);
rCDMA_FILTER_KEYDIS = keydis;
cdma_clock_enable(DMA_MEMORY_TX, false);
// SKG disable feature is broken.
// To workaroud, UID+ will tag along UID. If UID is disabled, we assume UID+ is also disabled
#if CDMA_VERSION >= 4
if (uid & CDMA_AES_UID_MASK) {
rCDMA_FILTER_SKGCNTL = CDMA_FSKGCNTL_SKGDIS;
}
#endif /* CDMA_VERSION >= 4 */
}
int
aes_hw_crypto_cmd(u_int32_t cmd, void *src, void *dst, size_t len, u_int32_t opts, const void *key, void *iv)
{
u_int32_t cbc_val, dec_val, kl_val, ks_val, kx_val;
clock_gate(CLK_CDMA, true);
cdma_clock_enable(DMA_MEMORY_TX, true);
cdma_clock_enable(DMA_MEMORY_RX, true);
/* clip length to multiple of 16 bytes */
if (len & 0xf)
goto fail;
switch (cmd & AES_CMD_MODE_MASK) {
case AES_CMD_ECB : cbc_val = 0; break;
case AES_CMD_CBC : cbc_val = 1; break;
default : goto fail;
}
switch (cmd & AES_CMD_DIR_MASK) {
case AES_CMD_ENC : dec_val = 1; break;
case AES_CMD_DEC : dec_val = 0; break;
default : goto fail;
}
/* Set the IV */
if (iv != 0) {
rCDMA_FILTER_IVR0(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)iv)[0];
rCDMA_FILTER_IVR1(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)iv)[1];
rCDMA_FILTER_IVR2(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)iv)[2];
rCDMA_FILTER_IVR3(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)iv)[3];
} else {
rCDMA_FILTER_IVR0(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_IVR1(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_IVR2(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_IVR3(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
}
/* Get the key length */
kl_val = 0;
switch (opts & AES_KEY_SIZE_MASK) {
case AES_KEY_SIZE_128 : kl_val = 0; break;
case AES_KEY_SIZE_192 : kl_val = 1; break;
case AES_KEY_SIZE_256 : kl_val = 2; break;
default : goto fail;
}
/* Set the key */
ks_val = 0;
kx_val = 0;
switch (opts & AES_KEY_TYPE_MASK) {
case AES_KEY_TYPE_USER :
ks_val = 1;
switch (kl_val) {
default:
rCDMA_FILTER_KBR7(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[7];
rCDMA_FILTER_KBR6(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[6];
case 1:
rCDMA_FILTER_KBR5(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[5];
rCDMA_FILTER_KBR4(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[4];
case 0:
rCDMA_FILTER_KBR3(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[3];
rCDMA_FILTER_KBR2(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[2];
rCDMA_FILTER_KBR1(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[1];
rCDMA_FILTER_KBR0(CDMA_FILTER_CONTEXT_NORMAL_M2M) = ((u_int32_t *)key)[0];
}
case AES_KEY_TYPE_UID0 : kx_val = 0; break;
case AES_KEY_TYPE_GID0 : kx_val = 1; break;
case AES_KEY_TYPE_GID1 : kx_val = 2; break;
}
/* make sure the requested key is available */
if ((0 == ks_val) && (rCDMA_FILTER_KEYDIS & (1 << kx_val))) {
dprintf(DEBUG_INFO, "CDMA: requested AES key has been disabled\n");
return -1;
}
rCDMA_FILTER_CSR(CDMA_FILTER_CONTEXT_NORMAL_M2M) =
((DMA_MEMORY_TX << 8) | // DMA Channel
(dec_val << 16) | // decode / encode
(cbc_val << 17) | // cbc / ecb
(kl_val << 18) | // key length
(ks_val << 20) | // key select: fixed / variable
(kx_val << 21)); // key index
if (0 != dma_setup_channel(DMA_CMD_DIR_MEM, DMA_MEMORY_TX, (u_int32_t)src, 0, len, 0, 0, true))
goto fail;
if (0 != dma_setup_channel(DMA_CMD_DIR_MEM, DMA_MEMORY_RX, (u_int32_t)dst, 0, len, 0, 0, false))
goto fail;
platform_cache_operation(CACHE_CLEAN, src, len);
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, dst, len);
/* Start the channel */
rCDMA_CHANNEL_CSR(DMA_MEMORY_TX) |= (1 << 0);
rCDMA_CHANNEL_CSR(DMA_MEMORY_RX) |= (1 << 0);
/* Wait for both channels to halt (this ensures both are marked idle) */
dma_wait_channel_halted(DMA_MEMORY_TX);
dma_wait_channel_halted(DMA_MEMORY_RX);
#if CDMA_IS_INCOHERENT
/* If core did any speculative prefetching during DMA, invalidate the cache lines again */
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE, dst, len);
#endif
/* Clear user key from the registers */
if (ks_val != 0) {
rCDMA_FILTER_KBR0(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR1(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR2(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR3(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR4(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR5(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR6(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
rCDMA_FILTER_KBR7(CDMA_FILTER_CONTEXT_NORMAL_M2M) = 0;
}
cdma_clock_enable(DMA_MEMORY_TX, false);
cdma_clock_enable(DMA_MEMORY_RX, false);
return 0;
fail:
panic("CDMA AES: bad arguments");
}
static int
dma_setup_channel(u_int32_t cmd_dir, int dma_channel,
u_int32_t mem_addr, u_int32_t dev_addr, u_int32_t length,
u_int32_t word_size, u_int32_t burst_size, bool filter)
{
struct cdma_command *cdma_cmd;
u_int32_t dw_val, dbs_val, dcr_val, ccache_val = 0;
int result = -1;
/* must always abort the channel before configuring it */
rCDMA_CHANNEL_CSR(dma_channel) = CDMA_CSR_ABT;
cdma_cmd = &cdma_aes_command[(dma_channel * 2) - 2];
cdma_cmd->ctrl = (filter ? CDMA_CTRL_FE | CDMA_CTRL_FR : 0) | CDMA_CTRL_BAR | CDMA_CTRL_CMD_MEM;
#if (CDMA_VERSION == 3) || (CDMA_VERSION == 4)
ccache_val = CDMA_CTRL_CACHE(CDMA_CACHE_CACHE);
cdma_cmd->ctrl |= CDMA_CTRL_CACHE(CDMA_CACHE_CACHE);
#elif CDMA_VERSION >= 5
ccache_val = CDMA_CTRL_CACHE(CDMA_CACHE_BUFFER | CDMA_CACHE_CACHE | CDMA_CACHE_WALLOC);
cdma_cmd->ctrl |= CDMA_CTRL_CACHE(CDMA_CACHE_BUFFER | CDMA_CACHE_CACHE);
if (dma_channel == DMA_MEMORY_TX)
cdma_cmd->ctrl |= CDMA_CTRL_CACHE(CDMA_CACHE_WALLOC);
else
cdma_cmd->ctrl |= CDMA_CTRL_CACHE(CDMA_CACHE_RALLOC);
#endif
cdma_cmd->addr = mem_addr;
cdma_cmd->length = length;
/* terminate the chain */
cdma_cmd->nxt = (u_int32_t)(cdma_cmd + 1);
(cdma_cmd + 1)->ctrl = CDMA_CTRL_CMD_HLT;
if (cmd_dir == DMA_CMD_DIR_MEM) {
dcr_val = CDMA_CSR_MTM;
} else {
dcr_val = 0; /* ! CDMA_CSR_MTM */
switch (word_size) {
case 1 : dw_val = 0; break;
case 2 : dw_val = 1; break;
case 4 : dw_val = 2; break;
default : goto exit; break;
}
switch (burst_size) {
case 1 : dbs_val = 0; break;
case 2 : dbs_val = 1; break;
case 4 : dbs_val = 2; break;
case 8 : dbs_val = 3; break;
case 16 : dbs_val = 4; break;
case 32 : dbs_val = 5; break;
default : goto exit; break;
}
rCDMA_CHANNEL_DCR(dma_channel) =
((cmd_dir == DMA_CMD_DIR_TX) ? CDMA_DCR_DTD_TX : CDMA_DCR_DTD_RX) |
(dw_val << 2) |
(dbs_val << 4);
rCDMA_CHANNEL_DAR(dma_channel) = dev_addr;
}
platform_cache_operation(CACHE_CLEAN , cdma_cmd, sizeof(struct cdma_command));
rCDMA_CHANNEL_DBR(dma_channel) = length;
rCDMA_CHANNEL_CAR(dma_channel) = (u_int32_t)cdma_cmd;
/* Clear channel status and configure */
rCDMA_CHANNEL_CSR(dma_channel) = CDMA_CSR_CIR | CDMA_CSR_HIR | CDMA_CSR_ERR | (filter << 8) | dcr_val | ccache_val;
result = 0;
exit:
return result;
}
static void
dma_wait_channel_halted(int dma_channel)
{
while (CDMA_CSR_RUN(rCDMA_CHANNEL_CSR(dma_channel)) == CDMA_CSR_RUN_RUNNING)
task_yield();
}
static void
cdma_dump_aes_channel(int channel)
{
int filter;
bool clock_state;
clock_state = cdma_clock_enable(channel, true);
printf("CDMA: CSR 0x%08x DCR 0x%08x DAR 0x%08x DBR 0x%08x MAR 0x%08x CAR 0x%08x\n",
rCDMA_CHANNEL_CSR(channel), rCDMA_CHANNEL_DCR(channel), rCDMA_CHANNEL_DAR(channel),
rCDMA_CHANNEL_DBR(channel), rCDMA_CHANNEL_MAR(channel), rCDMA_CHANNEL_CAR(channel));
printf("CDMA: CMD_NXT 0x%08x CMD_CMD 0x%08x CMD_ADDR 0x%08x CMD_LEN 0x%08x\n",
rCDMA_CHANNEL_CMND0(channel), rCDMA_CHANNEL_CMND1(channel), rCDMA_CHANNEL_CMND2(channel),
rCDMA_CHANNEL_CMND3(channel));
filter = (rCDMA_CHANNEL_CSR(channel) >> 8) & 0xff;
if (filter > 0) {
printf("FILT: CSR 0x%08x\n", rCDMA_FILTER_CSR(filter));
printf("FILT: IV 0x%08x 0x%08x 0x%08x 0x%08x\n",
rCDMA_FILTER_IVR0(filter),
rCDMA_FILTER_IVR1(filter),
rCDMA_FILTER_IVR2(filter),
rCDMA_FILTER_IVR3(filter));
printf("FILT: KEY 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
rCDMA_FILTER_KBR0(filter),
rCDMA_FILTER_KBR1(filter),
rCDMA_FILTER_KBR2(filter),
rCDMA_FILTER_KBR3(filter),
rCDMA_FILTER_KBR4(filter),
rCDMA_FILTER_KBR5(filter),
rCDMA_FILTER_KBR6(filter),
rCDMA_FILTER_KBR7(filter));
}
cdma_clock_enable(channel, clock_state);
}