From patchwork Fri May 1 04:51:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Biggers X-Patchwork-Id: 11522057 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9370B1805 for ; Fri, 1 May 2020 04:52:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7C6FC20857 for ; Fri, 1 May 2020 04:52:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1588308733; bh=Lxwo4gXMkJPbV+9fyX3CpmYZkaiMuOCZFbeBlGPFN1Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=IiFX6e//7eYWfXobRq9pMJWhhpetYzk1JzJYMMCOlQf45UwPnd3u5UHVbMPjZWwTg M6GEOh7UC2rISgh52jD5niIarsb/4pRCIemcS3KTEiYQ6EA2tHc2UakhGsxrKUD9xg Ev9t1gdsV6ZJzkFKaj9m2o5ToqQvoPFFP5ekrTT8= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728220AbgEAEwN (ORCPT ); Fri, 1 May 2020 00:52:13 -0400 Received: from mail.kernel.org ([198.145.29.99]:57308 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728159AbgEAEwL (ORCPT ); Fri, 1 May 2020 00:52:11 -0400 Received: from sol.hsd1.ca.comcast.net (c-107-3-166-239.hsd1.ca.comcast.net [107.3.166.239]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 7C112208CA; Fri, 1 May 2020 04:52:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1588308729; bh=Lxwo4gXMkJPbV+9fyX3CpmYZkaiMuOCZFbeBlGPFN1Q=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ISiwMKHOIx9K4WPrbh0LZRw3qadinw5UY743uR8kNbdCQR60kTQOKV8ah8/RgB/Aw Ka7SK0kFgd9kaIaKJqICSbqS5rXx4o0ct4Zy5HuNq/mnRWNzbrxoTB1DleLeRzqWTK c1dO8zJkEIQj+zv79GPitu+5Cjr9M/HqTRLGhXlY= From: Eric Biggers To: linux-scsi@vger.kernel.org, linux-arm-msm@vger.kernel.org Cc: linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, Alim Akhtar , Andy Gross , Avri Altman , Barani Muthukumaran , Bjorn Andersson , Can Guo , Elliot Berman , John Stultz , Satya Tangirala Subject: [RFC PATCH v4 1/4] firmware: qcom_scm: Add support for programming inline crypto keys Date: Thu, 30 Apr 2020 21:51:08 -0700 Message-Id: <20200501045111.665881-2-ebiggers@kernel.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200501045111.665881-1-ebiggers@kernel.org> References: <20200501045111.665881-1-ebiggers@kernel.org> MIME-Version: 1.0 Sender: linux-fscrypt-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fscrypt@vger.kernel.org From: Eric Biggers Add support for the Inline Crypto Engine (ICE) key programming interface that's needed for the ufs-qcom driver to use inline encryption on Snapdragon SoCs. This interface consists of two SCM calls: one to program a key into a keyslot, and one to invalidate a keyslot. Although the UFS specification defines a standard way to do this, on these SoCs the Linux kernel isn't permitted to access the needed crypto configuration registers directly; these SCM calls must be used instead. Signed-off-by: Eric Biggers Acked-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 101 ++++++++++++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 4 ++ include/linux/qcom_scm.h | 19 +++++++ 3 files changed, 124 insertions(+) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 059bb0fbae9e5b..646f9613393612 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -926,6 +926,107 @@ int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) } EXPORT_SYMBOL(qcom_scm_ocmem_unlock); +/** + * qcom_scm_ice_available() - Is the ICE key programming interface available? + * + * Return: true iff the SCM calls wrapped by qcom_scm_ice_invalidate_key() and + * qcom_scm_ice_set_key() are available. + */ +bool qcom_scm_ice_available(void) +{ + return __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, + QCOM_SCM_ES_INVALIDATE_ICE_KEY) && + __qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_ES, + QCOM_SCM_ES_CONFIG_SET_ICE_KEY); +} +EXPORT_SYMBOL(qcom_scm_ice_available); + +/** + * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key + * @index: the keyslot to invalidate + * + * The UFSHCI standard defines a standard way to do this, but it doesn't work on + * these SoCs; only this SCM call does. + * + * Return: 0 on success; -errno on failure. + */ +int qcom_scm_ice_invalidate_key(u32 index) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_ES, + .cmd = QCOM_SCM_ES_INVALIDATE_ICE_KEY, + .arginfo = QCOM_SCM_ARGS(1), + .args[0] = index, + .owner = ARM_SMCCC_OWNER_SIP, + }; + + return qcom_scm_call(__scm->dev, &desc, NULL); +} +EXPORT_SYMBOL(qcom_scm_ice_invalidate_key); + +/** + * qcom_scm_ice_set_key() - Set an inline encryption key + * @index: the keyslot into which to set the key + * @key: the key to program + * @key_size: the size of the key in bytes + * @cipher: the encryption algorithm the key is for + * @data_unit_size: the encryption data unit size, i.e. the size of each + * individual plaintext and ciphertext. Given in 512-byte + * units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc. + * + * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it + * can then be used to encrypt/decrypt UFS I/O requests inline. + * + * The UFSHCI standard defines a standard way to do this, but it doesn't work on + * these SoCs; only this SCM call does. + * + * Return: 0 on success; -errno on failure. + */ +int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, + enum qcom_scm_ice_cipher cipher, u32 data_unit_size) +{ + struct qcom_scm_desc desc = { + .svc = QCOM_SCM_SVC_ES, + .cmd = QCOM_SCM_ES_CONFIG_SET_ICE_KEY, + .arginfo = QCOM_SCM_ARGS(5, QCOM_SCM_VAL, QCOM_SCM_RW, + QCOM_SCM_VAL, QCOM_SCM_VAL, + QCOM_SCM_VAL), + .args[0] = index, + .args[2] = key_size, + .args[3] = cipher, + .args[4] = data_unit_size, + .owner = ARM_SMCCC_OWNER_SIP, + }; + void *keybuf; + dma_addr_t key_phys; + int ret; + + /* + * 'key' may point to vmalloc()'ed memory, but we need to pass a + * physical address that's been properly flushed. The sanctioned way to + * do this is by using the DMA API. But as is best practice for crypto + * keys, we also must wipe the key after use. This makes kmemdup() + + * dma_map_single() not clearly correct, since the DMA API can use + * bounce buffers. Instead, just use dma_alloc_coherent(). Programming + * keys is normally rare and thus not performance-critical. + */ + + keybuf = dma_alloc_coherent(__scm->dev, key_size, &key_phys, + GFP_KERNEL); + if (!keybuf) + return -ENOMEM; + memcpy(keybuf, key, key_size); + desc.args[1] = key_phys; + + ret = qcom_scm_call(__scm->dev, &desc, NULL); + + memzero_explicit(keybuf, key_size); + + dma_free_coherent(__scm->dev, key_size, keybuf, key_phys); + return ret; +} +EXPORT_SYMBOL(qcom_scm_ice_set_key); + /** * qcom_scm_hdcp_available() - Check if secure environment supports HDCP. * diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index d9ed670da222c8..38ea614d29fea2 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -103,6 +103,10 @@ extern int scm_legacy_call(struct device *dev, const struct qcom_scm_desc *desc, #define QCOM_SCM_OCMEM_LOCK_CMD 0x01 #define QCOM_SCM_OCMEM_UNLOCK_CMD 0x02 +#define QCOM_SCM_SVC_ES 0x10 /* Enterprise Security */ +#define QCOM_SCM_ES_INVALIDATE_ICE_KEY 0x03 +#define QCOM_SCM_ES_CONFIG_SET_ICE_KEY 0x04 + #define QCOM_SCM_SVC_HDCP 0x11 #define QCOM_SCM_HDCP_INVOKE 0x01 diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 3d6a2469776153..2e1193a3fb5f06 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -44,6 +44,13 @@ enum qcom_scm_sec_dev_id { QCOM_SCM_ICE_DEV_ID = 20, }; +enum qcom_scm_ice_cipher { + QCOM_SCM_ICE_CIPHER_AES_128_XTS = 0, + QCOM_SCM_ICE_CIPHER_AES_128_CBC = 1, + QCOM_SCM_ICE_CIPHER_AES_256_XTS = 3, + QCOM_SCM_ICE_CIPHER_AES_256_CBC = 4, +}; + #define QCOM_SCM_VMID_HLOS 0x3 #define QCOM_SCM_VMID_MSS_MSA 0xF #define QCOM_SCM_VMID_WLAN 0x18 @@ -88,6 +95,12 @@ extern int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, extern int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size); +extern bool qcom_scm_ice_available(void); +extern int qcom_scm_ice_invalidate_key(u32 index); +extern int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, + enum qcom_scm_ice_cipher cipher, + u32 data_unit_size); + extern bool qcom_scm_hdcp_available(void); extern int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp); @@ -138,6 +151,12 @@ static inline int qcom_scm_ocmem_lock(enum qcom_scm_ocmem_client id, u32 offset, static inline int qcom_scm_ocmem_unlock(enum qcom_scm_ocmem_client id, u32 offset, u32 size) { return -ENODEV; } +static inline bool qcom_scm_ice_available(void) { return false; } +static inline int qcom_scm_ice_invalidate_key(u32 index) { return -ENODEV; } +static inline int qcom_scm_ice_set_key(u32 index, const u8 *key, u32 key_size, + enum qcom_scm_ice_cipher cipher, + u32 data_unit_size) { return -ENODEV; } + static inline bool qcom_scm_hdcp_available(void) { return false; } static inline int qcom_scm_hdcp_req(struct qcom_scm_hdcp_req *req, u32 req_cnt, u32 *resp) { return -ENODEV; }