Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65e751ed authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "mmc: host: Add variant ops for cqhci crypto"

parents a5532e79 44934087
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -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.
+2 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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 −0
Original line number Diff line number Diff line
/* 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 −0
Original line number Diff line number Diff line
// 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;
}
Loading