From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arve=20Hj=C3=B8nnev=C3=A5g?= Date: Mon, 18 Nov 2013 20:46:48 -0800 Subject: [PATCH] trusty: Add trusty driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit includes: Add arm64 support includes: Add trusty_fast_call64 api on 64 bit systems. includes: move probe to subsys_initcall Child devices of trusty like FIQ-based debuggers and watchdogs may want to probe early, move trusty from module init to subsys init to allow it and its children to probe earlier. includes: Retry std_calls on SM_ERR_BUSY If the trusty spinlock is held, or if the strex fails for another reason, trusty returns SM_ERR_BUSY. Add retry code to handle this. Without this retry code, std_calls can fail. If the previous smc call had returned SM_ERR_INTERRUPTED, this failure would cause the driver to get out of sync with trusty. All later calls would then fail with SM_ERR_INTERLEAVED_SMC. Change-Id: Idc0bbe78b557bc5d95dbec448e4085e3ab9111b4 Signed-off-by: Arve Hjønnevåg --- .../devicetree/bindings/trusty/trusty-smc.txt | 6 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/trusty/Kconfig | 11 + drivers/trusty/Makefile | 5 + drivers/trusty/trusty.c | 258 ++++++++++++++++++ include/linux/trusty/sm_err.h | 39 +++ include/linux/trusty/smcall.h | 75 +++++ include/linux/trusty/trusty.h | 46 ++++ 9 files changed, 443 insertions(+) create mode 100644 Documentation/devicetree/bindings/trusty/trusty-smc.txt create mode 100644 drivers/trusty/Kconfig create mode 100644 drivers/trusty/Makefile create mode 100644 drivers/trusty/trusty.c create mode 100644 include/linux/trusty/sm_err.h create mode 100644 include/linux/trusty/smcall.h create mode 100644 include/linux/trusty/trusty.h diff --git a/Documentation/devicetree/bindings/trusty/trusty-smc.txt b/Documentation/devicetree/bindings/trusty/trusty-smc.txt new file mode 100644 index 000000000000..1b39ad317c67 --- /dev/null +++ b/Documentation/devicetree/bindings/trusty/trusty-smc.txt @@ -0,0 +1,6 @@ +Trusty smc interface + +Trusty is running in secure mode on the same (arm) cpu(s) as the current os. + +Required properties: +- compatible: "android,trusty-smc-v1" diff --git a/drivers/Kconfig b/drivers/Kconfig index c94b20af31ff..a3070e9670ea 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -79,6 +79,8 @@ source "drivers/hwmon/Kconfig" source "drivers/thermal/Kconfig" +source "drivers/trusty/Kconfig" + source "drivers/watchdog/Kconfig" source "drivers/ssb/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 78ef69c8ac9d..6c5071ab2660 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -120,6 +120,7 @@ obj-$(CONFIG_W1) += w1/ obj-y += power/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_THERMAL) += thermal/ +obj-$(CONFIG_TRUSTY) += trusty/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff --git a/drivers/trusty/Kconfig b/drivers/trusty/Kconfig new file mode 100644 index 000000000000..f577ae8acad3 --- /dev/null +++ b/drivers/trusty/Kconfig @@ -0,0 +1,11 @@ +# +# Trusty +# + +menu "Trusty" + +config TRUSTY + tristate "Trusty" + default n + +endmenu diff --git a/drivers/trusty/Makefile b/drivers/trusty/Makefile new file mode 100644 index 000000000000..1d77805d7dd6 --- /dev/null +++ b/drivers/trusty/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for trusty components +# + +obj-$(CONFIG_TRUSTY) += trusty.o diff --git a/drivers/trusty/trusty.c b/drivers/trusty/trusty.c new file mode 100644 index 000000000000..59ecf60fc050 --- /dev/null +++ b/drivers/trusty/trusty.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct trusty_state { + struct mutex smc_lock; +}; + +#ifdef CONFIG_ARM64 +#define SMC_ARG0 "x0" +#define SMC_ARG1 "x1" +#define SMC_ARG2 "x2" +#define SMC_ARG3 "x3" +#define SMC_ARCH_EXTENSION "" +#define SMC_REGISTERS_TRASHED "x4","x5","x6","x7","x8","x9","x10","x11", \ + "x12","x13","x14","x15","x16","x17" +#else +#define SMC_ARG0 "r0" +#define SMC_ARG1 "r1" +#define SMC_ARG2 "r2" +#define SMC_ARG3 "r3" +#define SMC_ARCH_EXTENSION ".arch_extension sec\n" +#define SMC_REGISTERS_TRASHED "ip" +#endif + +static inline ulong smc(ulong r0, ulong r1, ulong r2, ulong r3) +{ + register ulong _r0 asm(SMC_ARG0) = r0; + register ulong _r1 asm(SMC_ARG1) = r1; + register ulong _r2 asm(SMC_ARG2) = r2; + register ulong _r3 asm(SMC_ARG3) = r3; + + asm volatile( + __asmeq("%0", SMC_ARG0) + __asmeq("%1", SMC_ARG1) + __asmeq("%2", SMC_ARG2) + __asmeq("%3", SMC_ARG3) + __asmeq("%4", SMC_ARG0) + __asmeq("%5", SMC_ARG1) + __asmeq("%6", SMC_ARG2) + __asmeq("%7", SMC_ARG3) + SMC_ARCH_EXTENSION + "smc #0" /* switch to secure world */ + : "=r" (_r0), "=r" (_r1), "=r" (_r2), "=r" (_r3) + : "r" (_r0), "r" (_r1), "r" (_r2), "r" (_r3) + : SMC_REGISTERS_TRASHED); + return _r0; +} + +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +EXPORT_SYMBOL(trusty_fast_call32); + +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(!s); + BUG_ON(!SMC_IS_FASTCALL(smcnr)); + BUG_ON(!SMC_IS_SMC64(smcnr)); + + return smc(smcnr, a0, a1, a2); +} +#endif + +static ulong trusty_std_call_inner(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int retry = 5; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx)\n", + __func__, smcnr, a0, a1, a2); + while (true) { + ret = smc(smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY || !retry) + break; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, retry\n", + __func__, smcnr, a0, a1, a2); + retry--; + } + + return ret; +} + +static ulong trusty_std_call_helper(struct device *dev, ulong smcnr, + ulong a0, ulong a1, ulong a2) +{ + ulong ret; + int sleep_time = 1; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + while (true) { + ret = trusty_std_call_inner(dev, smcnr, a0, a1, a2); + if ((int)ret != SM_ERR_BUSY) + break; + + if (sleep_time == 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy\n", + __func__, smcnr, a0, a1, a2); + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) returned busy, wait %d ms\n", + __func__, smcnr, a0, a1, a2, sleep_time); + + msleep(sleep_time); + if (sleep_time < 1000) + sleep_time <<= 1; + + dev_dbg(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) retry\n", + __func__, smcnr, a0, a1, a2); + } + + if (sleep_time > 256) + dev_warn(dev, "%s(0x%lx 0x%lx 0x%lx 0x%lx) busy cleared\n", + __func__, smcnr, a0, a1, a2); + + return ret; +} + +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2) +{ + int ret; + struct trusty_state *s = platform_get_drvdata(to_platform_device(dev)); + + BUG_ON(SMC_IS_FASTCALL(smcnr)); + BUG_ON(SMC_IS_SMC64(smcnr)); + + mutex_lock(&s->smc_lock); + + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) started\n", + __func__, smcnr, a0, a1, a2); + + ret = trusty_std_call_helper(dev, smcnr, a0, a1, a2); + while (ret == SM_ERR_INTERRUPTED) { + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) interrupted\n", + __func__, smcnr, a0, a1, a2); + ret = trusty_std_call_helper(dev, SMC_SC_RESTART_LAST, 0, 0, 0); + } + dev_dbg(dev, "%s(0x%x 0x%x 0x%x 0x%x) returned 0x%x\n", + __func__, smcnr, a0, a1, a2, ret); + + WARN_ONCE(ret == SM_ERR_PANIC, "trusty crashed"); + + mutex_unlock(&s->smc_lock); + + return ret; +} +EXPORT_SYMBOL(trusty_std_call32); + +static int trusty_remove_child(struct device *dev, void *data) +{ + platform_device_unregister(to_platform_device(dev)); + return 0; +} + +static int trusty_probe(struct platform_device *pdev) +{ + int ret; + struct trusty_state *s; + struct device_node *node = pdev->dev.of_node; + + if (!node) { + dev_err(&pdev->dev, "of_node required\n"); + return -EINVAL; + } + + s = kzalloc(sizeof(*s), GFP_KERNEL); + if (!s) { + ret = -ENOMEM; + goto err_allocate_state; + } + mutex_init(&s->smc_lock); + platform_set_drvdata(pdev, s); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to add children: %d\n", ret); + goto err_add_children; + } + + return 0; + +err_add_children: + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); +err_allocate_state: + return ret; +} + +static int trusty_remove(struct platform_device *pdev) +{ + struct trusty_state *s = platform_get_drvdata(pdev); + + device_for_each_child(&pdev->dev, NULL, trusty_remove_child); + mutex_destroy(&s->smc_lock); + kfree(s); + return 0; +} + +static const struct of_device_id trusty_of_match[] = { + { .compatible = "android,trusty-smc-v1", }, + {}, +}; + +static struct platform_driver trusty_driver = { + .probe = trusty_probe, + .remove = trusty_remove, + .driver = { + .name = "trusty", + .owner = THIS_MODULE, + .of_match_table = trusty_of_match, + }, +}; + +static int __init trusty_driver_init(void) +{ + return platform_driver_register(&trusty_driver); +} + +static void __exit trusty_driver_exit(void) +{ + platform_driver_unregister(&trusty_driver); +} + +subsys_initcall(trusty_driver_init); +module_exit(trusty_driver_exit); diff --git a/include/linux/trusty/sm_err.h b/include/linux/trusty/sm_err.h new file mode 100644 index 000000000000..4ee67589ce63 --- /dev/null +++ b/include/linux/trusty/sm_err.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2013 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SM_ERR_H +#define __LINUX_TRUSTY_SM_ERR_H + +/* Errors from the secure monitor */ +#define SM_ERR_UNDEFINED_SMC 0xFFFFFFFF /* Unknown SMC (defined by ARM DEN 0028A(0.9.0) */ +#define SM_ERR_INVALID_PARAMETERS -2 +#define SM_ERR_INTERRUPTED -3 /* Got interrupted. Call back with restart SMC */ +#define SM_ERR_UNEXPECTED_RESTART -4 /* Got an restart SMC when we didn't expect it */ +#define SM_ERR_BUSY -5 /* Temporarily busy. Call back with original args */ +#define SM_ERR_INTERLEAVED_SMC -6 /* Got a trusted_service SMC when a restart SMC is required */ +#define SM_ERR_INTERNAL_FAILURE -7 /* Unknown error */ +#define SM_ERR_NOT_SUPPORTED -8 +#define SM_ERR_NOT_ALLOWED -9 /* SMC call not allowed */ +#define SM_ERR_END_OF_INPUT -10 +#define SM_ERR_PANIC -11 /* Secure OS crashed */ + +#endif diff --git a/include/linux/trusty/smcall.h b/include/linux/trusty/smcall.h new file mode 100644 index 000000000000..278a4b256fbc --- /dev/null +++ b/include/linux/trusty/smcall.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2013-2014 Google Inc. All rights reserved + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef __LINUX_TRUSTY_SMCALL_H +#define __LINUX_TRUSTY_SMCALL_H + +#define SMC_NUM_ENTITIES 64 +#define SMC_NUM_ARGS 4 +#define SMC_NUM_PARAMS (SMC_NUM_ARGS - 1) + +#define SMC_IS_FASTCALL(smc_nr) ((smc_nr) & 0x80000000) +#define SMC_IS_SMC64(smc_nr) ((smc_nr) & 0x40000000) +#define SMC_ENTITY(smc_nr) (((smc_nr) & 0x3F000000) >> 24) +#define SMC_FUNCTION(smc_nr) ((smc_nr) & 0x0000FFFF) + +#define SMC_NR(entity, fn, fastcall, smc64) ((((fastcall) & 0x1) << 31) | \ + (((smc64) & 0x1) << 30) | \ + (((entity) & 0x3F) << 24) | \ + ((fn) & 0xFFFF) \ + ) + +#define SMC_FASTCALL_NR(entity, fn) SMC_NR((entity), (fn), 1, 0) +#define SMC_STDCALL_NR(entity, fn) SMC_NR((entity), (fn), 0, 0) +#define SMC_FASTCALL64_NR(entity, fn) SMC_NR((entity), (fn), 1, 1) +#define SMC_STDCALL64_NR(entity, fn) SMC_NR((entity), (fn), 0, 1) + +#define SMC_ENTITY_ARCH 0 /* ARM Architecture calls */ +#define SMC_ENTITY_CPU 1 /* CPU Service calls */ +#define SMC_ENTITY_SIP 2 /* SIP Service calls */ +#define SMC_ENTITY_OEM 3 /* OEM Service calls */ +#define SMC_ENTITY_STD 4 /* Standard Service calls */ +#define SMC_ENTITY_RESERVED 5 /* Reserved for future use */ +#define SMC_ENTITY_TRUSTED_APP 48 /* Trusted Application calls */ +#define SMC_ENTITY_TRUSTED_OS 50 /* Trusted OS calls */ +#define SMC_ENTITY_SECURE_MONITOR 60 /* Trusted OS calls internal to secure monitor */ + +/* FC = Fast call, SC = Standard call */ +#define SMC_SC_RESTART_LAST SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_SC_NOP SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) + +/* + * Return from secure os to non-secure os with return value in r1 + */ +#define SMC_SC_NS_RETURN SMC_STDCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) + +#define SMC_FC_RESERVED SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 0) +#define SMC_FC_FIQ_EXIT SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 1) +#define SMC_FC_REQUEST_FIQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 2) +#define SMC_FC_GET_NEXT_IRQ SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 3) + +#define SMC_FC_CPU_SUSPEND SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 7) +#define SMC_FC_CPU_RESUME SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 8) + +#define SMC_FC_AARCH_SWITCH SMC_FASTCALL_NR (SMC_ENTITY_SECURE_MONITOR, 9) + +#endif /* __LINUX_TRUSTY_SMCALL_H */ diff --git a/include/linux/trusty/trusty.h b/include/linux/trusty/trusty.h new file mode 100644 index 000000000000..30d4300ba301 --- /dev/null +++ b/include/linux/trusty/trusty.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#ifndef __LINUX_TRUSTY_TRUSTY_H +#define __LINUX_TRUSTY_TRUSTY_H + +#include +#include + +#ifdef CONFIG_TRUSTY +s32 trusty_std_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +s32 trusty_fast_call32(struct device *dev, u32 smcnr, u32 a0, u32 a1, u32 a2); +#ifdef CONFIG_64BIT +s64 trusty_fast_call64(struct device *dev, u64 smcnr, u64 a0, u64 a1, u64 a2); +#endif +#else +static inline s32 trusty_std_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +static inline s32 trusty_fast_call32(struct device *dev, u32 smcnr, + u32 a0, u32 a1, u32 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#ifdef CONFIG_64BIT +static inline s64 trusty_fast_call64(struct device *dev, + u64 smcnr, u64 a0, u64 a1, u64 a2) +{ + return SM_ERR_UNDEFINED_SMC; +} +#endif +#endif + +#endif -- https://clearlinux.org