From 76a21174d2a2f1750b13b2680461b6b80a654848 Mon Sep 17 00:00:00 2001 From: Mikael Olsson Date: Fri, 12 Feb 2021 17:30:22 +0100 Subject: [PATCH] Add SiP service to configure Arm Ethos-N NPU By default the Arm Ethos-N NPU will boot up in secure mode. In this mode the non-secure world cannot access the registers needed to use the NPU. To still allow the non-secure world to use the NPU, a SiP service has been added that can delegate non-secure access to the registers needed to use it. Only the HW_CONFIG for the Arm Juno platform has been updated to include the device tree for the NPU and the platform currently only loads the HW_CONFIG in AArch64 builds. Signed-off-by: Mikael Olsson Change-Id: I65dfd864042ed43faae0a259dcf319cbadb5f3d2 --- docs/plat/arm/arm-build-options.rst | 8 +- drivers/arm/ethosn/ethosn_smc.c | 164 ++++++++++++++++++ fdts/juno-ethosn.dtsi | 25 +++ fdts/juno.dts | 4 + include/drivers/arm/ethosn.h | 61 +++++++ include/plat/arm/common/arm_sip_svc.h | 8 +- include/plat/arm/common/fconf_ethosn_getter.h | 35 ++++ plat/arm/common/arm_common.mk | 25 ++- plat/arm/common/arm_sip_svc.c | 21 ++- plat/arm/common/fconf/fconf_ethosn_getter.c | 108 ++++++++++++ 10 files changed, 452 insertions(+), 7 deletions(-) create mode 100644 drivers/arm/ethosn/ethosn_smc.c create mode 100644 fdts/juno-ethosn.dtsi create mode 100644 include/drivers/arm/ethosn.h create mode 100644 include/plat/arm/common/fconf_ethosn_getter.h create mode 100644 plat/arm/common/fconf/fconf_ethosn_getter.c diff --git a/docs/plat/arm/arm-build-options.rst b/docs/plat/arm/arm-build-options.rst index a1d231357..2aa973890 100644 --- a/docs/plat/arm/arm-build-options.rst +++ b/docs/plat/arm/arm-build-options.rst @@ -91,6 +91,12 @@ Arm Platform Build Options platforms. If this option is specified, then the path to the CryptoCell SBROM library must be specified via ``CCSBROM_LIB_PATH`` flag. +- ``ARM_ETHOSN_NPU_DRIVER``: boolean option to enable a SiP service that can + configure an Arm Ethos-N NPU. To use this service the target platform's + ``HW_CONFIG`` must include the device tree nodes for the NPU. Currently, only + the Arm Juno platform has this included in its ``HW_CONFIG`` and the platform + only loads the ``HW_CONFIG`` in AArch64 builds. Default is 0. + - ``ARM_SPMC_MANIFEST_DTS`` : path to an alternate manifest file used as the SPMC Core manifest. Valid when ``SPD=spmd`` is selected. @@ -128,4 +134,4 @@ Arm CSS Platform-Specific Build Options -------------- -*Copyright (c) 2019-2020, Arm Limited. All rights reserved.* +*Copyright (c) 2019-2021, Arm Limited. All rights reserved.* diff --git a/drivers/arm/ethosn/ethosn_smc.c b/drivers/arm/ethosn/ethosn_smc.c new file mode 100644 index 000000000..299d07c02 --- /dev/null +++ b/drivers/arm/ethosn/ethosn_smc.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* Arm Ethos-N NPU (NPU) status */ +#define ETHOSN_STATUS \ + FCONF_GET_PROPERTY(hw_config, ethosn_config, status) + +/* Number of NPU cores available */ +#define ETHOSN_NUM_CORES \ + FCONF_GET_PROPERTY(hw_config, ethosn_config, num_cores) + +/* Address to an NPU core */ +#define ETHOSN_CORE_ADDR(core_idx) \ + FCONF_GET_PROPERTY(hw_config, ethosn_core_addr, core_idx) + +/* NPU core sec registry address */ +#define ETHOSN_CORE_SEC_REG(core_addr, reg_offset) \ + (core_addr + reg_offset) + +/* Reset timeout in us */ +#define ETHOSN_RESET_TIMEOUT_US U(10 * 1000 * 1000) +#define ETHOSN_RESET_WAIT_US U(1) + +#define SEC_DEL_REG U(0x0004) +#define SEC_DEL_VAL U(0x81C) +#define SEC_DEL_EXCC_MASK U(0x20) + +#define SEC_SECCTLR_REG U(0x0010) +#define SEC_SECCTLR_VAL U(0x3) + +#define SEC_DEL_MMUSID_REG U(0x2008) +#define SEC_DEL_MMUSID_VAL U(0x3FFFF) + +#define SEC_DEL_ADDR_EXT_REG U(0x201C) +#define SEC_DEL_ADDR_EXT_VAL U(0x15) + +#define SEC_SYSCTRL0_REG U(0x0018) +#define SEC_SYSCTRL0_SOFT_RESET U(3U << 29) +#define SEC_SYSCTRL0_HARD_RESET U(1U << 31) + +static void ethosn_delegate_to_ns(uintptr_t core_addr) +{ + mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_SECCTLR_REG), + SEC_SECCTLR_VAL); + + mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_REG), + SEC_DEL_VAL); + + mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_MMUSID_REG), + SEC_DEL_MMUSID_VAL); + + mmio_setbits_32(ETHOSN_CORE_SEC_REG(core_addr, SEC_DEL_ADDR_EXT_REG), + SEC_DEL_ADDR_EXT_VAL); +} + +static int ethosn_is_sec(void) +{ + if ((mmio_read_32(ETHOSN_CORE_SEC_REG(ETHOSN_CORE_ADDR(0), SEC_DEL_REG)) + & SEC_DEL_EXCC_MASK) != 0U) { + return 0; + } + + return 1; +} + +static bool ethosn_reset(uintptr_t core_addr, int hard_reset) +{ + unsigned int timeout; + const uintptr_t sysctrl0_reg = + ETHOSN_CORE_SEC_REG(core_addr, SEC_SYSCTRL0_REG); + const uint32_t reset_val = (hard_reset != 0) ? SEC_SYSCTRL0_HARD_RESET + : SEC_SYSCTRL0_SOFT_RESET; + + mmio_write_32(sysctrl0_reg, reset_val); + + /* Wait for reset to complete */ + for (timeout = 0U; timeout < ETHOSN_RESET_TIMEOUT_US; + timeout += ETHOSN_RESET_WAIT_US) { + + if ((mmio_read_32(sysctrl0_reg) & reset_val) == 0U) { + break; + } + + udelay(ETHOSN_RESET_WAIT_US); + } + + return timeout < ETHOSN_RESET_TIMEOUT_US; +} + +uintptr_t ethosn_smc_handler(uint32_t smc_fid, + u_register_t core_idx, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags) +{ + uintptr_t core_addr; + int hard_reset = 0; + + /* Only SiP fast calls are expected */ + if ((GET_SMC_TYPE(smc_fid) != SMC_TYPE_FAST) || + (GET_SMC_OEN(smc_fid) != OEN_SIP_START)) { + SMC_RET1(handle, SMC_UNK); + } + + /* Truncate parameters to 32-bits for SMC32 */ + if (GET_SMC_CC(smc_fid) == SMC_32) { + core_idx &= 0xFFFFFFFF; + x2 &= 0xFFFFFFFF; + x3 &= 0xFFFFFFFF; + x4 &= 0xFFFFFFFF; + } + + if (!is_ethosn_fid(smc_fid)) { + SMC_RET1(handle, SMC_UNK); + } + + if (ETHOSN_STATUS == ETHOSN_STATUS_DISABLED) { + WARN("ETHOSN: Arm Ethos-N NPU not available\n"); + SMC_RET1(handle, ETHOSN_NOT_SUPPORTED); + } + + switch (smc_fid & FUNCID_NUM_MASK) { + case ETHOSN_FNUM_VERSION: + SMC_RET2(handle, ETHOSN_VERSION_MAJOR, ETHOSN_VERSION_MINOR); + case ETHOSN_FNUM_IS_SEC: + SMC_RET1(handle, ethosn_is_sec()); + case ETHOSN_FNUM_HARD_RESET: + hard_reset = 1; + /* Fallthrough */ + case ETHOSN_FNUM_SOFT_RESET: + if (core_idx >= ETHOSN_NUM_CORES) { + WARN("ETHOSN: core index out of range\n"); + SMC_RET1(handle, ETHOSN_CORE_IDX_OUT_OF_RANGE); + } + + core_addr = ETHOSN_CORE_ADDR(core_idx); + + if (!ethosn_reset(core_addr, hard_reset)) { + SMC_RET1(handle, ETHOSN_FAILURE); + } + + ethosn_delegate_to_ns(core_addr); + + SMC_RET1(handle, ETHOSN_SUCCESS); + default: + SMC_RET1(handle, SMC_UNK); + } +} diff --git a/fdts/juno-ethosn.dtsi b/fdts/juno-ethosn.dtsi new file mode 100644 index 000000000..87ab378a2 --- /dev/null +++ b/fdts/juno-ethosn.dtsi @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/ { + #address-cells = <2>; + #size-cells = <2>; + + ethosn: ethosn@6f300000 { + compatible = "ethosn"; + reg = <0 0x6f300000 0 0x00100000>; + status = "okay"; + + /* + * Single-core NPU. For multi-core NPU, additional core nodes + * and reg values must be added. + */ + core0 { + compatible = "ethosn-core"; + status = "okay"; + }; + }; +}; diff --git a/fdts/juno.dts b/fdts/juno.dts index b628d1809..56fe1676f 100644 --- a/fdts/juno.dts +++ b/fdts/juno.dts @@ -9,3 +9,7 @@ / { }; + +#if ARM_ETHOSN_NPU_DRIVER + #include "juno-ethosn.dtsi" +#endif diff --git a/include/drivers/arm/ethosn.h b/include/drivers/arm/ethosn.h new file mode 100644 index 000000000..6de2abb85 --- /dev/null +++ b/include/drivers/arm/ethosn.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ETHOSN_H +#define ETHOSN_H + +#include + +/* Function numbers */ +#define ETHOSN_FNUM_VERSION U(0x50) +#define ETHOSN_FNUM_IS_SEC U(0x51) +#define ETHOSN_FNUM_HARD_RESET U(0x52) +#define ETHOSN_FNUM_SOFT_RESET U(0x53) +/* 0x54-0x5F reserved for future use */ + +/* SMC64 function IDs */ +#define ETHOSN_FID_64(func_num) U(0xC2000000 | func_num) +#define ETHOSN_FID_VERSION_64 ETHOSN_FID_64(ETHOSN_FNUM_VERSION) +#define ETHOSN_FID_IS_SEC_64 ETHOSN_FID_64(ETHOSN_FNUM_IS_SEC) +#define ETHOSN_FID_HARD_RESET_64 ETHOSN_FID_64(ETHOSN_FNUM_HARD_RESET) +#define ETHOSN_FID_SOFT_RESET_64 ETHOSN_FID_64(ETHOSN_FNUM_SOFT_RESET) + +/* SMC32 function IDs */ +#define ETHOSN_FID_32(func_num) U(0x82000000 | func_num) +#define ETHOSN_FID_VERSION_32 ETHOSN_FID_32(ETHOSN_FNUM_VERSION) +#define ETHOSN_FID_IS_SEC_32 ETHOSN_FID_32(ETHOSN_FNUM_IS_SEC) +#define ETHOSN_FID_HARD_RESET_32 ETHOSN_FID_32(ETHOSN_FNUM_HARD_RESET) +#define ETHOSN_FID_SOFT_RESET_32 ETHOSN_FID_32(ETHOSN_FNUM_SOFT_RESET) + +#define ETHOSN_NUM_SMC_CALLS 8 + +/* Macro to identify function calls */ +#define ETHOSN_FID_MASK U(0xFFF0) +#define ETHOSN_FID_VALUE U(0x50) +#define is_ethosn_fid(_fid) (((_fid) & ETHOSN_FID_MASK) == ETHOSN_FID_VALUE) + +/* Service version */ +#define ETHOSN_VERSION_MAJOR U(0) +#define ETHOSN_VERSION_MINOR U(1) + +/* Return codes for function calls */ +#define ETHOSN_SUCCESS 0 +#define ETHOSN_NOT_SUPPORTED -1 +/* -2 Reserved for NOT_REQUIRED */ +/* -3 Reserved for INVALID_PARAMETER */ +#define ETHOSN_FAILURE -4 +#define ETHOSN_CORE_IDX_OUT_OF_RANGE -5 + +uintptr_t ethosn_smc_handler(uint32_t smc_fid, + u_register_t core_idx, + u_register_t x2, + u_register_t x3, + u_register_t x4, + void *cookie, + void *handle, + u_register_t flags); + +#endif /* ETHOSN_H */ diff --git a/include/plat/arm/common/arm_sip_svc.h b/include/plat/arm/common/arm_sip_svc.h index 85fdb289f..2eeed95f0 100644 --- a/include/plat/arm/common/arm_sip_svc.h +++ b/include/plat/arm/common/arm_sip_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2019,2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,6 +25,12 @@ /* DEBUGFS_SMC_32 0x82000030U */ /* DEBUGFS_SMC_64 0xC2000030U */ +/* + * Arm Ethos-N NPU SiP SMC function IDs + * 0xC2000050-0xC200005F + * 0x82000050-0x8200005F + */ + /* ARM SiP Service Calls version numbers */ #define ARM_SIP_SVC_VERSION_MAJOR U(0x0) #define ARM_SIP_SVC_VERSION_MINOR U(0x2) diff --git a/include/plat/arm/common/fconf_ethosn_getter.h b/include/plat/arm/common/fconf_ethosn_getter.h new file mode 100644 index 000000000..0fd1f025a --- /dev/null +++ b/include/plat/arm/common/fconf_ethosn_getter.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FCONF_ETHOSN_GETTER_H +#define FCONF_ETHOSN_GETTER_H + +#include + +#include + +#define hw_config__ethosn_config_getter(prop) ethosn_config.prop +#define hw_config__ethosn_core_addr_getter(idx) __extension__ ({ \ + assert(idx < ethosn_config.num_cores); \ + ethosn_config.core_addr[idx]; \ +}) + +#define ETHOSN_STATUS_DISABLED U(0) +#define ETHOSN_STATUS_ENABLED U(1) + +#define ETHOSN_CORE_NUM_MAX U(64) + +struct ethosn_config_t { + uint8_t status; + uint32_t num_cores; + uint64_t core_addr[ETHOSN_CORE_NUM_MAX]; +}; + +int fconf_populate_arm_ethosn(uintptr_t config); + +extern struct ethosn_config_t ethosn_config; + +#endif /* FCONF_ETHOSN_GETTER_H */ diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index a225b4052..232d56242 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -100,6 +100,11 @@ ifeq (${ARM_LINUX_KERNEL_AS_BL33},1) $(eval $(call add_define,ARM_PRELOADED_DTB_BASE)) endif +# Arm Ethos-N NPU SiP service +ARM_ETHOSN_NPU_DRIVER := 0 +$(eval $(call assert_boolean,ARM_ETHOSN_NPU_DRIVER)) +$(eval $(call add_define,ARM_ETHOSN_NPU_DRIVER)) + # Use an implementation of SHA-256 with a smaller memory footprint but reduced # speed. $(eval $(call add_define,MBEDTLS_SHA256_SMALLER)) @@ -248,14 +253,26 @@ BL31_SOURCES += plat/arm/common/arm_bl31_setup.c \ plat/arm/common/arm_topology.c \ plat/common/plat_psci_common.c -ifeq (${ENABLE_PMF}, 1) +ifneq ($(filter 1,${ENABLE_PMF} ${ARM_ETHOSN_NPU_DRIVER}),) +ARM_SVC_HANDLER_SRCS := + +ifeq (${ENABLE_PMF},1) +ARM_SVC_HANDLER_SRCS += lib/pmf/pmf_smc.c +endif + +ifeq (${ARM_ETHOSN_NPU_DRIVER},1) +ARM_SVC_HANDLER_SRCS += plat/arm/common/fconf/fconf_ethosn_getter.c \ + drivers/delay_timer/delay_timer.c \ + drivers/arm/ethosn/ethosn_smc.c +endif + ifeq (${ARCH}, aarch64) BL31_SOURCES += plat/arm/common/aarch64/execution_state_switch.c\ plat/arm/common/arm_sip_svc.c \ - lib/pmf/pmf_smc.c + ${ARM_SVC_HANDLER_SRCS} else BL32_SOURCES += plat/arm/common/arm_sip_svc.c \ - lib/pmf/pmf_smc.c + ${ARM_SVC_HANDLER_SRCS} endif endif diff --git a/plat/arm/common/arm_sip_svc.c b/plat/arm/common/arm_sip_svc.c index 9f5d4557c..6456c7841 100644 --- a/plat/arm/common/arm_sip_svc.c +++ b/plat/arm/common/arm_sip_svc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2019,2021, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,8 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, { int call_count = 0; +#if ENABLE_PMF + /* * Dispatch PMF calls to PMF SMC handler and return its return * value @@ -59,6 +62,8 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, handle, flags); } +#endif /* ENABLE_PMF */ + #if USE_DEBUGFS if (is_debugfs_fid(smc_fid)) { @@ -68,6 +73,15 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, #endif /* USE_DEBUGFS */ +#if ARM_ETHOSN_NPU_DRIVER + + if (is_ethosn_fid(smc_fid)) { + return ethosn_smc_handler(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); + } + +#endif /* ARM_ETHOSN_NPU_DRIVER */ + switch (smc_fid) { case ARM_SIP_SVC_EXE_STATE_SWITCH: { /* Execution state can be switched only if EL3 is AArch64 */ @@ -92,6 +106,11 @@ static uintptr_t arm_sip_handler(unsigned int smc_fid, /* PMF calls */ call_count += PMF_NUM_SMC_CALLS; +#if ARM_ETHOSN_NPU_DRIVER + /* ETHOSN calls */ + call_count += ETHOSN_NUM_SMC_CALLS; +#endif /* ARM_ETHOSN_NPU_DRIVER */ + /* State switch call */ call_count += 1; diff --git a/plat/arm/common/fconf/fconf_ethosn_getter.c b/plat/arm/common/fconf/fconf_ethosn_getter.c new file mode 100644 index 000000000..1ba9f3a23 --- /dev/null +++ b/plat/arm/common/fconf/fconf_ethosn_getter.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#include +#include +#include +#include + +struct ethosn_config_t ethosn_config; + +static uint8_t fdt_node_get_status(const void *fdt, int node) +{ + int len; + uint8_t status = ETHOSN_STATUS_DISABLED; + const char *node_status; + + node_status = fdt_getprop(fdt, node, "status", &len); + if (node_status == NULL || + (len == 5 && /* Includes null character */ + strncmp(node_status, "okay", 4U) == 0)) { + status = ETHOSN_STATUS_ENABLED; + } + + return status; +} + +int fconf_populate_ethosn_config(uintptr_t config) +{ + int ethosn_node; + int sub_node; + uint8_t ethosn_status; + uint32_t core_count = 0U; + uint32_t core_addr_idx = 0U; + const void *hw_conf_dtb = (const void *)config; + + /* Find offset to node with 'ethosn' compatible property */ + ethosn_node = fdt_node_offset_by_compatible(hw_conf_dtb, -1, "ethosn"); + if (ethosn_node < 0) { + ERROR("FCONF: Can't find 'ethosn' compatible node in dtb\n"); + return ethosn_node; + } + + /* If the Arm Ethos-N NPU is disabled the core check can be skipped */ + ethosn_status = fdt_node_get_status(hw_conf_dtb, ethosn_node); + if (ethosn_status == ETHOSN_STATUS_DISABLED) { + return 0; + } + + fdt_for_each_subnode(sub_node, hw_conf_dtb, ethosn_node) { + int err; + uintptr_t addr; + uint8_t status; + + /* Check that the sub node is "ethosn-core" compatible */ + if (fdt_node_check_compatible(hw_conf_dtb, sub_node, + "ethosn-core") != 0) { + /* Ignore incompatible sub node */ + continue; + } + + /* Including disabled cores */ + if (core_addr_idx >= ETHOSN_CORE_NUM_MAX) { + ERROR("FCONF: Reached max number of Arm Ethos-N NPU cores\n"); + return -1; + } + + status = fdt_node_get_status(hw_conf_dtb, ethosn_node); + if (status == ETHOSN_STATUS_DISABLED) { + ++core_addr_idx; + continue; + } + + err = fdt_get_reg_props_by_index(hw_conf_dtb, ethosn_node, + core_addr_idx, &addr, NULL); + if (err < 0) { + ERROR("FCONF: Failed to read reg property for Arm Ethos-N NPU core %u\n", + core_addr_idx); + return err; + } + + ethosn_config.core_addr[core_count++] = addr; + ++core_addr_idx; + } + + if ((sub_node < 0) && (sub_node != -FDT_ERR_NOTFOUND)) { + ERROR("FCONF: Failed to parse sub nodes\n"); + return sub_node; + } + + /* The Arm Ethos-N NPU can't be used if no cores were found */ + if (core_count == 0) { + ERROR("FCONF: No Arm Ethos-N NPU cores found\n"); + return -1; + } + + ethosn_config.num_cores = core_count; + ethosn_config.status = ethosn_status; + + return 0; +} + +FCONF_REGISTER_POPULATOR(HW_CONFIG, ethosn_config, fconf_populate_ethosn_config);