blob: 696aa52f0e459e2ce2b6c7205775319508729003 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Pixel UFS inline encryption support
*
* Copyright 2020 Google LLC
*
* UFS inline encryption support using FMP (Flash Memory Protector).
* Two operating modes are supported:
*
* - Hardware keys mode, also called KDN mode. In this mode, there are a
* certain number of keyslots, like there are in the UFS standard crypto.
* However, unlike the UFS standard crypto, all keys are hardware-wrapped keys
* rather than raw keys. The keys are delivered to FMP indirectly via the KDN
* (Key Distribution Network) and GSA (Google Security Anchor) rather than via
* writes to UFS registers. The way the keyslot and IV of each request are
* passed to the UFS controller also differs from the UFS standard.
*
* - Software keys mode, also called the traditional FMP mode or legacy FMP
* mode. In this mode, software specifies the raw keys to use, similar to the
* UFS standard crypto. However, the way the keys and IVs are passed to the
* UFS controller still differs from the UFS standard. This mode must be enabled
* via a Kconfig option.
*
* These two modes are not compatible with each other, and the mode to use is
* set at module load time by the "use_kdn" module parameter. Upper layers in
* the storage stack must be configured to use the appropriate type of keys when
* the mode is changed; otherwise inline encryption won't be able to be used.
*/
#include <linux/gsa/gsa_kdn.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <ufs/ufshcd.h>
#include <core/ufshcd-crypto.h>
#include "ufs-pixel.h"
#include "ufs-pixel-fips.h"
#include "ufs-pixel-crypto.h"
#undef CREATE_TRACE_POINTS
#include <trace/hooks/ufshcd.h>
#if IS_ENABLED(CONFIG_SCSI_UFS_CRYPTO_SW_KEYS_MODE)
static bool use_kdn = true;
module_param(use_kdn, bool, 0444);
MODULE_PARM_DESC(use_kdn, "Use hardware keys mode (KDN mode) for inline crypto");
#endif
static void pixel_ufs_crypto_restore_keys(void *unused, struct ufs_hba *hba,
int *err);
/*
* Format of UFS PRDT entries when the KDN is enabled and the PRDT-based
* descriptor mode is enabled. In this mode, when the data in a UFS request
* should be encrypted (or decrypted), the keyslot and IV for each 4KB of data
* is specified in the corresponding PRDT entry. This uses extra fields beyond
* the ones specified by the UFSHCI standard.
*/
struct pixel_ufs_prdt_entry {
/* The first four fields correspond to those of ufshcd_sg_entry. */
__le32 des0;
__le32 des1;
__le32 des2;
/*
* The crypto enable bit and keyslot are configured in the high bits of
* des3, whose low bits already contain ufshcd_sg_entry::size.
*/
#define CRYPTO_ENABLE (1U << 31)
#define CRYPTO_KEYSLOT(keyslot) ((keyslot) << 18)
__le32 des3;
/* The IV with all bytes reversed */
__be64 iv[2];
/* Unused (when KE=0) */
__le32 nonce[4];
/* Unused */
__le32 reserved[20];
};
/*
* Determine the number of pending commands by counting the bits in the SCSI
* device budget maps.
*/
static u32 ufshcd_pending_cmds(struct ufs_hba *hba)
{
struct scsi_device *sdev;
u32 pending = 0;
shost_for_each_device(sdev, hba->host)
pending += sbitmap_weight(&sdev->budget_map);
return pending;
}
/*
* Block new UFS requests from being issued, and wait for any outstanding UFS
* requests to complete. Must be paired with ufshcd_resume_io().
*/
static void ufshcd_block_io(struct ufs_hba *hba)
{
ktime_t deadline = ktime_add_ms(ktime_get(), 5 * 1000);
struct scsi_device *sdev;
/*
* If ufshcd_block_io() is called before the tag set has been
* initialized then we know that no I/O is ongoing.
*/
if (!hba->host->tag_set.tags)
return;
shost_for_each_device(sdev, hba->host)
blk_mq_quiesce_queue(sdev->request_queue);
while (ufshcd_pending_cmds(hba)) {
if (ktime_after(ktime_get(), deadline)) {
dev_err(hba->dev, "%s waited too long\n", __func__);
return;
}
io_schedule_timeout(msecs_to_jiffies(20));
}
}
static void ufshcd_resume_io(struct ufs_hba *hba)
{
struct scsi_device *sdev;
if (!hba->host->tag_set.tags)
return;
shost_for_each_device(sdev, hba->host)
blk_mq_unquiesce_queue(sdev->request_queue);
}
static int pixel_ufs_keyslot_program(struct blk_crypto_profile *profile,
const struct blk_crypto_key *key,
unsigned int slot)
{
struct ufs_hba *hba = container_of(profile,
struct ufs_hba, crypto_profile);
struct pixel_ufs *ufs = to_pixel_ufs(hba);
int err;
dev_info(ufs->dev,
"kdn: programming keyslot %u with %u-byte wrapped key\n",
slot, key->size);
/*
* This hardware doesn't allow any encrypted I/O at all while a keyslot
* is being modified.
*/
ufshcd_block_io(hba);
err = gsa_kdn_program_key(ufs->gsa_dev, slot, key->raw, key->size);
if (err)
dev_err(ufs->dev, "kdn: failed to program key; err=%d\n", err);
ufshcd_resume_io(hba);
return err;
}
static int pixel_ufs_keyslot_evict(struct blk_crypto_profile *profile,
const struct blk_crypto_key *key,
unsigned int slot)
{
struct ufs_hba *hba = container_of(profile,
struct ufs_hba, crypto_profile);
struct pixel_ufs *ufs = to_pixel_ufs(hba);
int err;
dev_info(ufs->dev, "kdn: evicting keyslot %u\n", slot);
/*
* This hardware doesn't allow any encrypted I/O at all while a keyslot
* is being modified.
*/
ufshcd_block_io(hba);
err = gsa_kdn_program_key(ufs->gsa_dev, slot, NULL, 0);
if (err)
dev_err(ufs->dev, "kdn: failed to evict key; err=%d\n", err);
ufshcd_resume_io(hba);
return err;
}
static int pixel_ufs_derive_sw_secret(struct blk_crypto_profile *profile,
const u8 *eph_key, size_t eph_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
{
struct ufs_hba *hba = container_of(profile,
struct ufs_hba, crypto_profile);
struct pixel_ufs *ufs = to_pixel_ufs(hba);
int ret;
dev_info(ufs->dev,
"kdn: deriving %u-byte raw secret from %zu-byte wrapped key\n",
BLK_CRYPTO_SW_SECRET_SIZE, eph_key_size);
ret = gsa_kdn_derive_raw_secret(ufs->gsa_dev, sw_secret,
BLK_CRYPTO_SW_SECRET_SIZE,
eph_key, eph_key_size);
if (ret != BLK_CRYPTO_SW_SECRET_SIZE) {
dev_err(ufs->dev, "kdn: failed to derive raw secret; ret=%d\n",
ret);
/*
* gsa_kdn_derive_raw_secret() returns -EIO on "bad key" but
* upper layers expect -EINVAL. Just always return -EINVAL.
*/
return -EINVAL;
}
return 0;
}
static const struct blk_crypto_ll_ops pixel_ufs_crypto_ops = {
.keyslot_program = pixel_ufs_keyslot_program,
.keyslot_evict = pixel_ufs_keyslot_evict,
.derive_sw_secret = pixel_ufs_derive_sw_secret,
};
static void pixel_ufs_release_gsa_device(void *_ufs)
{
struct pixel_ufs *ufs = _ufs;
put_device(ufs->gsa_dev);
}
/*
* Get the GSA device from the device tree and save a pointer to it in the UFS
* host struct.
*/
static int pixel_ufs_find_gsa_device(struct pixel_ufs *ufs)
{
struct device_node *np;
struct platform_device *gsa_pdev;
np = of_parse_phandle(ufs->dev->of_node, "gsa-device", 0);
if (!np) {
dev_warn(ufs->dev,
"gsa-device phandle not found in UFS device tree node\n");
return -ENODEV;
}
gsa_pdev = of_find_device_by_node(np);
of_node_put(np);
if (!gsa_pdev) {
dev_err(ufs->dev,
"gsa-device phandle doesn't refer to a device\n");
return -ENODEV;
}
ufs->gsa_dev = &gsa_pdev->dev;
return devm_add_action_or_reset(ufs->dev, pixel_ufs_release_gsa_device,
ufs);
}
static int pixel_ufs_crypto_init_hw_keys_mode(struct ufs_hba *hba)
{
struct pixel_ufs *ufs = to_pixel_ufs(hba);
int err;
err = pixel_ufs_find_gsa_device(ufs);
if (err == -ENODEV)
goto disable;
if (err)
return err;
if (ufs->crypto_ops->crypto_init)
err = ufs->crypto_ops->crypto_init(hba);
if (err == -ENODEV)
goto disable;
if (err)
return err;
err = register_trace_android_rvh_ufs_reprogram_all_keys(
pixel_ufs_crypto_restore_keys, NULL);
if (err)
return err;
/* Advertise crypto support to ufshcd-core. */
hba->caps |= UFSHCD_CAP_CRYPTO;
/* Advertise crypto quirks to ufshcd-core. */
/*
* We need to override the blk_keyslot_manager, firstly in order to
* override the UFSHCI standand blk_crypto_ll_ops with operations that
* program/evict wrapped keys via the KDN, and secondly in order to
* declare wrapped key support rather than standard key support.
*/
hba->android_quirks |= UFSHCD_ANDROID_QUIRK_CUSTOM_CRYPTO_PROFILE;
/*
* This host controller doesn't support the standard
* CRYPTO_GENERAL_ENABLE bit in REG_CONTROLLER_ENABLE. Instead it just
* always has crypto support enabled.
*/
hba->android_quirks |= UFSHCD_ANDROID_QUIRK_BROKEN_CRYPTO_ENABLE;
/* Advertise crypto capabilities to the block layer. */
err = devm_blk_crypto_profile_init(hba->dev, &hba->crypto_profile,
KDN_SLOT_NUM);
if (err)
return err;
hba->crypto_profile.ll_ops = pixel_ufs_crypto_ops;
/*
* The PRDT entries accept 16-byte IVs, but currently the driver passes
* the DUN through ufshcd_lrb::data_unit_num which is 8-byte. 8 bytes
* is enough for upper layers, so for now just use that as the limit.
*/
hba->crypto_profile.max_dun_bytes_supported = 8;
hba->crypto_profile.key_types_supported = BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
hba->crypto_profile.dev = ufs->dev;
hba->crypto_profile.modes_supported[BLK_ENCRYPTION_MODE_AES_256_XTS] =
CRYPTO_DATA_UNIT_SIZE;
dev_info(ufs->dev,
"enabled inline encryption support with wrapped keys\n");
return 0;
disable:
/*
* If the GSA support for wrapped keys seems to be missing, then fall
* back to disabling crypto support and continuing with driver probe.
* Attempts to use wrapped keys will fail, but any other use of UFS will
* continue to work.
*/
dev_warn(hba->dev, "disabling inline encryption support\n");
hba->caps &= ~UFSHCD_CAP_CRYPTO;
return 0;
}
/* Initialize UFS inline encryption support. */
int pixel_ufs_crypto_init(struct ufs_hba *hba)
{
#if IS_ENABLED(CONFIG_SCSI_UFS_CRYPTO_SW_KEYS_MODE)
if (!use_kdn)
return exynos_ufs_crypto_init_sw_keys_mode(hba);
#endif
return pixel_ufs_crypto_init_hw_keys_mode(hba);
}
static void pixel_ufs_crypto_restore_keys(void *unused, struct ufs_hba *hba,
int *err)
{
struct pixel_ufs *ufs = to_pixel_ufs(hba);
/*
* GSA provides a function to restore all keys which is faster than
* programming all keys individually, so use it in order to avoid
* unnecessary resume latency.
*
* GSA also relies on this function being called in order to configure
* some hardening against power analysis attacks.
*/
dev_info(ufs->dev, "kdn: restoring keys\n");
*err = gsa_kdn_restore_keys(ufs->gsa_dev);
if (*err)
dev_err(ufs->dev, "kdn: failed to restore keys; err=%d\n",
*err);
}