unplugged-kernel/drivers/mmc/core/mmc_crypto.c

348 lines
8.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include <crypto/algapi.h>
#include <linux/mmc/host.h>
#include "mmc_crypto.h"
#include "queue.h"
static bool mmc_cap_idx_valid(struct mmc_host *host, u8 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 swcqhci_crypto_key_size size)
{
switch (size) {
case SWCQHCI_CRYPTO_KEY_SIZE_128: return 16;
case SWCQHCI_CRYPTO_KEY_SIZE_192: return 24;
case SWCQHCI_CRYPTO_KEY_SIZE_256: return 32;
default: return 0;
}
}
static u8 mmc_crypto_cap_find(void *mmc_p,
enum blk_crypto_mode_num crypto_mode,
unsigned int data_unit_size)
{
struct mmc_host *host = mmc_p;
enum swcqhci_crypto_alg mmc_alg;
u8 data_unit_mask, cap_idx;
enum swcqhci_crypto_key_size mmc_key_size;
union swcqhci_crypto_cap_entry *ccap_array = host->crypto_cap_array;
if (!mmc_is_crypto_supported(host))
return -EINVAL;
switch (crypto_mode) {
case BLK_ENCRYPTION_MODE_AES_256_XTS:
/* "4" means XTS */
mmc_alg = SWCQHCI_CRYPTO_ALG_AES_XTS;
/* "2" means 256 bits */
mmc_key_size = SWCQHCI_CRYPTO_KEY_SIZE_256;
break;
default:
return -EINVAL;
}
data_unit_mask = get_data_unit_size_mask(data_unit_size);
/* There is only one capability */
for (cap_idx = 0; cap_idx < host->crypto_capabilities.num_crypto_cap;
cap_idx++) {
if (ccap_array[cap_idx].algorithm_id == mmc_alg &&
(ccap_array[cap_idx].sdus_mask & data_unit_mask) &&
ccap_array[cap_idx].key_size == mmc_key_size)
return cap_idx;
}
return -EINVAL;
}
/**
* mmc_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 mmc_crypto_cfg_entry_write_key(union swcqhci_crypto_cfg_entry *cfg,
const u8 *key,
union swcqhci_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 SWCQHCI_CRYPTO_ALG_INVALID: /* non-cqe */
case SWCQHCI_CRYPTO_ALG_AES_XTS:
key_size_bytes *= 2;
if (key_size_bytes > MMC_CRYPTO_KEY_MAX_SIZE)
return -EINVAL;
memcpy(cfg->crypto_key, key, key_size_bytes/2);
memcpy(cfg->crypto_key + MMC_CRYPTO_KEY_MAX_SIZE/2,
key + key_size_bytes/2, key_size_bytes/2);
return 0;
case SWCQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC: // fallthrough
case SWCQHCI_CRYPTO_ALG_AES_ECB: // fallthrough
case SWCQHCI_CRYPTO_ALG_ESSIV_AES_CBC:
memcpy(cfg->crypto_key, key, key_size_bytes);
return 0;
}
return -EINVAL;
}
static int mmc_crypto_keyslot_program(struct keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int slot)
{
struct mmc_host *host = keyslot_manager_private(ksm);
int err = 0;
u8 data_unit_mask;
union swcqhci_crypto_cfg_entry cfg;
union swcqhci_crypto_cfg_entry *cfg_arr;
u8 cap_idx;
if (!host || !key)
return -EINVAL;
cfg_arr = host->crypto_cfgs;
cap_idx = mmc_crypto_cap_find(host, key->crypto_mode,
key->data_unit_size);
if (!mmc_is_crypto_enabled(host) ||
!mmc_keyslot_valid(host, slot) ||
!mmc_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;
#ifdef CONFIG_MMC_CRYPTO_LEGACY
/* used fsrypt v2 in OTA fscrypt v1 environment */
if (key->hie_duint_size != 4096)
cfg.data_unit_size = 1;
#endif
cfg.crypto_cap_idx = cap_idx;
cfg.config_enable |= MMC_CRYPTO_CONFIGURATION_ENABLE;
err = mmc_crypto_cfg_entry_write_key(&cfg, key->raw,
host->crypto_cap_array[cap_idx]);
if (err)
return err;
memcpy(&cfg_arr[slot], &cfg, sizeof(cfg));
memzero_explicit(&cfg, sizeof(cfg));
return 0;
}
static int mmc_crypto_keyslot_evict(struct keyslot_manager *ksm,
const struct blk_crypto_key *key,
unsigned int slot)
{
struct mmc_host *host = keyslot_manager_private(ksm);
union swcqhci_crypto_cfg_entry *cfg_arr = host->crypto_cfgs;
if (!mmc_is_crypto_enabled(host) ||
!mmc_keyslot_valid(host, slot))
return -EINVAL;
memset(&cfg_arr[slot], 0, sizeof(cfg_arr[slot]));
return 0;
}
static const struct keyslot_mgmt_ll_ops swcq_ksm_ops = {
.keyslot_program = mmc_crypto_keyslot_program,
.keyslot_evict = mmc_crypto_keyslot_evict,
};
/**
* mmc_init_crypto_spec - 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.
*/
static int mmc_init_crypto_spec(struct mmc_host *host,
const struct keyslot_mgmt_ll_ops *ksm_ops)
{
int err;
u32 count;
unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX] = {0};
if (host->ksm)
return 0;
if (!(host->caps2 & MMC_CAP2_CRYPTO)) {
err = -ENODEV;
goto out;
}
/*
* Crypto Capabilities should never be 0, because the
* config_array_ptr != 0. So we use a 0 value to indicate that
* crypto init failed, and can't be enabled.
*/
if (host->crypto_vops->host_init_crypto)
host->crypto_vops->host_init_crypto(host);
count = ((u32)(host->crypto_capabilities.num_crypto_cap) & 0xFF);
host->crypto_cap_array =
devm_kcalloc(&host->class_dev,
count,
sizeof(host->crypto_cap_array[0]),
GFP_KERNEL);
if (!host->crypto_cap_array) {
err = -ENOMEM;
goto out;
}
/*
* Store all the capabilities now so that we don't need to repeatedly
* access the device each time we want to know its capabilities
*/
if (host->crypto_vops->get_crypto_capabilities)
err = host->crypto_vops->get_crypto_capabilities(host);
if (err)
goto out_free_cfg_mem;
host->crypto_cfgs =
devm_kcalloc(&host->class_dev,
NUM_KEYSLOTS(host),
sizeof(host->crypto_cfgs[0]),
GFP_KERNEL);
if (!host->crypto_cfgs) {
err = -ENOMEM;
goto out_free_cfg_mem;
}
/* Peng: temp */
crypto_modes_supported[1] = 0x1200;
host->ksm = keyslot_manager_create(host->parent,
NUM_KEYSLOTS(host), ksm_ops,
BLK_CRYPTO_FEATURE_STANDARD_KEYS,
crypto_modes_supported, host);
if (!host->ksm) {
err = -ENOMEM;
goto out_free_crypto_cfgs;
}
return 0;
out_free_crypto_cfgs:
devm_kfree(&host->class_dev, host->crypto_cfgs);
out_free_cfg_mem:
devm_kfree(&host->class_dev, 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;
}
static int mmc_prepare_mqr_crypto_spec(struct mmc_host *host,
struct mmc_queue_req *mqr)
{
struct bio_crypt_ctx *bc;
struct request *request = mmc_queue_req_to_req(mqr);
if (!request->bio ||
!bio_crypt_should_process(request)) {
return 0;
}
bc = request->bio->bi_crypt_context;
if (!mmc_keyslot_valid(host, bc->bc_keyslot))
return -EINVAL;
mqr->brq.mrq.crypto_key_slot = bc->bc_keyslot;
/*
* OTA with ext4 (dun is 512 bytes) used LBA,
* with F2FS (dun is 512 bytes), the dun[0] had
* multiplied by 8.
*/
if (bc->hie_ext4 == true)
mqr->brq.mrq.data_unit_num =
(blk_rq_pos(request) & 0xFFFFFFFF);
else
mqr->brq.mrq.data_unit_num = bc->bc_dun[0];
mqr->brq.mrq.crypto_key = bc->bc_key;
return 0;
}
int mmc_init_crypto(struct mmc_host *host)
{
if (!(host->caps2 & MMC_CAP2_NO_SD))
return 0;
host->caps2 |= MMC_CAP2_CRYPTO;
return mmc_init_crypto_spec(host, &swcq_ksm_ops);
}
int mmc_swcq_prepare_mqr_crypto(struct mmc_host *host,
struct mmc_request *mrq)
{
int ret, ddir, slot, tag;
struct request *req;
struct mmc_queue_req *mqr;
req = mrq->req;
mqr = req_to_mmc_queue_req(req);
ret = mmc_prepare_mqr_crypto_spec(host, mqr);
if (ret || !mmc_request_crypto_enabled(&(mqr->brq.mrq)))
return ret;
/* non-CQE */
if (!(host->caps2 & (MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD))) {
ddir = rq_data_dir(req);
slot = req->bio->bi_crypt_context->bc_keyslot;
tag = mqr->brq.mrq.tag;
if (host->crypto_vops && host->crypto_vops->prepare_mqr_crypto)
return host->crypto_vops->prepare_mqr_crypto(host,
mqr->brq.mrq.data_unit_num, ddir, tag, slot);
}
return 0;
}
int mmc_complete_mqr_crypto(struct mmc_host *host)
{
if (host->crypto_vops && host->crypto_vops->complete_mqr_crypto)
return host->crypto_vops->complete_mqr_crypto(host);
return 0;
}