staticbool qcom_ice_use_wrapped_keys;
module_param_named(use_wrapped_keys, qcom_ice_use_wrapped_keys, bool, 0660);
MODULE_PARM_DESC(use_wrapped_keys, "Support wrapped keys instead of raw keys, if available on the platform");
staticbool qcom_ice_check_supported(struct qcom_ice *ice)
{
u32 regval = qcom_ice_readl(ice, QCOM_ICE_REG_VERSION); struct device *dev = ice->dev; int major = FIELD_GET(GENMASK(31, 24), regval); int minor = FIELD_GET(GENMASK(23, 16), regval); int step = FIELD_GET(GENMASK(15, 0), regval);
/* For now this driver only supports ICE version 3 and 4. */ if (major != 3 && major != 4) {
dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n",
major, minor, step); returnfalse;
}
/* If fuses are blown, ICE might not work in the standard way. */
regval = qcom_ice_readl(ice, QCOM_ICE_REG_FUSE_SETTING); if (regval & (QCOM_ICE_FUSE_SETTING_MASK |
QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK |
QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) {
dev_warn(dev, "Fuses are blown; ICE is unusable!\n"); returnfalse;
}
/* * Check for HWKM support and decide whether to use it or not. ICE * v3.2.1 and later have HWKM v2. ICE v3.2.0 has HWKM v1. Earlier ICE * versions don't have HWKM at all. However, for HWKM to be fully * usable by Linux, the TrustZone software also needs to support certain * SCM calls including the ones to generate and prepare keys. That * effectively makes the earliest supported SoC be SM8650, which has * HWKM v2. Therefore, this driver doesn't include support for HWKM v1, * and it checks for the SCM call support before it decides to use HWKM. * * Also, since HWKM and legacy mode are mutually exclusive, and * ICE-capable storage driver(s) need to know early on whether to * advertise support for raw keys or wrapped keys, HWKM cannot be used * unconditionally. A module parameter is used to opt into using it.
*/ if ((major >= 4 ||
(major == 3 && (minor >= 3 || (minor == 2 && step >= 1)))) &&
qcom_scm_has_wrapped_key_support()) { if (qcom_ice_use_wrapped_keys) {
dev_info(dev, "Using HWKM. Supporting wrapped keys only.\n");
ice->use_hwkm = true;
} else {
dev_info(dev, "Not using HWKM. Supporting raw keys only.\n");
}
} elseif (qcom_ice_use_wrapped_keys) {
dev_warn(dev, "A supported HWKM is not present. Ignoring qcom_ice.use_wrapped_keys=1.\n");
} else {
dev_info(dev, "A supported HWKM is not present. Supporting raw keys only.\n");
} returntrue;
}
/* * Wait until the ICE BIST (built-in self-test) has completed. * * This may be necessary before ICE can be used. * Note that we don't really care whether the BIST passed or failed; * we really just want to make sure that it isn't still running. This is * because (a) the BIST is a FIPS compliance thing that never fails in * practice, (b) ICE is documented to reject crypto requests if the BIST * fails, so we needn't do it in software too, and (c) properly testing * storage encryption requires testing the full storage stack anyway, * and not relying on hardware-level self-tests.
*/ staticint qcom_ice_wait_bist_status(struct qcom_ice *ice)
{
u32 regval; int err;
err = readl_poll_timeout(ice->base + QCOM_ICE_REG_BIST_STATUS,
regval, !(regval & QCOM_ICE_BIST_STATUS_MASK),
50, 5000); if (err) {
dev_err(ice->dev, "Timed out waiting for ICE self-test to complete\n"); return err;
}
if (ice->use_hwkm &&
qcom_ice_readl(ice, QCOM_ICE_REG_HWKM_TZ_KM_STATUS) !=
(QCOM_ICE_HWKM_KT_CLEAR_DONE |
QCOM_ICE_HWKM_BOOT_CMD_LIST0_DONE |
QCOM_ICE_HWKM_BOOT_CMD_LIST1_DONE |
QCOM_ICE_HWKM_CRYPTO_BIST_DONE_V2 |
QCOM_ICE_HWKM_BIST_DONE_V2)) {
dev_err(ice->dev, "HWKM self-test error!\n"); /* * Too late to revoke use_hwkm here, as it was already * propagated up the stack into the crypto capabilities.
*/
} return 0;
}
BUILD_BUG_ON(QCOM_ICE_HWKM_WRAPPED_KEY_SIZE >
BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE); /* * When ICE is in HWKM mode, it only supports wrapped keys. * When ICE is in legacy mode, it only supports raw keys. * * Put ICE in HWKM mode. ICE defaults to legacy mode.
*/
regval = qcom_ice_readl(ice, QCOM_ICE_REG_CONTROL);
regval &= ~QCOM_ICE_LEGACY_MODE_ENABLED;
qcom_ice_writel(ice, regval, QCOM_ICE_REG_CONTROL);
/* Disable CRC checks. This HWKM feature is not used. */
qcom_ice_writel(ice, QCOM_ICE_HWKM_DISABLE_CRC_CHECKS_VAL,
QCOM_ICE_REG_HWKM_TZ_KM_CTL);
/* * Allow the HWKM slave to read and write the keyslots in the ICE HWKM * slave. Without this, TrustZone cannot program keys into ICE.
*/
qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_0);
qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_1);
qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_2);
qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_3);
qcom_ice_writel(ice, GENMASK(31, 0), QCOM_ICE_REG_HWKM_BANK0_BBAC_4);
if (!ice->use_hwkm) {
dev_err_ratelimited(dev, "Got wrapped key when not using HWKM\n"); return -EINVAL;
} if (!ice->hwkm_init_complete) {
dev_err_ratelimited(dev, "HWKM not yet initialized\n"); return -EINVAL;
}
/* Clear CFGE before programming the key. */
qcom_ice_writel(ice, 0x0, QCOM_ICE_REG_CRYPTOCFG(slot));
/* Call into TrustZone to program the wrapped key using HWKM. */
err = qcom_scm_ice_set_key(translate_hwkm_slot(ice, slot), bkey->bytes,
bkey->size, cfg.capidx, cfg.dusize); if (err) {
dev_err_ratelimited(dev, "qcom_scm_ice_set_key failed; err=%d, slot=%u\n",
err, slot); return err;
}
/* Set CFGE after programming the key. */
qcom_ice_writel(ice, le32_to_cpu(cfg.regval),
QCOM_ICE_REG_CRYPTOCFG(slot)); return 0;
}
int qcom_ice_program_key(struct qcom_ice *ice, unsignedint slot, conststruct blk_crypto_key *blk_key)
{ struct device *dev = ice->dev; union {
u8 bytes[AES_256_XTS_KEY_SIZE];
u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
} key; int i; int err;
/* Only AES-256-XTS has been tested so far. */ if (blk_key->crypto_cfg.crypto_mode !=
BLK_ENCRYPTION_MODE_AES_256_XTS) {
dev_err_ratelimited(dev, "Unsupported crypto mode: %d\n",
blk_key->crypto_cfg.crypto_mode); return -EINVAL;
}
if (blk_key->crypto_cfg.key_type == BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) return qcom_ice_program_wrapped_key(ice, slot, blk_key);
if (ice->use_hwkm) {
dev_err_ratelimited(dev, "Got raw key when using HWKM\n"); return -EINVAL;
}
int qcom_ice_evict_key(struct qcom_ice *ice, int slot)
{ if (ice->hwkm_init_complete)
slot = translate_hwkm_slot(ice, slot); return qcom_scm_ice_invalidate_key(slot);
}
EXPORT_SYMBOL_GPL(qcom_ice_evict_key);
/** * qcom_ice_get_supported_key_type() - Get the supported key type * @ice: ICE driver data * * Return: the blk-crypto key type that the ICE driver is configured to use. * This is the key type that ICE-capable storage drivers should advertise as * supported in the crypto capabilities of any disks they register.
*/ enum blk_crypto_key_type qcom_ice_get_supported_key_type(struct qcom_ice *ice)
{ if (ice->use_hwkm) return BLK_CRYPTO_KEY_TYPE_HW_WRAPPED; return BLK_CRYPTO_KEY_TYPE_RAW;
}
EXPORT_SYMBOL_GPL(qcom_ice_get_supported_key_type);
/** * qcom_ice_derive_sw_secret() - Derive software secret from wrapped key * @ice: ICE driver data * @eph_key: an ephemerally-wrapped key * @eph_key_size: size of @eph_key in bytes * @sw_secret: output buffer for the software secret * * Use HWKM to derive the "software secret" from a hardware-wrapped key that is * given in ephemerally-wrapped form. * * Return: 0 on success; -EBADMSG if the given ephemerally-wrapped key is * invalid; or another -errno value.
*/ int qcom_ice_derive_sw_secret(struct qcom_ice *ice, const u8 *eph_key, size_t eph_key_size,
u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
{ int err = qcom_scm_derive_sw_secret(eph_key, eph_key_size,
sw_secret,
BLK_CRYPTO_SW_SECRET_SIZE); if (err == -EIO || err == -EINVAL)
err = -EBADMSG; /* probably invalid key */ return err;
}
EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret);
/** * qcom_ice_generate_key() - Generate a wrapped key for inline encryption * @ice: ICE driver data * @lt_key: output buffer for the long-term wrapped key * * Use HWKM to generate a new key and return it as a long-term wrapped key. * * Return: the size of the resulting wrapped key on success; -errno on failure.
*/ int qcom_ice_generate_key(struct qcom_ice *ice,
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
{ int err;
err = qcom_scm_generate_ice_key(lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE); if (err) return err;
/** * qcom_ice_prepare_key() - Prepare a wrapped key for inline encryption * @ice: ICE driver data * @lt_key: a long-term wrapped key * @lt_key_size: size of @lt_key in bytes * @eph_key: output buffer for the ephemerally-wrapped key * * Use HWKM to re-wrap a long-term wrapped key with the per-boot ephemeral key. * * Return: the size of the resulting wrapped key on success; -EBADMSG if the * given long-term wrapped key is invalid; or another -errno value.
*/ int qcom_ice_prepare_key(struct qcom_ice *ice, const u8 *lt_key, size_t lt_key_size,
u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
{ int err;
/** * qcom_ice_import_key() - Import a raw key for inline encryption * @ice: ICE driver data * @raw_key: the raw key to import * @raw_key_size: size of @raw_key in bytes * @lt_key: output buffer for the long-term wrapped key * * Use HWKM to import a raw key and return it as a long-term wrapped key. * * Return: the size of the resulting wrapped key on success; -errno on failure.
*/ int qcom_ice_import_key(struct qcom_ice *ice, const u8 *raw_key, size_t raw_key_size,
u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
{ int err;
err = qcom_scm_import_ice_key(raw_key, raw_key_size,
lt_key, QCOM_ICE_HWKM_WRAPPED_KEY_SIZE); if (err) return err;
if (!qcom_scm_is_available()) return ERR_PTR(-EPROBE_DEFER);
if (!qcom_scm_ice_available()) {
dev_warn(dev, "ICE SCM interface not found\n"); return NULL;
}
engine = devm_kzalloc(dev, sizeof(*engine), GFP_KERNEL); if (!engine) return ERR_PTR(-ENOMEM);
engine->dev = dev;
engine->base = base;
/* * Legacy DT binding uses different clk names for each consumer, * so lets try those first. If none of those are a match, it means * the we only have one clock and it is part of the dedicated DT node. * Also, enable the clock before we check what HW version the driver * supports.
*/
engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk"); if (!engine->core_clk)
engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); if (!engine->core_clk)
engine->core_clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(engine->core_clk)) return ERR_CAST(engine->core_clk);
if (!qcom_ice_check_supported(engine)) return ERR_PTR(-EOPNOTSUPP);
/** * of_qcom_ice_get() - get an ICE instance from a DT node * @dev: device pointer for the consumer device * * This function will provide an ICE instance either by creating one for the * consumer device if its DT node provides the 'ice' reg range and the 'ice' * clock (for legacy DT style). On the other hand, if consumer provides a * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * * Return: ICE pointer on success, NULL if there is no ICE data provided by the * consumer or ERR_PTR() on error.
*/ staticstruct qcom_ice *of_qcom_ice_get(struct device *dev)
{ struct platform_device *pdev = to_platform_device(dev); struct qcom_ice *ice; struct resource *res; void __iomem *base; struct device_link *link;
if (!dev || !dev->of_node) return ERR_PTR(-ENODEV);
/* * In order to support legacy style devicetree bindings, we need * to create the ICE instance using the consumer device and the reg * range called 'ice' it provides.
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ice"); if (res) {
base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return ERR_CAST(base);
/* create ICE instance using consumer dev */ return qcom_ice_create(&pdev->dev, base);
}
/* * If the consumer node does not provider an 'ice' reg range * (legacy DT binding), then it must at least provide a phandle * to the ICE devicetree node, otherwise ICE is not supported.
*/ struct device_node *node __free(device_node) = of_parse_phandle(dev->of_node, "qcom,ice", 0); if (!node) return NULL;
ice = platform_get_drvdata(pdev); if (!ice) {
dev_err(dev, "Cannot get ice instance from %s\n",
dev_name(&pdev->dev));
platform_device_put(pdev); return ERR_PTR(-EPROBE_DEFER);
}
link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); if (!link) {
dev_err(&pdev->dev, "Failed to create device link to consumer %s\n",
dev_name(dev));
platform_device_put(pdev);
ice = ERR_PTR(-EINVAL);
}
/** * devm_of_qcom_ice_get() - Devres managed helper to get an ICE instance from * a DT node. * @dev: device pointer for the consumer device. * * This function will provide an ICE instance either by creating one for the * consumer device if its DT node provides the 'ice' reg range and the 'ice' * clock (for legacy DT style). On the other hand, if consumer provides a * phandle via 'qcom,ice' property to an ICE DT, the ICE instance will already * be created and so this function will return that instead. * * Return: ICE pointer on success, NULL if there is no ICE data provided by the * consumer or ERR_PTR() on error.
*/ struct qcom_ice *devm_of_qcom_ice_get(struct device *dev)
{ struct qcom_ice *ice, **dr;
dr = devres_alloc(devm_of_qcom_ice_put, sizeof(*dr), GFP_KERNEL); if (!dr) return ERR_PTR(-ENOMEM);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.