aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/cyanogenmod_tuna_defconfig4
-rw-r--r--arch/arm/configs/tuna_defconfig4
-rwxr-xr-xsecurity/smc/Makefile1
-rw-r--r--security/smc/tf_comm_mshield.c15
-rw-r--r--security/smc/tf_crypto.c22
-rw-r--r--security/smc/tf_crypto.h5
-rw-r--r--security/smc/tf_crypto_digest_clock_workaround.c235
7 files changed, 282 insertions, 4 deletions
diff --git a/arch/arm/configs/cyanogenmod_tuna_defconfig b/arch/arm/configs/cyanogenmod_tuna_defconfig
index c41e079..6063a00 100644
--- a/arch/arm/configs/cyanogenmod_tuna_defconfig
+++ b/arch/arm/configs/cyanogenmod_tuna_defconfig
@@ -2690,9 +2690,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_APPARMOR is not set
# CONFIG_IMA is not set
CONFIG_TF_ZEBRA=y
-# CONFIG_TF_CRYPTO_RNG is not set
+CONFIG_TF_CRYPTO_RNG=y
CONFIG_SECURITY_MIDDLEWARE_COMPONENT=y
-# CONFIG_SMC_KERNEL_CRYPTO is not set
+CONFIG_SMC_KERNEL_CRYPTO=y
CONFIG_SECURE_TRACE=y
# CONFIG_TF_DRIVER_DEBUG_SUPPORT is not set
CONFIG_DEFAULT_SECURITY_SELINUX=y
diff --git a/arch/arm/configs/tuna_defconfig b/arch/arm/configs/tuna_defconfig
index ea9342c..9544c95 100644
--- a/arch/arm/configs/tuna_defconfig
+++ b/arch/arm/configs/tuna_defconfig
@@ -2690,9 +2690,9 @@ CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
# CONFIG_SECURITY_APPARMOR is not set
# CONFIG_IMA is not set
CONFIG_TF_ZEBRA=y
-# CONFIG_TF_CRYPTO_RNG is not set
+CONFIG_TF_CRYPTO_RNG=y
CONFIG_SECURITY_MIDDLEWARE_COMPONENT=y
-# CONFIG_SMC_KERNEL_CRYPTO is not set
+CONFIG_SMC_KERNEL_CRYPTO=y
CONFIG_SECURE_TRACE=y
# CONFIG_TF_DRIVER_DEBUG_SUPPORT is not set
CONFIG_DEFAULT_SECURITY_SELINUX=y
diff --git a/security/smc/Makefile b/security/smc/Makefile
index 6b5fefd..44e74ae 100755
--- a/security/smc/Makefile
+++ b/security/smc/Makefile
@@ -35,6 +35,7 @@ tf_driver-objs += tf_device.o
tf_driver-objs += tf_comm.o
tf_driver-objs += tf_crypto.o
tf_driver-objs += tf_crypto_digest.o
+tf_driver-objs += tf_crypto_digest_clock_workaround.o
tf_driver-objs += tf_crypto_aes.o
tf_driver-objs += tf_crypto_des.o
tf_driver-objs += tf_dma.o
diff --git a/security/smc/tf_comm_mshield.c b/security/smc/tf_comm_mshield.c
index 23b6203..4b1a3f5 100644
--- a/security/smc/tf_comm_mshield.c
+++ b/security/smc/tf_comm_mshield.c
@@ -115,6 +115,20 @@ static struct wake_lock g_tf_wake_lock;
static struct clockdomain *smc_l4_sec_clkdm;
+void tf_crypto_clockdomain_wakeup(void)
+{
+ if (smc_l4_sec_clkdm) {
+ clkdm_wakeup(smc_l4_sec_clkdm);
+ }
+}
+
+void tf_crypto_clockdomain_idle(void)
+{
+ if (smc_l4_sec_clkdm) {
+ clkdm_allow_idle(smc_l4_sec_clkdm);
+ }
+}
+
static int __init tf_early_init(void)
{
g_secure_task_id = 0;
@@ -203,6 +217,7 @@ static void tf_clock_timer_cb(unsigned long data)
clkdm_allow_idle(smc_l4_sec_clkdm);
spin_unlock_irqrestore(&clk_timer_lock, flags);
+ tf_crypto_digest_apply_clock_workaround();
dprintk(KERN_INFO "%s success\n", __func__);
return;
diff --git a/security/smc/tf_crypto.c b/security/smc/tf_crypto.c
index 1be70ce..f70b087 100644
--- a/security/smc/tf_crypto.c
+++ b/security/smc/tf_crypto.c
@@ -850,6 +850,28 @@ end:
spin_unlock_irqrestore(&clk_lock, flags);
}
+u32 tf_crypto_read_clock_value(uint32_t clock_paddr)
+{
+ u32 *clock_reg;
+ u32 val;
+ unsigned long flags;
+
+ dprintk(KERN_INFO "tf_crypto_read_clock_value: " \
+ "clock_paddr=0x%08X\n",
+ clock_paddr);
+
+ /* Ensure none concurrent access when changing clock registers */
+ spin_lock_irqsave(&clk_lock, flags);
+
+ clock_reg = (u32 *)IO_ADDRESS(clock_paddr);
+
+ val = __raw_readl(clock_reg);
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return val;
+}
+
/*------------------------------------------------------------------------- */
/* CUS RPCs */
/*------------------------------------------------------------------------- */
diff --git a/security/smc/tf_crypto.h b/security/smc/tf_crypto.h
index 797b082..92fe9f0 100644
--- a/security/smc/tf_crypto.h
+++ b/security/smc/tf_crypto.h
@@ -395,4 +395,9 @@ extern char *tf_integrity_hmac_sha256_expected_value;
#endif /* CONFIG_TF_DRIVER_CRYPTO_FIPS */
+u32 tf_crypto_read_clock_value(uint32_t clock_paddr);
+void tf_crypto_digest_apply_clock_workaround(void);
+void tf_crypto_clockdomain_wakeup(void);
+void tf_crypto_clockdomain_idle(void);
+
#endif /*__TF_PUBLIC_CRYPTO_H */
diff --git a/security/smc/tf_crypto_digest_clock_workaround.c b/security/smc/tf_crypto_digest_clock_workaround.c
new file mode 100644
index 0000000..4f135ea
--- /dev/null
+++ b/security/smc/tf_crypto_digest_clock_workaround.c
@@ -0,0 +1,235 @@
+/**
+ * Copyright (c) 2016 Luden
+ * 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 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* SMC PA implementation for tuna devices has the following bug.
+ * Once SMC PA is loaded and PKCS11 session is opened at least once,
+ * SHA2MD5 clock is stuck in TRANSITION state. This consumes extra
+ * battery and prevents the device from going into sleep mode.
+ *
+ * See also the report of another user about this problem:
+ * https://e2e.ti.com/support/omap/f/849/t/235401,
+ * potentially the bug reported in this commit
+ * https://android.googlesource.com/device/samsung/tuna/+/b74801dc22bb4945ddf79b2e12e6328a862d68c3
+ * is also related.
+ *
+ * It's hard to say what is the exact cause of this bug given that
+ * no information about either SHA2MD5, SMC PA or OMAP4 PPA is public.
+ *
+ * I was able to reproduce the similar issue by setting SHA2MD5
+ * SYSCONFIG register to 0x88 (SADVANCED | SDMA_EN), potentially
+ * there's a "secure world" version of this register that is
+ * programmed incorrectly in PKCS11 session initialization?
+ *
+ * In any case, given that there's no way to modify SMC PA to fix
+ * this bug even if the exact reason was known (SMC PA is signed
+ * and the signature is verified during its loading), the whole
+ * investigation as to what causes it exactly is not that useful.
+ *
+ * However, apparently once SHA2MD5 "secure world" functionality
+ * is used at least once, the stuckness goes away (most likely HWA
+ * is re-initialized at this point in "secure world" and this time
+ * it's done properly?). It's enough to do this just once after the
+ * boot.
+ *
+ * Therefore, the code below implements a work-around for the
+ * stuckness problem by performing dummy calculation of the MD5
+ * hash of 1 byte buffer.
+ *
+ * Note that the computation has to happen in the "secure world",
+ * using just the "normal world" interface to HWA doesn't cut it. */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+
+#include "tee_client_api.h"
+#include "tf_crypto.h"
+
+#define SERVICE_SYSTEM_UUID \
+ { 0x56304b83, 0x5c4e, 0x4428, \
+ { 0xb9, 0x9e, 0x60, 0x5c, 0x96, 0xae, 0x58, 0xd6 } }
+
+#define CKF_SERIAL_SESSION 0x00000004
+/* It doesn't matter which hash is used as long as HWA supports it. */
+#define CKM_MD5 0x00000210
+
+#define SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID 0x00000026
+#define SERVICE_SYSTEM_PKCS11_C_DIGEST_COMMAND_ID 0x00000027
+#define SERVICE_SYSTEM_PKCS11_C_OPEN_SESSION_COMMAND_ID 0x00000042
+#define SERVICE_SYSTEM_PKCS11_C_CLOSE_SESSION_COMMAND_ID 0x00000043
+
+/* Exact buffer size doesn't matter as long as it's > 0. */
+#define BUFFER_SIZE 1
+/* Size of MD5 digest. */
+#define DIGEST_SIZE 16
+#define TOTAL_BUFFER_SIZE (BUFFER_SIZE + DIGEST_SIZE)
+
+static void _tf_crypto_digest_apply_clock_workaround(struct work_struct *work)
+{
+ uint8_t *buf, *digest;
+ u32 cmd;
+ u32 crypto_session;
+ TEEC_Result ret;
+ TEEC_Operation op;
+ TEEC_Context teec_context;
+ TEEC_Session teec_session;
+ TEEC_UUID uuid = SERVICE_SYSTEM_UUID;
+ bool result = true;
+
+ tf_crypto_clockdomain_wakeup();
+
+ ret = TEEC_InitializeContext(NULL, &teec_context);
+ if (ret != TEEC_SUCCESS) {
+ tf_crypto_clockdomain_idle();
+ return;
+ }
+
+ buf = kmalloc(TOTAL_BUFFER_SIZE, GFP_KERNEL);
+
+ if (buf == NULL) {
+ TEEC_FinalizeContext(&teec_context);
+ tf_crypto_clockdomain_idle();
+ return;
+ }
+
+ digest = buf + BUFFER_SIZE;
+
+ /* Call C_OpenSession */
+ memset(&op, 0, sizeof(TEEC_Operation));
+
+ op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE,
+ TEEC_NONE);
+
+ ret = TEEC_OpenSession(&teec_context, &teec_session,
+ &uuid, TEEC_LOGIN_PUBLIC, NULL, &op, NULL);
+ if (ret != TEEC_SUCCESS) {
+ TEEC_FinalizeContext(&teec_context);
+ tf_crypto_clockdomain_idle();
+ kfree(buf);
+ return;
+ }
+
+ memset(&op, 0, sizeof(TEEC_Operation));
+
+ op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT, TEEC_NONE, TEEC_NONE,
+ TEEC_NONE);
+ op.params[0].value.a = 0;
+ op.params[0].value.b = CKF_SERIAL_SESSION;
+
+ cmd = SERVICE_SYSTEM_PKCS11_C_OPEN_SESSION_COMMAND_ID & 0x00007FFF;
+
+ ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
+ if (ret != TEEC_SUCCESS) {
+ printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
+ __func__, ret);
+ result = false;
+ goto exit;
+ }
+
+ crypto_session = op.params[0].value.a;
+
+ /* Call C_DigestInit */
+ memset(&op, 0, sizeof(TEEC_Operation));
+
+ op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
+ TEEC_MEMREF_TEMP_INPUT, TEEC_NONE, TEEC_NONE);
+ op.params[0].value.a = CKM_MD5;
+ op.params[1].tmpref.buffer = NULL;
+ op.params[1].tmpref.size = 0;
+
+ cmd = (crypto_session << 16) |
+ (SERVICE_SYSTEM_PKCS11_C_DIGESTINIT_COMMAND_ID & 0x7fff);
+
+ ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
+ if (ret != TEEC_SUCCESS) {
+ printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
+ __func__, ret);
+ result = false;
+ }
+
+ /* Call C_Digest */
+ memset(&op, 0, sizeof(TEEC_Operation));
+
+ op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
+ TEEC_MEMREF_TEMP_OUTPUT, TEEC_NONE, TEEC_NONE);
+ op.params[0].tmpref.buffer = (uint8_t *) buf;
+ op.params[0].tmpref.size = (uint32_t) BUFFER_SIZE;
+ op.params[1].tmpref.buffer = (uint8_t *) digest;
+ op.params[1].tmpref.size = (uint32_t) DIGEST_SIZE;
+
+ cmd = (crypto_session << 16) |
+ (SERVICE_SYSTEM_PKCS11_C_DIGEST_COMMAND_ID & 0x7fff);
+
+ ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
+ if (ret != TEEC_SUCCESS) {
+ printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
+ __func__, ret);
+ result = false;
+ }
+
+ /* Call C_CloseSession */
+ memset(&op, 0, sizeof(TEEC_Operation));
+
+ op.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE, TEEC_NONE,
+ TEEC_NONE);
+
+ cmd = (crypto_session << 16) |
+ (SERVICE_SYSTEM_PKCS11_C_CLOSE_SESSION_COMMAND_ID & 0x7fff);
+
+ ret = TEEC_InvokeCommand(&teec_session, cmd, &op, NULL);
+ if (ret != TEEC_SUCCESS) {
+ printk(KERN_ERR "%s: TEEC_InvokeCommand returned 0x%08x\n",
+ __func__, ret);
+ result = false;
+ }
+
+ if (result) {
+ u32 clock_val = tf_crypto_read_clock_value(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ result = (clock_val & 0x30000) != 0x10000;
+ }
+
+exit:
+ TEEC_CloseSession(&teec_session);
+ TEEC_FinalizeContext(&teec_context);
+
+ tf_crypto_clockdomain_idle();
+ kfree(buf);
+
+ printk(KERN_INFO "%s: SHA2MD5 clock work-around result: %s\n",
+ __func__, result ? "succeeded" : "failed");
+
+ return;
+}
+
+static DECLARE_WORK(digest_clock_workaround_work, _tf_crypto_digest_apply_clock_workaround);
+
+void tf_crypto_digest_apply_clock_workaround(void)
+{
+ u32 clock_val;
+
+ clock_val = tf_crypto_read_clock_value(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ if ((clock_val & 0x30000) == 0x10000) {
+ printk(KERN_INFO "%s: SHA2MD5 clock is stuck, trying to work-around\n", __func__);
+ /* The calling path of our workaround function might involve interrupt handlers
+ * and other interesting parts of the kernel code. That doesn't play nice
+ * with the functionality used by that function (e.g. memory allocation) -
+ * therefore, just schedule it for execution from the more appropriate
+ * context instead of executing it directly. */
+ schedule_work(&digest_clock_workaround_work);
+ }
+}