diff options
-rw-r--r-- | arch/arm/configs/cyanogenmod_tuna_defconfig | 4 | ||||
-rw-r--r-- | arch/arm/configs/tuna_defconfig | 4 | ||||
-rwxr-xr-x | security/smc/Makefile | 1 | ||||
-rw-r--r-- | security/smc/tf_comm_mshield.c | 15 | ||||
-rw-r--r-- | security/smc/tf_crypto.c | 22 | ||||
-rw-r--r-- | security/smc/tf_crypto.h | 5 | ||||
-rw-r--r-- | security/smc/tf_crypto_digest_clock_workaround.c | 235 |
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); + } +} |