unplugged-kernel/drivers/mmc/host/mediatek/ComboA/mt6885/mtk_hw_fde.c

608 lines
13 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/hie.h>
#include "mtk_secure_api.h"
#include <mmc/core/queue.h>
/* map from AES Spec */
enum {
MSDC_CRYPTO_XTS_AES = 4,
MSDC_CRYPTO_AES_CBC_ESSIV = 9,
MSDC_CRYPTO_BITLOCKER = 17,
MSDC_CRYPTO_AES_ECB = 0,
MSDC_CRYPTO_AES_CBC = 1,
MSDC_CRYPTO_AES_CTR = 2,
MSDC_CRYPTO_AES_CBC_MAC = 3,
} aes_mode;
enum {
BIT_128 = 0,
BIT_192 = 1,
BIT_256 = 2,
BIT_0 = 4,
};
#if defined(CONFIG_MTK_HW_FDE)
static inline bool check_fde_mode(struct request *req,
unsigned int *key_idx)
{
if (req->bio && req->bio->bi_hw_fde) {
if (key_idx)
*key_idx = req->bio->bi_key_idx;
return true;
}
return false;
}
#else
static inline bool check_fde_mode(struct request *req,
unsigned int *key_idx)
{
return false;
}
#endif
static inline bool check_fbe_mode(struct request *req)
{
if (hie_request_crypted(req))
return true;
return false;
}
static void msdc_enable_crypto(struct msdc_host *host)
{
void __iomem *base = host->base;
/* enable AES path by clr bypass bit */
MSDC_CLR_BIT32(EMMC52_AES_SWST, EMMC52_AES_BYPASS);
}
static void msdc_disable_crypto(struct msdc_host *host)
{
void __iomem *base = host->base;
/* disable AES path by set bypass bit */
MSDC_SET_BIT32(EMMC52_AES_SWST, EMMC52_AES_BYPASS);
}
#ifdef CONFIG_MTK_EMMC_HW_CQ
/* HCI crypto start */
/* crypto context fields in cmdq data command task descriptor */
#define DATA_UNIT_NUM(x) (((u64)(x) & 0xFFFFFFFF) << 0)
#define CRYPTO_CONFIG_INDEX(x) (((u64)(x) & 0xFF) << 32)
#define CRYPTO_ENABLE(x) (((u64)(x) & 0x1) << 47)
union cqhci_cpt_capx {
u32 capx_raw;
struct {
u8 alg_id;
u8 du_size;
u8 key_size;
u8 resv;
} capx;
};
/* multi_host/resv1/vsb/resv2 don't use,
*just for use the same structure as UFS
*/
union cqhci_cap_cfg {
u32 cfgx_raw[32];
struct {
u32 key[16];
u8 du_size;
u8 cap_id;
u16 resv0:15;
u16 cfg_en:1;
u8 mu1ti_host;
u8 resv1;
u16 vsb;
u32 resv2[14];
} cfgx;
};
struct cqhci_crypto {
u32 cfg_id;
u32 cap_id;
union cqhci_cpt_cap cap;
union cqhci_cpt_capx capx;
union cqhci_cap_cfg cfg;
};
/* capability id */
enum {
CQHCI_CRYPTO_AES_XTS_128 = 0,
CQHCI_CRYPTO_AES_XTS_256 = 1,
CQHCI_CRYPTO_BITLOCKER_AES_CBC_128 = 2,
CQHCI_CRYPTO_BITLOCKER_AES_CBC_256 = 3,
CQHCI_CRYPTO_AES_ECB_128 = 4,
CQHCI_CRYPTO_AES_ECB_192 = 5,
CQHCI_CRYPTO_AES_ECB_256 = 6,
CQHCI_CRYPTO_ESSIV_AES_CBC_128 = 7,
CQHCI_CRYPTO_ESSIV_AES_CBC_192 = 8,
CQHCI_CRYPTO_ESSIV_AES_CBC_256 = 9,
};
/* data unit size */
enum {
CQHCI_CRYPTO_DU_SIZE_512B = 0,
};
static int msdc_cqhci_fde_init(struct msdc_host *host,
unsigned int key_idx)
{
if ((!host->is_crypto_init ||
(host->key_idx != key_idx))) {
/* cqhci fde init */
mt_secure_call(MTK_SIP_KERNEL_HW_FDE_MSDC_CTL,
(1 << 5), 4, 1, 0);
host->is_crypto_init = true;
host->key_idx = key_idx;
}
return 0;
}
static int msdc_cqhci_fbe_init(struct msdc_host *host,
struct request *req)
{
int err = 0;
if (!host->is_crypto_init) {
/* fbe init */
mt_secure_call(MTK_SIP_KERNEL_HW_FDE_MSDC_CTL,
(1 << 0), 4, 1, 0);
host->is_crypto_init = true;
}
if (rq_data_dir(req) == WRITE)
err = hie_encrypt(msdc_hie_get_dev(),
req, host);
else
err = hie_decrypt(msdc_hie_get_dev(),
req, host);
if (err) {
err = -EIO;
ERR_MSG("%s %d: req: %p, err %d\n",
__func__, __LINE__, req, err);
WARN_ON(1);
}
return err;
}
static int msdc_cqhci_crypto_cfg(struct mmc_host *mmc,
struct mmc_request *mrq, u32 slot, u64 *ctx)
{
struct msdc_host *host = mmc_priv(mmc);
sector_t lba = 0;
unsigned int key_idx;
int err = 0, cc_idx = slot;
bool c_en = false;
struct request *req;
if (WARN_ON(!mrq))
return -EINVAL;
req = mrq->req;
if (req) {
lba = blk_rq_pos(req);
if (check_fde_mode(req, &key_idx)) {
err = msdc_cqhci_fde_init(host, key_idx);
/* use slot 0 for fde mode */
cc_idx = 0;
c_en = true;
} else if (check_fbe_mode(req)) {
u64 hw_hie_iv_num;
err = msdc_cqhci_fbe_init(host, req);
cc_idx = slot;
c_en = true;
hw_hie_iv_num = hie_get_iv(req);
if (hw_hie_iv_num)
lba = hw_hie_iv_num;
}
}
if (ctx)
*ctx = DATA_UNIT_NUM(lba) |
CRYPTO_CONFIG_INDEX(cc_idx) |
CRYPTO_ENABLE(!!c_en);
return err;
}
#endif
static void msdc_crypto_switch_config(struct msdc_host *host,
struct request *req,
u32 block_address, u32 dir)
{
void __iomem *base = host->base;
u32 aes_mode_current = 0, aes_sw_reg = 0;
u32 ctr[4] = {0};
unsigned long polling_tmo = 0;
u64 hw_hie_iv_num = 0;
/* 1. set ctr */
aes_sw_reg = MSDC_READ32(EMMC52_AES_EN);
hw_hie_iv_num = hie_get_iv(req);
if (aes_sw_reg & EMMC52_AES_SWITCH_VALID0)
MSDC_GET_FIELD(EMMC52_AES_CFG_GP0,
EMMC52_AES_MODE_0, aes_mode_current);
else if (aes_sw_reg & EMMC52_AES_SWITCH_VALID1)
MSDC_GET_FIELD(EMMC52_AES_CFG_GP1,
EMMC52_AES_MODE_1, aes_mode_current);
else {
pr_info("msdc: EMMC52_AES_SWITCH_VALID error\n");
WARN_ON(1);
return;
}
switch (aes_mode_current) {
case MSDC_CRYPTO_XTS_AES:
case MSDC_CRYPTO_AES_CBC_ESSIV:
case MSDC_CRYPTO_BITLOCKER:
{
if (hw_hie_iv_num) {
ctr[0] = hw_hie_iv_num & 0xffffffff;
#ifndef CONFIG_MTK_EMMC_HW_CQ
ctr[1] = (hw_hie_iv_num >> 32) & 0xffffffff;
#endif
} else {
ctr[0] = block_address;
}
break;
}
case MSDC_CRYPTO_AES_ECB:
case MSDC_CRYPTO_AES_CBC:
break;
case MSDC_CRYPTO_AES_CTR:
{
if (hw_hie_iv_num) {
ctr[0] = hw_hie_iv_num & 0xffffffff;
ctr[1] = (hw_hie_iv_num >> 32) & 0xffffffff;
} else {
ctr[0] = block_address;
}
break;
}
case MSDC_CRYPTO_AES_CBC_MAC:
break;
default:
pr_info("msdc unknown aes mode\n");
WARN_ON(1);
return;
}
if (aes_sw_reg & EMMC52_AES_SWITCH_VALID0) {
MSDC_WRITE32(EMMC52_AES_CTR0_GP0, ctr[0]);
MSDC_WRITE32(EMMC52_AES_CTR1_GP0, ctr[1]);
MSDC_WRITE32(EMMC52_AES_CTR2_GP0, ctr[2]);
MSDC_WRITE32(EMMC52_AES_CTR3_GP0, ctr[3]);
} else {
MSDC_WRITE32(EMMC52_AES_CTR0_GP1, ctr[0]);
MSDC_WRITE32(EMMC52_AES_CTR1_GP1, ctr[1]);
MSDC_WRITE32(EMMC52_AES_CTR2_GP1, ctr[2]);
MSDC_WRITE32(EMMC52_AES_CTR3_GP1, ctr[3]);
}
/* 2. enable AES path */
msdc_enable_crypto(host);
/* 3. AES switch start (flush the configure) */
if (dir == DMA_TO_DEVICE) {
MSDC_SET_BIT32(EMMC52_AES_SWST,
EMMC52_AES_SWITCH_START_ENC);
polling_tmo = jiffies + POLLING_BUSY;
while (MSDC_READ32(EMMC52_AES_SWST) &
EMMC52_AES_SWITCH_START_ENC) {
if (time_after(jiffies, polling_tmo)) {
pr_info("msdc%d trigger AES ENC tmo!\n",
host->id);
WARN_ON(1);
}
}
} else {
MSDC_SET_BIT32(EMMC52_AES_SWST,
EMMC52_AES_SWITCH_START_DEC);
polling_tmo = jiffies + POLLING_BUSY;
while (MSDC_READ32(EMMC52_AES_SWST) &
EMMC52_AES_SWITCH_START_DEC) {
if (time_after(jiffies, polling_tmo)) {
pr_info("msdc%d trigger AES DEC tmo!\n",
host->id);
WARN_ON(1);
}
}
}
}
static void msdc_pre_crypto(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct msdc_host *host = mmc_priv(mmc);
struct mmc_command *cmd = mrq->cmd;
u32 dir = DMA_FROM_DEVICE;
u32 blk_addr = 0;
u32 is_fde = 0, is_fbe = 0;
unsigned int key_idx;
int err;
struct mmc_blk_request *brq;
struct mmc_queue_req *mq_rq = NULL;
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
struct mmc_async_req *areq;
#endif
struct request *req = NULL;
if (!host->hw || !mmc->card)
return;
if (host->hw->host_function != MSDC_EMMC)
return;
#ifdef CONFIG_MTK_EMMC_CQ_SUPPORT
/* CMDQ Command */
if (check_mmc_cmd4647(cmd->opcode)) {
areq = mmc->areq_que[(cmd->arg >> 16) & 0x1f];
mq_rq = container_of(areq, struct mmc_queue_req, areq);
blk_addr = mq_rq->brq.que.arg;
req = mmc_queue_req_to_req(mq_rq);
goto check_hw_crypto;
}
#endif
/* Normal Read Write Command */
if (mrq->is_mmc_req &&
(check_mmc_cmd1718(cmd->opcode) ||
check_mmc_cmd2425(cmd->opcode))) {
brq = container_of(mrq, struct mmc_blk_request, mrq);
mq_rq = container_of(brq, struct mmc_queue_req, brq);
blk_addr = cmd->arg;
req = mmc_queue_req_to_req(mq_rq);
goto check_hw_crypto;
}
return;
check_hw_crypto:
dir = cmd->data->flags & MMC_DATA_READ ?
DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (check_fde_mode(req, &key_idx))
is_fde = 1;
else if (check_fbe_mode(req))
is_fbe = 1;
if (is_fde || is_fbe) {
if (is_fde &&
(!host->is_crypto_init ||
(host->key_idx != key_idx))) {
/* fde init */
mt_secure_call(MTK_SIP_KERNEL_HW_FDE_MSDC_CTL,
(1 << 3), 4, 1, 0);
host->is_crypto_init = true;
host->key_idx = key_idx;
}
if (is_fbe) {
if (!host->is_crypto_init) {
/* fbe init */
mt_secure_call(MTK_SIP_KERNEL_HW_FDE_MSDC_CTL,
(1 << 0), 4, 1, 0);
host->is_crypto_init = true;
}
if (dir == DMA_TO_DEVICE)
err = hie_encrypt(msdc_hie_get_dev(), req,
host);
else
err = hie_decrypt(msdc_hie_get_dev(), req,
host);
if (err) {
err = -EIO;
ERR_MSG(
"%s: fail in crypto hook, req: %p, err %d\n",
__func__, req, err);
WARN_ON(1);
return;
}
if (hie_is_nocrypt())
return;
if (hie_is_dummy()) {
struct mmc_data *data = cmd->data;
if (dir == DMA_TO_DEVICE &&
msdc_use_async_dma(data->host_cookie)) {
dma_unmap_sg(mmc_dev(mmc), data->sg,
data->sg_len, dir);
dma_map_sg(mmc_dev(mmc), data->sg,
data->sg_len, dir);
}
return;
}
}
if (!mmc_card_blockaddr(mmc->card))
blk_addr = blk_addr >> 9;
/* Check data size with 16bytes */
WARN_ON(host->dma.xfersz & 0xf);
/* Check data addressw with 16bytes alignment */
WARN_ON((host->dma.gpd_addr & 0xf)
|| (host->dma.bd_addr & 0xf));
msdc_crypto_switch_config(host, req, blk_addr, dir);
}
}
static void msdc_post_crypto(struct msdc_host *host)
{
msdc_disable_crypto(host);
}
#ifdef CONFIG_MTK_EMMC_HW_CQ
static int _msdc_hie_cqhci_cfg(unsigned int mode, const char *key,
int len, struct request *req, void *priv)
{
struct msdc_host *host = (struct msdc_host *)priv;
struct mmc_host *mmc = host->mmc;
struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
u32 addr, i, cpt_mode, cc_idx;
union cqhci_cpt_cap cpt_cap;
union cqhci_cap_cfg cpt_cfg;
if (mode & BC_AES_256_XTS) {
cpt_mode = CQHCI_CRYPTO_AES_XTS_256;
WARN_ON(len != 64);
} else if (mode & BC_AES_128_XTS) {
cpt_mode = CQHCI_CRYPTO_AES_XTS_128;
WARN_ON(len != 32);
} else {
ERR_MSG("%s: unknown mode 0x%x\n", __func__, mode);
WARN_ON(1);
return -EIO;
}
memset(&cpt_cfg, 0, sizeof(cpt_cfg));
/* init key */
if (len > 64) {
pr_notice("Key size is over %d bits\n", len * 8);
len = 64;
}
memcpy((void *)&(cpt_cfg.cfgx.key[0]), &key[0], len);
#ifdef _CQHCI_DEBUG
for (i = 0; i < len / 4; i++) {
pr_notice("%s: REG[%02d] = 0x%x\n", __func__, i,
cpt_cfg.cfgx.key[i]);
}
#endif
/* cc idx is same as slot no. or tag no. */
cc_idx = req->tag;
/* enable this cfg */
cpt_cfg.cfgx.cfg_en = 1;
/* init capability id */
cpt_cfg.cfgx.cap_id = (u8)cpt_mode;
/* init data unit size: fixed as 512 * 2^0) for eMMC */
cpt_cfg.cfgx.du_size = (1 << CQHCI_CRYPTO_DU_SIZE_512B);
/*
* Get address of cfg[cfg_id], this is also
* address of key in cfg[cfg_id].
*/
cpt_cap.cap_raw = cmdq_readl(cq_host, CRCAP);
addr = (cpt_cap.cap.cfg_ptr << 8) + (u32)(cc_idx << 7);
/* write configuration only to register */
for (i = 0; i < 32; i++) {
cmdq_writel(cq_host, cpt_cfg.cfgx_raw[i],
(addr + i * 4));
#ifdef _CQHCI_DEBUG
pr_notice("%s: (%d)REG[0x%x]=0x%x\n",
__func__, cc_idx,
(addr + i * 4),
cpt_cfg.cfgx_raw[i]);
#endif
}
return 0;
}
#endif
static int _msdc_hie_cfg(unsigned int mode, const char *key,
int len, struct request *req, void *priv)
{
struct msdc_host *host = (struct msdc_host *)priv;
void __iomem *base = host->base;
u32 iv[4] = {0}, aes_key[8] = {0}, aes_tkey[8] = {0};
u32 data_unit_size, i, half_len;
u8 key_bit, aes_mode;
if (mode & BC_AES_256_XTS) {
aes_mode = MSDC_CRYPTO_XTS_AES;
key_bit = BIT_256;
WARN_ON(len != 64);
} else if (mode & BC_AES_128_XTS) {
aes_mode = MSDC_CRYPTO_XTS_AES;
key_bit = BIT_128;
WARN_ON(len != 32);
} else {
ERR_MSG("%s: unknown mode 0x%x\n", __func__, mode);
WARN_ON(1);
return -EIO;
}
/*
* limit half_len as u32 * 8
* prevent local buffer overflow
*/
half_len = min_t(u32, len / 2, sizeof(u32) * 8);
/* Split key into key & tkey */
memcpy(aes_key, &key[0], half_len);
memcpy(aes_tkey, &key[half_len], half_len);
/* eMMC block size 512bytes */
data_unit_size = (1 << 9);
/* AES config */
MSDC_WRITE32(EMMC52_AES_CFG_GP1,
(data_unit_size << 16 | key_bit << 8 | aes_mode << 0));
/* IV */
for (i = 0; i < 4; i++)
MSDC_WRITE32((EMMC52_AES_IV0_GP1 + i * 4), iv[i]);
/* KEY */
for (i = 0; i < 8; i++)
MSDC_WRITE32((EMMC52_AES_KEY0_GP1 + i * 4), aes_key[i]);
/* TKEY */
for (i = 0; i < 8; i++)
MSDC_WRITE32((EMMC52_AES_TKEY0_GP1 + i * 4), aes_tkey[i]);
return 0;
}
/* configure request for HIE */
static int msdc_hie_cfg_request(unsigned int mode, const char *key,
int len, struct request *req, void *priv)
{
#ifdef CONFIG_MTK_EMMC_HW_CQ
struct msdc_host *host = (struct msdc_host *)priv;
if (mmc_card_cmdq(host->mmc->card))
_msdc_hie_cqhci_cfg(mode, key, len, req, priv);
else
#endif
_msdc_hie_cfg(mode, key, len, req, priv);
return 0;
}
struct hie_dev msdc_hie_dev = {
.name = "msdc",
.mode = (BC_AES_256_XTS | BC_AES_128_XTS),
.encrypt = msdc_hie_cfg_request,
.decrypt = msdc_hie_cfg_request,
.priv = NULL,
};
struct hie_dev *msdc_hie_get_dev(void)
{
return &msdc_hie_dev;
}
static void msdc_hie_register(struct msdc_host *host)
{
if (host->hw->host_function == MSDC_EMMC)
hie_register_device(&msdc_hie_dev);
}