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 <mikael.olsson@arm.com>
Change-Id: I65dfd864042ed43faae0a259dcf319cbadb5f3d2
This commit is contained in:
Mikael Olsson 2021-02-12 17:30:22 +01:00 committed by Manish Pandey
parent 5d5fb10f9c
commit 76a21174d2
10 changed files with 452 additions and 7 deletions

View File

@ -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.*

View File

@ -0,0 +1,164 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <stdbool.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <drivers/arm/ethosn.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <plat/arm/common/fconf_ethosn_getter.h>
/* 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);
}
}

25
fdts/juno-ethosn.dtsi Normal file
View File

@ -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";
};
};
};

View File

@ -9,3 +9,7 @@
/ {
};
#if ARM_ETHOSN_NPU_DRIVER
#include "juno-ethosn.dtsi"
#endif

View File

@ -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 <lib/smccc.h>
/* 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 */

View File

@ -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)

View File

@ -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 <assert.h>
#include <lib/fconf/fconf.h>
#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 */

View File

@ -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

View File

@ -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 <common/debug.h>
#include <common/runtime_svc.h>
#include <drivers/arm/ethosn.h>
#include <lib/debugfs.h>
#include <lib/pmf/pmf.h>
#include <plat/arm/common/arm_sip_svc.h>
@ -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;

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <libfdt.h>
#include <plat/arm/common/fconf_ethosn_getter.h>
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);