mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 04:12:04 -04:00
Merge "mmc: host: Add variant ops for cqhci crypto"
This commit is contained in:
commit
65e751ed01
9 changed files with 1191 additions and 1 deletions
|
|
@ -1041,3 +1041,20 @@ config SDC_QTI
|
|||
help
|
||||
This configuration flag allows adding QTI code in
|
||||
MMC upstream driver.
|
||||
|
||||
config MMC_CQHCI_CRYPTO
|
||||
bool "CQHCI Crypto Engine Support"
|
||||
depends on MMC_CQHCI && BLK_INLINE_ENCRYPTION
|
||||
help
|
||||
Enable Crypto Engine Support in CQHCI.
|
||||
Enabling this makes it possible for the kernel to use the crypto
|
||||
capabilities of the CQHCI device (if present) to perform crypto
|
||||
operations on data being transferred to/from the device.
|
||||
|
||||
config MMC_CQHCI_CRYPTO_QTI
|
||||
bool "Vendor specific CQHCI Crypto Engine Support"
|
||||
depends on MMC_CQHCI_CRYPTO
|
||||
help
|
||||
Enable Vendor Crypto Engine Support in CQHCI
|
||||
Enabling this allows kernel to use CQHCI crypto operations defined
|
||||
and implemented by QTI.
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
|
|||
obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o
|
||||
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
|
||||
obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o
|
||||
obj-$(CONFIG_MMC_CQHCI_CRYPTO) += cqhci-crypto.o
|
||||
obj-$(CONFIG_MMC_CQHCI_CRYPTO_QTI) += cqhci-crypto-qti.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
CFLAGS-cb710-mmc += -DDEBUG
|
||||
|
|
|
|||
293
drivers/mmc/host/cqhci-crypto-qti.c
Normal file
293
drivers/mmc/host/cqhci-crypto-qti.c
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2020, Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include "sdhci.h"
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci-crypto-qti.h"
|
||||
#include <linux/crypto-qti-common.h>
|
||||
|
||||
#define RAW_SECRET_SIZE 32
|
||||
#define MINIMUM_DUN_SIZE 512
|
||||
#define MAXIMUM_DUN_SIZE 65536
|
||||
|
||||
static struct cqhci_host_crypto_variant_ops cqhci_crypto_qti_variant_ops = {
|
||||
.host_init_crypto = cqhci_crypto_qti_init_crypto,
|
||||
.enable = cqhci_crypto_qti_enable,
|
||||
.disable = cqhci_crypto_qti_disable,
|
||||
.resume = cqhci_crypto_qti_resume,
|
||||
.debug = cqhci_crypto_qti_debug,
|
||||
};
|
||||
|
||||
static bool ice_cap_idx_valid(struct cqhci_host *host,
|
||||
unsigned int cap_idx)
|
||||
{
|
||||
return cap_idx < host->crypto_capabilities.num_crypto_cap;
|
||||
}
|
||||
|
||||
static uint8_t get_data_unit_size_mask(unsigned int data_unit_size)
|
||||
{
|
||||
if (data_unit_size < MINIMUM_DUN_SIZE ||
|
||||
data_unit_size > MAXIMUM_DUN_SIZE ||
|
||||
!is_power_of_2(data_unit_size))
|
||||
return 0;
|
||||
|
||||
return data_unit_size / MINIMUM_DUN_SIZE;
|
||||
}
|
||||
|
||||
|
||||
void cqhci_crypto_qti_enable(struct cqhci_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!cqhci_host_is_crypto_supported(host))
|
||||
return;
|
||||
|
||||
host->caps |= CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
|
||||
err = crypto_qti_enable(host->crypto_vops->priv);
|
||||
if (err) {
|
||||
pr_err("%s: Error enabling crypto, err %d\n",
|
||||
__func__, err);
|
||||
cqhci_crypto_qti_disable(host);
|
||||
}
|
||||
}
|
||||
|
||||
void cqhci_crypto_qti_disable(struct cqhci_host *host)
|
||||
{
|
||||
cqhci_crypto_disable_spec(host);
|
||||
crypto_qti_disable(host->crypto_vops->priv);
|
||||
}
|
||||
|
||||
static int cqhci_crypto_qti_keyslot_program(struct keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
{
|
||||
struct cqhci_host *host = keyslot_manager_private(ksm);
|
||||
int err = 0;
|
||||
u8 data_unit_mask;
|
||||
int crypto_alg_id;
|
||||
|
||||
crypto_alg_id = cqhci_crypto_cap_find(host, key->crypto_mode,
|
||||
key->data_unit_size);
|
||||
|
||||
if (!cqhci_is_crypto_enabled(host) ||
|
||||
!cqhci_keyslot_valid(host, slot) ||
|
||||
!ice_cap_idx_valid(host, crypto_alg_id)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_unit_mask = get_data_unit_size_mask(key->data_unit_size);
|
||||
|
||||
if (!(data_unit_mask &
|
||||
host->crypto_cap_array[crypto_alg_id].sdus_mask)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = crypto_qti_keyslot_program(host->crypto_vops->priv, key,
|
||||
slot, data_unit_mask, crypto_alg_id);
|
||||
if (err)
|
||||
pr_err("%s: failed with error %d\n", __func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_qti_keyslot_evict(struct keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
{
|
||||
int err = 0;
|
||||
struct cqhci_host *host = keyslot_manager_private(ksm);
|
||||
|
||||
if (!cqhci_is_crypto_enabled(host) ||
|
||||
!cqhci_keyslot_valid(host, slot))
|
||||
return -EINVAL;
|
||||
|
||||
err = crypto_qti_keyslot_evict(host->crypto_vops->priv, slot);
|
||||
if (err)
|
||||
pr_err("%s: failed with error %d\n", __func__, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_qti_derive_raw_secret(struct keyslot_manager *ksm,
|
||||
const u8 *wrapped_key, unsigned int wrapped_key_size,
|
||||
u8 *secret, unsigned int secret_size)
|
||||
{
|
||||
int err = 0;
|
||||
struct cqhci_host *host = keyslot_manager_private(ksm);
|
||||
|
||||
err = crypto_qti_derive_raw_secret(host->crypto_vops->priv, wrapped_key, wrapped_key_size,
|
||||
secret, secret_size);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct keyslot_mgmt_ll_ops cqhci_crypto_qti_ksm_ops = {
|
||||
.keyslot_program = cqhci_crypto_qti_keyslot_program,
|
||||
.keyslot_evict = cqhci_crypto_qti_keyslot_evict,
|
||||
.derive_raw_secret = cqhci_crypto_qti_derive_raw_secret
|
||||
};
|
||||
|
||||
enum blk_crypto_mode_num cqhci_blk_crypto_qti_mode_num_for_alg_dusize(
|
||||
enum cqhci_crypto_alg cqhci_crypto_alg,
|
||||
enum cqhci_crypto_key_size key_size)
|
||||
{
|
||||
/*
|
||||
* Currently the only mode that eMMC and blk-crypto both support.
|
||||
*/
|
||||
if (cqhci_crypto_alg == CQHCI_CRYPTO_ALG_AES_XTS &&
|
||||
key_size == CQHCI_CRYPTO_KEY_SIZE_256)
|
||||
return BLK_ENCRYPTION_MODE_AES_256_XTS;
|
||||
|
||||
return BLK_ENCRYPTION_MODE_INVALID;
|
||||
}
|
||||
|
||||
int cqhci_host_init_crypto_qti_spec(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops)
|
||||
{
|
||||
int cap_idx = 0;
|
||||
int err = 0;
|
||||
unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX];
|
||||
enum blk_crypto_mode_num blk_mode_num;
|
||||
|
||||
/* Default to disabling crypto */
|
||||
host->caps &= ~CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
|
||||
if (!(cqhci_readl(host, CQHCI_CAP) & CQHCI_CAP_CS)) {
|
||||
pr_debug("%s no crypto capability\n", __func__);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Crypto Capabilities should never be 0, because the
|
||||
* config_array_ptr > 04h. So we use a 0 value to indicate that
|
||||
* crypto init failed, and can't be enabled.
|
||||
*/
|
||||
host->crypto_capabilities.reg_val = cqhci_readl(host, CQHCI_CCAP);
|
||||
host->crypto_cfg_register =
|
||||
(u32)host->crypto_capabilities.config_array_ptr * 0x100;
|
||||
host->crypto_cap_array =
|
||||
devm_kcalloc(mmc_dev(host->mmc),
|
||||
host->crypto_capabilities.num_crypto_cap,
|
||||
sizeof(host->crypto_cap_array[0]), GFP_KERNEL);
|
||||
if (!host->crypto_cap_array) {
|
||||
err = -ENOMEM;
|
||||
pr_err("%s failed to allocate memory\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(crypto_modes_supported, 0, sizeof(crypto_modes_supported));
|
||||
|
||||
/*
|
||||
* Store all the capabilities now so that we don't need to repeatedly
|
||||
* access the device each time we want to know its capabilities
|
||||
*/
|
||||
for (cap_idx = 0; cap_idx < host->crypto_capabilities.num_crypto_cap;
|
||||
cap_idx++) {
|
||||
host->crypto_cap_array[cap_idx].reg_val =
|
||||
cpu_to_le32(cqhci_readl(host,
|
||||
CQHCI_CRYPTOCAP +
|
||||
cap_idx * sizeof(__le32)));
|
||||
blk_mode_num = cqhci_blk_crypto_qti_mode_num_for_alg_dusize(
|
||||
host->crypto_cap_array[cap_idx].algorithm_id,
|
||||
host->crypto_cap_array[cap_idx].key_size);
|
||||
if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID)
|
||||
continue;
|
||||
crypto_modes_supported[blk_mode_num] |=
|
||||
host->crypto_cap_array[cap_idx].sdus_mask * 512;
|
||||
}
|
||||
|
||||
host->ksm = keyslot_manager_create(host->mmc->parent,
|
||||
cqhci_num_keyslots(host), ksm_ops,
|
||||
BLK_CRYPTO_FEATURE_STANDARD_KEYS |
|
||||
BLK_CRYPTO_FEATURE_WRAPPED_KEYS,
|
||||
crypto_modes_supported,
|
||||
host);
|
||||
|
||||
keyslot_manager_set_max_dun_bytes(host->ksm, sizeof(u32));
|
||||
|
||||
if (!host->ksm) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* In case host controller supports cryptographic operations
|
||||
* then, it uses 128bit task descriptor. Upper 64 bits of task
|
||||
* descriptor would be used to pass crypto specific informaton.
|
||||
*/
|
||||
host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
/* Indicate that init failed by setting crypto_capabilities to 0 */
|
||||
host->crypto_capabilities.reg_val = 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
int cqhci_crypto_qti_init_crypto(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops)
|
||||
{
|
||||
int err = 0;
|
||||
struct resource *cqhci_ice_memres = NULL;
|
||||
|
||||
cqhci_ice_memres = platform_get_resource_byname(host->pdev,
|
||||
IORESOURCE_MEM,
|
||||
"cqhci_ice");
|
||||
if (!cqhci_ice_memres) {
|
||||
pr_debug("%s ICE not supported\n", __func__);
|
||||
host->icemmio = NULL;
|
||||
return PTR_ERR(cqhci_ice_memres);
|
||||
}
|
||||
|
||||
host->icemmio = devm_ioremap(&host->pdev->dev,
|
||||
cqhci_ice_memres->start,
|
||||
resource_size(cqhci_ice_memres));
|
||||
if (!host->icemmio) {
|
||||
pr_err("%s failed to remap ice regs\n", __func__);
|
||||
return PTR_ERR(host->icemmio);
|
||||
}
|
||||
|
||||
err = cqhci_host_init_crypto_qti_spec(host, &cqhci_crypto_qti_ksm_ops);
|
||||
if (err) {
|
||||
pr_err("%s: Error initiating crypto capabilities, err %d\n",
|
||||
__func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = crypto_qti_init_crypto(&host->pdev->dev,
|
||||
host->icemmio, (void **)&host->crypto_vops->priv);
|
||||
if (err) {
|
||||
pr_err("%s: Error initiating crypto, err %d\n",
|
||||
__func__, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int cqhci_crypto_qti_debug(struct cqhci_host *host)
|
||||
{
|
||||
return crypto_qti_debug(host->crypto_vops->priv);
|
||||
}
|
||||
|
||||
void cqhci_crypto_qti_set_vops(struct cqhci_host *host)
|
||||
{
|
||||
return cqhci_crypto_set_vops(host, &cqhci_crypto_qti_variant_ops);
|
||||
}
|
||||
|
||||
int cqhci_crypto_qti_resume(struct cqhci_host *host)
|
||||
{
|
||||
return crypto_qti_resume(host->crypto_vops->priv);
|
||||
}
|
||||
26
drivers/mmc/host/cqhci-crypto-qti.h
Normal file
26
drivers/mmc/host/cqhci-crypto-qti.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _UFSHCD_CRYPTO_QTI_H
|
||||
#define _UFSHCD_CRYPTO_QTI_H
|
||||
|
||||
#include "cqhci-crypto.h"
|
||||
|
||||
void cqhci_crypto_qti_enable(struct cqhci_host *host);
|
||||
|
||||
void cqhci_crypto_qti_disable(struct cqhci_host *host);
|
||||
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
int cqhci_crypto_qti_init_crypto(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops);
|
||||
#endif
|
||||
|
||||
int cqhci_crypto_qti_debug(struct cqhci_host *host);
|
||||
|
||||
void cqhci_crypto_qti_set_vops(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_qti_resume(struct cqhci_host *host);
|
||||
|
||||
#endif /* _UFSHCD_ICE_QTI_H */
|
||||
532
drivers/mmc/host/cqhci-crypto.c
Normal file
532
drivers/mmc/host/cqhci-crypto.c
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* drivers/mmc/host/cqhci-crypto.c - Qualcomm Technologies, Inc.
|
||||
*
|
||||
* Original source is taken from:
|
||||
* https://android.googlesource.com/kernel/common/+/4bac1109a10c55d49c0aa4f7ebdc4bc53cc368e8
|
||||
* The driver caters to crypto engine support for UFS controllers.
|
||||
* The crypto engine programming sequence, HW functionality and register
|
||||
* offset is almost same in UFS and eMMC controllers.
|
||||
*/
|
||||
|
||||
#include <crypto/algapi.h>
|
||||
#include "cqhci-crypto.h"
|
||||
#include "../core/queue.h"
|
||||
|
||||
static bool cqhci_cap_idx_valid(struct cqhci_host *host, unsigned int cap_idx)
|
||||
{
|
||||
return cap_idx < host->crypto_capabilities.num_crypto_cap;
|
||||
}
|
||||
|
||||
static u8 get_data_unit_size_mask(unsigned int data_unit_size)
|
||||
{
|
||||
if (data_unit_size < 512 || data_unit_size > 65536 ||
|
||||
!is_power_of_2(data_unit_size))
|
||||
return 0;
|
||||
|
||||
return data_unit_size / 512;
|
||||
}
|
||||
|
||||
static size_t get_keysize_bytes(enum cqhci_crypto_key_size size)
|
||||
{
|
||||
switch (size) {
|
||||
case CQHCI_CRYPTO_KEY_SIZE_128:
|
||||
return 16;
|
||||
case CQHCI_CRYPTO_KEY_SIZE_192:
|
||||
return 24;
|
||||
case CQHCI_CRYPTO_KEY_SIZE_256:
|
||||
return 32;
|
||||
case CQHCI_CRYPTO_KEY_SIZE_512:
|
||||
return 64;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int cqhci_crypto_cap_find(void *host_p, enum blk_crypto_mode_num crypto_mode,
|
||||
unsigned int data_unit_size)
|
||||
{
|
||||
struct cqhci_host *host = host_p;
|
||||
enum cqhci_crypto_alg cqhci_alg;
|
||||
u8 data_unit_mask;
|
||||
int cap_idx;
|
||||
enum cqhci_crypto_key_size cqhci_key_size;
|
||||
union cqhci_crypto_cap_entry *ccap_array = host->crypto_cap_array;
|
||||
|
||||
if (!cqhci_host_is_crypto_supported(host))
|
||||
return -EINVAL;
|
||||
|
||||
switch (crypto_mode) {
|
||||
case BLK_ENCRYPTION_MODE_AES_256_XTS:
|
||||
cqhci_alg = CQHCI_CRYPTO_ALG_AES_XTS;
|
||||
cqhci_key_size = CQHCI_CRYPTO_KEY_SIZE_256;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data_unit_mask = get_data_unit_size_mask(data_unit_size);
|
||||
|
||||
for (cap_idx = 0; cap_idx < host->crypto_capabilities.num_crypto_cap;
|
||||
cap_idx++) {
|
||||
if (ccap_array[cap_idx].algorithm_id == cqhci_alg &&
|
||||
(ccap_array[cap_idx].sdus_mask & data_unit_mask) &&
|
||||
ccap_array[cap_idx].key_size == cqhci_key_size)
|
||||
return cap_idx;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_crypto_cap_find);
|
||||
|
||||
/**
|
||||
* cqhci_crypto_cfg_entry_write_key - Write a key into a crypto_cfg_entry
|
||||
*
|
||||
* Writes the key with the appropriate format - for AES_XTS,
|
||||
* the first half of the key is copied as is, the second half is
|
||||
* copied with an offset halfway into the cfg->crypto_key array.
|
||||
* For the other supported crypto algs, the key is just copied.
|
||||
*
|
||||
* @cfg: The crypto config to write to
|
||||
* @key: The key to write
|
||||
* @cap: The crypto capability (which specifies the crypto alg and key size)
|
||||
*
|
||||
* Returns 0 on success, or -EINVAL
|
||||
*/
|
||||
static int cqhci_crypto_cfg_entry_write_key(union cqhci_crypto_cfg_entry *cfg,
|
||||
const u8 *key,
|
||||
union cqhci_crypto_cap_entry cap)
|
||||
{
|
||||
size_t key_size_bytes = get_keysize_bytes(cap.key_size);
|
||||
|
||||
if (key_size_bytes == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (cap.algorithm_id) {
|
||||
case CQHCI_CRYPTO_ALG_AES_XTS:
|
||||
key_size_bytes *= 2;
|
||||
if (key_size_bytes > CQHCI_CRYPTO_KEY_MAX_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cfg->crypto_key, key, key_size_bytes/2);
|
||||
memcpy(cfg->crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2,
|
||||
key + key_size_bytes/2, key_size_bytes/2);
|
||||
return 0;
|
||||
case CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC:
|
||||
fallthrough;
|
||||
case CQHCI_CRYPTO_ALG_AES_ECB:
|
||||
fallthrough;
|
||||
case CQHCI_CRYPTO_ALG_ESSIV_AES_CBC:
|
||||
memcpy(cfg->crypto_key, key, key_size_bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void cqhci_program_key(struct cqhci_host *host,
|
||||
const union cqhci_crypto_cfg_entry *cfg,
|
||||
int slot)
|
||||
{
|
||||
int i;
|
||||
u32 slot_offset = host->crypto_cfg_register + slot * sizeof(*cfg);
|
||||
|
||||
if (host->crypto_vops && host->crypto_vops->program_key)
|
||||
host->crypto_vops->program_key(host, cfg, slot);
|
||||
|
||||
/* Clear the dword 16 */
|
||||
cqhci_writel(host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0]));
|
||||
/* Ensure that CFGE is cleared before programming the key */
|
||||
wmb();
|
||||
for (i = 0; i < 16; i++) {
|
||||
cqhci_writel(host, le32_to_cpu(cfg->reg_val[i]),
|
||||
slot_offset + i * sizeof(cfg->reg_val[0]));
|
||||
/* Spec says each dword in key must be written sequentially */
|
||||
wmb();
|
||||
}
|
||||
/* Write dword 17 */
|
||||
cqhci_writel(host, le32_to_cpu(cfg->reg_val[17]),
|
||||
slot_offset + 17 * sizeof(cfg->reg_val[0]));
|
||||
/* Dword 16 must be written last */
|
||||
wmb();
|
||||
/* Write dword 16 */
|
||||
cqhci_writel(host, le32_to_cpu(cfg->reg_val[16]),
|
||||
slot_offset + 16 * sizeof(cfg->reg_val[0]));
|
||||
/*Ensure that dword 16 is written */
|
||||
wmb();
|
||||
}
|
||||
|
||||
static void cqhci_crypto_clear_keyslot(struct cqhci_host *host, int slot)
|
||||
{
|
||||
union cqhci_crypto_cfg_entry cfg = { {0} };
|
||||
|
||||
cqhci_program_key(host, &cfg, slot);
|
||||
}
|
||||
|
||||
static void cqhci_crypto_clear_all_keyslots(struct cqhci_host *host)
|
||||
{
|
||||
int slot;
|
||||
|
||||
for (slot = 0; slot < cqhci_num_keyslots(host); slot++)
|
||||
cqhci_crypto_clear_keyslot(host, slot);
|
||||
}
|
||||
|
||||
static int cqhci_crypto_keyslot_program(struct keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
{
|
||||
struct cqhci_host *host = keyslot_manager_private(ksm);
|
||||
int err = 0;
|
||||
u8 data_unit_mask;
|
||||
union cqhci_crypto_cfg_entry cfg;
|
||||
int cap_idx;
|
||||
|
||||
cap_idx = cqhci_crypto_cap_find(host, key->crypto_mode,
|
||||
key->data_unit_size);
|
||||
|
||||
if (!cqhci_is_crypto_enabled(host) ||
|
||||
!cqhci_keyslot_valid(host, slot) ||
|
||||
!cqhci_cap_idx_valid(host, cap_idx))
|
||||
return -EINVAL;
|
||||
|
||||
data_unit_mask = get_data_unit_size_mask(key->data_unit_size);
|
||||
|
||||
if (!(data_unit_mask & host->crypto_cap_array[cap_idx].sdus_mask))
|
||||
return -EINVAL;
|
||||
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.data_unit_size = data_unit_mask;
|
||||
cfg.crypto_cap_idx = cap_idx;
|
||||
cfg.config_enable |= CQHCI_CRYPTO_CONFIGURATION_ENABLE;
|
||||
|
||||
err = cqhci_crypto_cfg_entry_write_key(&cfg, key->raw,
|
||||
host->crypto_cap_array[cap_idx]);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cqhci_program_key(host, &cfg, slot);
|
||||
|
||||
memzero_explicit(&cfg, sizeof(cfg));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cqhci_crypto_keyslot_evict(struct keyslot_manager *ksm,
|
||||
const struct blk_crypto_key *key,
|
||||
unsigned int slot)
|
||||
{
|
||||
struct cqhci_host *host = keyslot_manager_private(ksm);
|
||||
|
||||
if (!cqhci_is_crypto_enabled(host) ||
|
||||
!cqhci_keyslot_valid(host, slot))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Clear the crypto cfg on the device. Clearing CFGE
|
||||
* might not be sufficient, so just clear the entire cfg.
|
||||
*/
|
||||
cqhci_crypto_clear_keyslot(host, slot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Functions implementing eMMC v5.2 specification behaviour */
|
||||
void cqhci_crypto_enable_spec(struct cqhci_host *host)
|
||||
{
|
||||
if (!cqhci_host_is_crypto_supported(host))
|
||||
return;
|
||||
|
||||
host->caps |= CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_crypto_enable_spec);
|
||||
|
||||
void cqhci_crypto_disable_spec(struct cqhci_host *host)
|
||||
{
|
||||
host->caps &= ~CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_crypto_disable_spec);
|
||||
|
||||
static const struct keyslot_mgmt_ll_ops cqhci_ksm_ops = {
|
||||
.keyslot_program = cqhci_crypto_keyslot_program,
|
||||
.keyslot_evict = cqhci_crypto_keyslot_evict,
|
||||
};
|
||||
|
||||
enum blk_crypto_mode_num cqhci_crypto_blk_crypto_mode_num_for_alg_dusize(
|
||||
enum cqhci_crypto_alg cqhci_crypto_alg,
|
||||
enum cqhci_crypto_key_size key_size)
|
||||
{
|
||||
/*
|
||||
* Currently the only mode that eMMC and blk-crypto both support.
|
||||
*/
|
||||
if (cqhci_crypto_alg == CQHCI_CRYPTO_ALG_AES_XTS &&
|
||||
key_size == CQHCI_CRYPTO_KEY_SIZE_256)
|
||||
return BLK_ENCRYPTION_MODE_AES_256_XTS;
|
||||
|
||||
return BLK_ENCRYPTION_MODE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* cqhci_host_init_crypto - Read crypto capabilities, init crypto fields in host
|
||||
* @host: Per adapter instance
|
||||
*
|
||||
* Returns 0 on success. Returns -ENODEV if such capabilities don't exist, and
|
||||
* -ENOMEM upon OOM.
|
||||
*/
|
||||
int cqhci_host_init_crypto_spec(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops)
|
||||
{
|
||||
int cap_idx = 0;
|
||||
int err = 0;
|
||||
unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX];
|
||||
enum blk_crypto_mode_num blk_mode_num;
|
||||
|
||||
/* Default to disabling crypto */
|
||||
host->caps &= ~CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
|
||||
if (!(cqhci_readl(host, CQHCI_CAP) & CQHCI_CAP_CS)) {
|
||||
pr_err("%s no crypto capability\n", __func__);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Crypto Capabilities should never be 0, because the
|
||||
* config_array_ptr > 04h. So we use a 0 value to indicate that
|
||||
* crypto init failed, and can't be enabled.
|
||||
*/
|
||||
host->crypto_capabilities.reg_val = cqhci_readl(host, CQHCI_CCAP);
|
||||
host->crypto_cfg_register =
|
||||
(u32)host->crypto_capabilities.config_array_ptr * 0x100;
|
||||
host->crypto_cap_array =
|
||||
devm_kcalloc(mmc_dev(host->mmc),
|
||||
host->crypto_capabilities.num_crypto_cap,
|
||||
sizeof(host->crypto_cap_array[0]), GFP_KERNEL);
|
||||
if (!host->crypto_cap_array) {
|
||||
err = -ENOMEM;
|
||||
pr_err("%s no memory cap\n", __func__);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(crypto_modes_supported, 0, sizeof(crypto_modes_supported));
|
||||
|
||||
/*
|
||||
* Store all the capabilities now so that we don't need to repeatedly
|
||||
* access the device each time we want to know its capabilities
|
||||
*/
|
||||
for (cap_idx = 0; cap_idx < host->crypto_capabilities.num_crypto_cap;
|
||||
cap_idx++) {
|
||||
host->crypto_cap_array[cap_idx].reg_val =
|
||||
cpu_to_le32(cqhci_readl(host,
|
||||
CQHCI_CRYPTOCAP +
|
||||
cap_idx * sizeof(__le32)));
|
||||
blk_mode_num = cqhci_crypto_blk_crypto_mode_num_for_alg_dusize(
|
||||
host->crypto_cap_array[cap_idx].algorithm_id,
|
||||
host->crypto_cap_array[cap_idx].key_size);
|
||||
if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID)
|
||||
continue;
|
||||
crypto_modes_supported[blk_mode_num] |=
|
||||
host->crypto_cap_array[cap_idx].sdus_mask * 512;
|
||||
}
|
||||
|
||||
cqhci_crypto_clear_all_keyslots(host);
|
||||
|
||||
host->ksm = keyslot_manager_create(host->mmc->parent,
|
||||
cqhci_num_keyslots(host), ksm_ops,
|
||||
BLK_CRYPTO_FEATURE_STANDARD_KEYS |
|
||||
BLK_CRYPTO_FEATURE_WRAPPED_KEYS,
|
||||
crypto_modes_supported,
|
||||
host);
|
||||
|
||||
if (!host->ksm) {
|
||||
err = -ENOMEM;
|
||||
goto out_free_caps;
|
||||
}
|
||||
/*
|
||||
* In case host controller supports cryptographic operations
|
||||
* then, it uses 128bit task descriptor. Upper 64 bits of task
|
||||
* descriptor would be used to pass crypto specific informaton.
|
||||
*/
|
||||
host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
|
||||
return 0;
|
||||
out_free_caps:
|
||||
devm_kfree(mmc_dev(host->mmc), host->crypto_cap_array);
|
||||
out:
|
||||
// TODO: print error?
|
||||
/* Indicate that init failed by setting crypto_capabilities to 0 */
|
||||
host->crypto_capabilities.reg_val = 0;
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_host_init_crypto_spec);
|
||||
|
||||
void cqhci_crypto_setup_rq_keyslot_manager_spec(struct cqhci_host *host,
|
||||
struct request_queue *q)
|
||||
{
|
||||
if (!cqhci_host_is_crypto_supported(host) || !q)
|
||||
return;
|
||||
|
||||
q->ksm = host->ksm;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_crypto_setup_rq_keyslot_manager_spec);
|
||||
|
||||
void cqhci_crypto_destroy_rq_keyslot_manager_spec(struct cqhci_host *host,
|
||||
struct request_queue *q)
|
||||
{
|
||||
keyslot_manager_destroy(host->ksm);
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_crypto_destroy_rq_keyslot_manager_spec);
|
||||
|
||||
int cqhci_prepare_crypto_desc_spec(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx)
|
||||
{
|
||||
struct bio_crypt_ctx *bc;
|
||||
struct mmc_queue_req *mqrq = container_of(mrq, struct mmc_queue_req,
|
||||
brq.mrq);
|
||||
struct request *req = mmc_queue_req_to_req(mqrq);
|
||||
|
||||
if (!req->bio ||
|
||||
!bio_crypt_should_process(req)) {
|
||||
*ice_ctx = 0;
|
||||
return 0;
|
||||
}
|
||||
if (WARN_ON(!cqhci_is_crypto_enabled(host))) {
|
||||
/*
|
||||
* Upper layer asked us to do inline encryption
|
||||
* but that isn't enabled, so we fail this request.
|
||||
*/
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bc = req->bio->bi_crypt_context;
|
||||
|
||||
if (!cqhci_keyslot_valid(host, bc->bc_keyslot))
|
||||
return -EINVAL;
|
||||
|
||||
if (ice_ctx) {
|
||||
*ice_ctx = DATA_UNIT_NUM(bc->bc_dun[0]) |
|
||||
CRYPTO_CONFIG_INDEX(bc->bc_keyslot) |
|
||||
CRYPTO_ENABLE(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(cqhci_prepare_crypto_desc_spec);
|
||||
|
||||
/* Crypto Variant Ops Support */
|
||||
|
||||
void cqhci_crypto_enable(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->enable)
|
||||
return host->crypto_vops->enable(host);
|
||||
|
||||
return cqhci_crypto_enable_spec(host);
|
||||
}
|
||||
|
||||
void cqhci_crypto_disable(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->disable)
|
||||
return host->crypto_vops->disable(host);
|
||||
|
||||
return cqhci_crypto_disable_spec(host);
|
||||
}
|
||||
|
||||
int cqhci_host_init_crypto(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->host_init_crypto)
|
||||
return host->crypto_vops->host_init_crypto(host,
|
||||
&cqhci_ksm_ops);
|
||||
|
||||
return cqhci_host_init_crypto_spec(host, &cqhci_ksm_ops);
|
||||
}
|
||||
|
||||
void cqhci_crypto_setup_rq_keyslot_manager(struct cqhci_host *host,
|
||||
struct request_queue *q)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->setup_rq_keyslot_manager)
|
||||
return host->crypto_vops->setup_rq_keyslot_manager(host, q);
|
||||
|
||||
return cqhci_crypto_setup_rq_keyslot_manager_spec(host, q);
|
||||
}
|
||||
|
||||
void cqhci_crypto_destroy_rq_keyslot_manager(struct cqhci_host *host,
|
||||
struct request_queue *q)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->destroy_rq_keyslot_manager)
|
||||
return host->crypto_vops->destroy_rq_keyslot_manager(host, q);
|
||||
|
||||
return cqhci_crypto_destroy_rq_keyslot_manager_spec(host, q);
|
||||
}
|
||||
|
||||
int cqhci_crypto_get_ctx(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->prepare_crypto_desc)
|
||||
return host->crypto_vops->prepare_crypto_desc(host, mrq,
|
||||
ice_ctx);
|
||||
|
||||
return cqhci_prepare_crypto_desc_spec(host, mrq, ice_ctx);
|
||||
}
|
||||
|
||||
int cqhci_complete_crypto_desc(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->complete_crypto_desc)
|
||||
return host->crypto_vops->complete_crypto_desc(host, mrq,
|
||||
ice_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cqhci_crypto_debug(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->debug)
|
||||
host->crypto_vops->debug(host);
|
||||
}
|
||||
|
||||
void cqhci_crypto_set_vops(struct cqhci_host *host,
|
||||
struct cqhci_host_crypto_variant_ops *crypto_vops)
|
||||
{
|
||||
host->crypto_vops = crypto_vops;
|
||||
}
|
||||
|
||||
int cqhci_crypto_suspend(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->suspend)
|
||||
return host->crypto_vops->suspend(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cqhci_crypto_resume(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->resume)
|
||||
return host->crypto_vops->resume(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cqhci_crypto_reset(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->reset)
|
||||
return host->crypto_vops->reset(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cqhci_crypto_recovery_finish(struct cqhci_host *host)
|
||||
{
|
||||
if (host->crypto_vops && host->crypto_vops->recovery_finish)
|
||||
return host->crypto_vops->recovery_finish(host);
|
||||
|
||||
/* Reset/Recovery might clear all keys, so reprogram all the keys. */
|
||||
keyslot_manager_reprogram_all_keys(host->ksm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
182
drivers/mmc/host/cqhci-crypto.h
Normal file
182
drivers/mmc/host/cqhci-crypto.h
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _CQHCI_CRYPTO_H
|
||||
#define _CQHCI_CRYPTO_H
|
||||
|
||||
#ifdef CONFIG_MMC_CQHCI_CRYPTO
|
||||
#include <linux/mmc/host.h>
|
||||
#include "cqhci.h"
|
||||
|
||||
static inline int cqhci_num_keyslots(struct cqhci_host *host)
|
||||
{
|
||||
return host->crypto_capabilities.config_count + 1;
|
||||
}
|
||||
|
||||
static inline bool cqhci_keyslot_valid(struct cqhci_host *host,
|
||||
unsigned int slot)
|
||||
{
|
||||
/*
|
||||
* The actual number of configurations supported is (CFGC+1), so slot
|
||||
* numbers range from 0 to config_count inclusive.
|
||||
*/
|
||||
return slot < cqhci_num_keyslots(host);
|
||||
}
|
||||
|
||||
static inline bool cqhci_host_is_crypto_supported(struct cqhci_host *host)
|
||||
{
|
||||
return host->crypto_capabilities.reg_val != 0;
|
||||
}
|
||||
|
||||
static inline bool cqhci_is_crypto_enabled(struct cqhci_host *host)
|
||||
{
|
||||
return host->caps & CQHCI_CAP_CRYPTO_SUPPORT;
|
||||
}
|
||||
|
||||
/* Functions implementing eMMC v5.2 specification behaviour */
|
||||
int cqhci_prepare_crypto_desc_spec(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx);
|
||||
|
||||
void cqhci_crypto_enable_spec(struct cqhci_host *host);
|
||||
|
||||
void cqhci_crypto_disable_spec(struct cqhci_host *host);
|
||||
|
||||
int cqhci_host_init_crypto_spec(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops);
|
||||
|
||||
void cqhci_crypto_setup_rq_keyslot_manager_spec(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
|
||||
void cqhci_crypto_destroy_rq_keyslot_manager_spec(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
|
||||
void cqhci_crypto_set_vops(struct cqhci_host *host,
|
||||
struct cqhci_host_crypto_variant_ops *crypto_vops);
|
||||
|
||||
/* Crypto Variant Ops Support */
|
||||
|
||||
void cqhci_crypto_enable(struct cqhci_host *host);
|
||||
|
||||
void cqhci_crypto_disable(struct cqhci_host *host);
|
||||
|
||||
int cqhci_host_init_crypto(struct cqhci_host *host);
|
||||
|
||||
void cqhci_crypto_setup_rq_keyslot_manager(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
|
||||
void cqhci_crypto_destroy_rq_keyslot_manager(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
|
||||
int cqhci_crypto_get_ctx(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx);
|
||||
|
||||
int cqhci_complete_crypto_desc(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx);
|
||||
|
||||
void cqhci_crypto_debug(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_suspend(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_resume(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_reset(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_recovery_finish(struct cqhci_host *host);
|
||||
|
||||
int cqhci_crypto_cap_find(void *host_p, enum blk_crypto_mode_num crypto_mode,
|
||||
unsigned int data_unit_size);
|
||||
|
||||
#else /* CONFIG_MMC_CQHCI_CRYPTO */
|
||||
|
||||
static inline bool cqhci_keyslot_valid(struct cqhci_host *host,
|
||||
unsigned int slot)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cqhci_host_is_crypto_supported(struct cqhci_host *host)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool cqhci_is_crypto_enabled(struct cqhci_host *host)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void cqhci_crypto_enable(struct cqhci_host *host) { }
|
||||
|
||||
static inline int cqhci_crypto_cap_find(void *host_p,
|
||||
enum blk_crypto_mode_num crypto_mode,
|
||||
unsigned int data_unit_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cqhci_crypto_disable(struct cqhci_host *host) { }
|
||||
|
||||
static inline int cqhci_host_init_crypto(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cqhci_crypto_setup_rq_keyslot_manager(
|
||||
struct cqhci_host *host,
|
||||
struct request_queue *q) { }
|
||||
|
||||
static inline void
|
||||
cqhci_crypto_destroy_rq_keyslot_manager(struct cqhci_host *host,
|
||||
struct request_queue *q) { }
|
||||
|
||||
static inline int cqhci_crypto_get_ctx(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx)
|
||||
{
|
||||
*ice_ctx = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cqhci_complete_crypto_desc(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cqhci_crypto_debug(struct cqhci_host *host) { }
|
||||
|
||||
static inline void cqhci_crypto_set_vops(struct cqhci_host *host,
|
||||
struct cqhci_host_crypto_variant_ops *crypto_vops) { }
|
||||
|
||||
static inline int cqhci_crypto_suspend(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cqhci_crypto_resume(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cqhci_crypto_reset(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int cqhci_crypto_recovery_finish(struct cqhci_host *host)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MMC_CQHCI_CRYPTO */
|
||||
#endif /* _CQHCI_CRYPTO_H */
|
||||
|
||||
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/mmc/card.h>
|
||||
|
||||
#include "cqhci.h"
|
||||
#include "cqhci-crypto.h"
|
||||
|
||||
#define DCMD_SLOT 31
|
||||
#define NUM_SLOTS 32
|
||||
|
|
@ -138,6 +139,8 @@ static void cqhci_dumpregs(struct cqhci_host *cq_host)
|
|||
cqhci_readl(cq_host, CQHCI_CRI),
|
||||
cqhci_readl(cq_host, CQHCI_CRA));
|
||||
|
||||
cqhci_crypto_debug(cq_host);
|
||||
|
||||
if (cq_host->ops->dumpregs)
|
||||
cq_host->ops->dumpregs(mmc);
|
||||
else
|
||||
|
|
@ -258,6 +261,11 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
|
|||
if (cq_host->caps & CQHCI_TASK_DESC_SZ_128)
|
||||
cqcfg |= CQHCI_TASK_DESC_SZ;
|
||||
|
||||
if (cqhci_host_is_crypto_supported(cq_host)) {
|
||||
cqhci_crypto_enable(cq_host);
|
||||
cqcfg |= CQHCI_ICE_ENABLE;
|
||||
}
|
||||
|
||||
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
|
||||
|
||||
cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base),
|
||||
|
|
@ -290,6 +298,9 @@ static void __cqhci_disable(struct cqhci_host *cq_host)
|
|||
{
|
||||
u32 cqcfg;
|
||||
|
||||
if (cqhci_host_is_crypto_supported(cq_host))
|
||||
cqhci_crypto_disable(cq_host);
|
||||
|
||||
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
|
||||
cqcfg &= ~CQHCI_ENABLE;
|
||||
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
|
||||
|
|
@ -303,6 +314,8 @@ int cqhci_deactivate(struct mmc_host *mmc)
|
|||
{
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
|
||||
cqhci_crypto_suspend(cq_host);
|
||||
|
||||
if (cq_host->enabled && cq_host->activated)
|
||||
__cqhci_disable(cq_host);
|
||||
|
||||
|
|
@ -561,6 +574,30 @@ static inline int cqhci_tag(struct mmc_request *mrq)
|
|||
return mrq->cmd ? DCMD_SLOT : mrq->tag;
|
||||
}
|
||||
|
||||
static inline
|
||||
void cqhci_prep_crypto_desc(struct cqhci_host *cq_host, u64 *task_desc,
|
||||
u64 ice_ctx)
|
||||
{
|
||||
__le64 *ice_desc = NULL;
|
||||
|
||||
if (cq_host->caps & CQHCI_CAP_CRYPTO_SUPPORT) {
|
||||
/*
|
||||
* Get the address of ice context for the given task descriptor.
|
||||
* ice context is present in the upper 64bits of task descriptor
|
||||
* ice_conext_base_address = task_desc + 8-bytes
|
||||
*/
|
||||
ice_desc = (__le64 *)((u8 *)task_desc +
|
||||
CQHCI_TASK_DESC_ICE_PARAM_OFFSET);
|
||||
memset(ice_desc, 0, CQHCI_TASK_DESC_ICE_PARAMS_SIZE);
|
||||
|
||||
/*
|
||||
* Assign upper 64bits data of task descritor with ice context
|
||||
*/
|
||||
if (ice_ctx)
|
||||
*ice_desc = cpu_to_le64(ice_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
int err = 0;
|
||||
|
|
@ -569,6 +606,7 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
int tag = cqhci_tag(mrq);
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
unsigned long flags;
|
||||
u64 ice_ctx = 0;
|
||||
|
||||
if (!cq_host->enabled) {
|
||||
pr_err("%s: cqhci: not enabled\n", mmc_hostname(mmc));
|
||||
|
|
@ -592,9 +630,17 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
}
|
||||
|
||||
if (mrq->data) {
|
||||
err = cqhci_crypto_get_ctx(cq_host, mrq, &ice_ctx);
|
||||
if (err) {
|
||||
pr_err("%s: failed to retrieve crypto ctx for tag %d\n",
|
||||
mmc_hostname(mmc), tag);
|
||||
goto out;
|
||||
}
|
||||
task_desc = (__le64 __force *)get_desc(cq_host, tag);
|
||||
cqhci_prep_task_desc(mrq, &data, 1);
|
||||
*task_desc = cpu_to_le64(data);
|
||||
cqhci_prep_crypto_desc(cq_host, task_desc, ice_ctx);
|
||||
|
||||
err = cqhci_prep_tran_desc(mrq, cq_host, tag);
|
||||
if (err) {
|
||||
pr_err("%s: cqhci: failed to setup tx desc: %d\n",
|
||||
|
|
@ -628,6 +674,9 @@ out_unlock:
|
|||
if (err)
|
||||
cqhci_post_req(mmc, mrq);
|
||||
|
||||
if (mrq->data)
|
||||
cqhci_complete_crypto_desc(cq_host, mrq, NULL);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -635,7 +684,15 @@ out_unlock:
|
|||
static void cqhci_crypto_update_queue(struct mmc_host *mmc,
|
||||
struct request_queue *queue)
|
||||
{
|
||||
//struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
|
||||
if (cq_host->caps & CQHCI_CAP_CRYPTO_SUPPORT) {
|
||||
if (queue)
|
||||
cqhci_crypto_setup_rq_keyslot_manager(cq_host, queue);
|
||||
else
|
||||
pr_err("%s can not register keyslot manager\n",
|
||||
mmc_hostname(mmc));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -755,6 +812,8 @@ static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag)
|
|||
|
||||
data = mrq->data;
|
||||
if (data) {
|
||||
cqhci_complete_crypto_desc(cq_host, mrq, NULL);
|
||||
|
||||
if (data->error)
|
||||
data->bytes_xfered = 0;
|
||||
else
|
||||
|
|
@ -942,6 +1001,8 @@ static void cqhci_recovery_start(struct mmc_host *mmc)
|
|||
|
||||
pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__);
|
||||
|
||||
cqhci_crypto_reset(cq_host);
|
||||
|
||||
WARN_ON(!cq_host->recovery_halt);
|
||||
|
||||
cqhci_halt(mmc, CQHCI_START_HALT_TIMEOUT);
|
||||
|
|
@ -1062,6 +1123,8 @@ static void cqhci_recovery_finish(struct mmc_host *mmc)
|
|||
|
||||
cqhci_set_irqs(cq_host, CQHCI_IS_MASK);
|
||||
|
||||
cqhci_crypto_recovery_finish(cq_host);
|
||||
|
||||
pr_debug("%s: cqhci: recovery done\n", mmc_hostname(mmc));
|
||||
}
|
||||
|
||||
|
|
@ -1103,6 +1166,11 @@ struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "failed to remap cqhci regs\n");
|
||||
return ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMC_CQHCI_CRYPTO_QTI
|
||||
cq_host->pdev = pdev;
|
||||
#endif /* CONFIG_MMC_CQHCI_CRYPTO_QTI */
|
||||
|
||||
dev_dbg(&pdev->dev, "CMDQ ioremap: done\n");
|
||||
|
||||
return cq_host;
|
||||
|
|
@ -1148,6 +1216,13 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc,
|
|||
|
||||
spin_lock_init(&cq_host->lock);
|
||||
|
||||
err = cqhci_host_init_crypto(cq_host);
|
||||
if (err) {
|
||||
pr_err("%s: CQHCI version %u.%02u Crypto init failed err %d\n",
|
||||
mmc_hostname(mmc), cqhci_ver_major(cq_host),
|
||||
cqhci_ver_minor(cq_host), err);
|
||||
}
|
||||
|
||||
init_completion(&cq_host->halt_comp);
|
||||
init_waitqueue_head(&cq_host->wait_queue);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/wait.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/keyslot-manager.h>
|
||||
|
||||
/* registers */
|
||||
/* version */
|
||||
|
|
@ -31,6 +32,7 @@
|
|||
#define CQHCI_DCMD 0x00001000
|
||||
#define CQHCI_TASK_DESC_SZ 0x00000100
|
||||
#define CQHCI_ENABLE 0x00000001
|
||||
#define CQHCI_ICE_ENABLE 0x00000002
|
||||
|
||||
/* control */
|
||||
#define CQHCI_CTL 0x0C
|
||||
|
|
@ -147,11 +149,20 @@
|
|||
#define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16)
|
||||
#define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32)
|
||||
#define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0)
|
||||
#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)
|
||||
|
||||
/* ICE context is present in the upper 64bits of task descriptor */
|
||||
#define CQHCI_TASK_DESC_ICE_PARAM_OFFSET 8
|
||||
/* ICE descriptor size */
|
||||
#define CQHCI_TASK_DESC_ICE_PARAMS_SIZE 8
|
||||
|
||||
struct cqhci_host_ops;
|
||||
struct mmc_host;
|
||||
struct mmc_request;
|
||||
struct cqhci_slot;
|
||||
struct cqhci_host;
|
||||
|
||||
/* CCAP - Crypto Capability 100h */
|
||||
union cqhci_crypto_capabilities {
|
||||
|
|
@ -208,9 +219,38 @@ union cqhci_crypto_cfg_entry {
|
|||
};
|
||||
};
|
||||
|
||||
struct cqhci_host_crypto_variant_ops {
|
||||
void (*setup_rq_keyslot_manager)(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
void (*destroy_rq_keyslot_manager)(struct cqhci_host *host,
|
||||
struct request_queue *q);
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
int (*host_init_crypto)(struct cqhci_host *host,
|
||||
const struct keyslot_mgmt_ll_ops *ksm_ops);
|
||||
#endif
|
||||
void (*enable)(struct cqhci_host *host);
|
||||
void (*disable)(struct cqhci_host *host);
|
||||
int (*suspend)(struct cqhci_host *host);
|
||||
int (*resume)(struct cqhci_host *host);
|
||||
int (*debug)(struct cqhci_host *host);
|
||||
int (*prepare_crypto_desc)(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx);
|
||||
int (*complete_crypto_desc)(struct cqhci_host *host,
|
||||
struct mmc_request *mrq,
|
||||
u64 *ice_ctx);
|
||||
int (*reset)(struct cqhci_host *host);
|
||||
int (*recovery_finish)(struct cqhci_host *host);
|
||||
int (*program_key)(struct cqhci_host *host,
|
||||
const union cqhci_crypto_cfg_entry *cfg,
|
||||
int slot);
|
||||
void *priv;
|
||||
};
|
||||
|
||||
struct cqhci_host {
|
||||
const struct cqhci_host_ops *ops;
|
||||
void __iomem *mmio;
|
||||
void __iomem *icemmio;
|
||||
struct mmc_host *mmc;
|
||||
|
||||
spinlock_t lock;
|
||||
|
|
@ -226,6 +266,7 @@ struct cqhci_host {
|
|||
u32 dcmd_slot;
|
||||
u32 caps;
|
||||
#define CQHCI_TASK_DESC_SZ_128 0x1
|
||||
#define CQHCI_CAP_CRYPTO_SUPPORT 0x2
|
||||
|
||||
u32 quirks;
|
||||
#define CQHCI_QUIRK_SHORT_TXFR_DESC_SZ 0x1
|
||||
|
|
@ -262,6 +303,19 @@ struct cqhci_host {
|
|||
struct completion halt_comp;
|
||||
wait_queue_head_t wait_queue;
|
||||
struct cqhci_slot *slot;
|
||||
const struct cqhci_host_crypto_variant_ops *crypto_vops;
|
||||
|
||||
#ifdef CONFIG_MMC_CQHCI_CRYPTO
|
||||
union cqhci_crypto_capabilities crypto_capabilities;
|
||||
union cqhci_crypto_cap_entry *crypto_cap_array;
|
||||
u32 crypto_cfg_register;
|
||||
#ifdef CONFIG_BLK_INLINE_ENCRYPTION
|
||||
struct keyslot_manager *ksm;
|
||||
#endif /* CONFIG_BLK_INLINE_ENCRYPTION */
|
||||
#endif /* CONFIG_MMC_CQHCI_CRYPTO */
|
||||
#ifdef CONFIG_MMC_CQHCI_CRYPTO_QTI
|
||||
struct platform_device *pdev;
|
||||
#endif /* CONFIG_MMC_CQHCI_CRYPTO_QTI */
|
||||
};
|
||||
|
||||
struct cqhci_host_ops {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
#include "cqhci-crypto-qti.h"
|
||||
#if defined(CONFIG_SDC_QTI)
|
||||
#include "../core/core.h"
|
||||
#endif
|
||||
|
|
@ -3103,6 +3104,14 @@ static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
|
|||
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
/*
|
||||
* Set the vendor specific ops needed for ICE.
|
||||
* Default implementation if the ops are not set.
|
||||
*/
|
||||
#ifdef CONFIG_MMC_CQHCI_CRYPTO_QTI
|
||||
cqhci_crypto_qti_set_vops(cq_host);
|
||||
#endif
|
||||
|
||||
ret = cqhci_init(cq_host, host->mmc, dma64);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n",
|
||||
|
|
|
|||
Loading…
Reference in a new issue