diff options
author | Jorge Ramirez-Ortiz <jorge@foundries.io> | 2021-10-07 23:02:13 +0200 |
---|---|---|
committer | Jérôme Forissier <jerome@forissier.org> | 2021-11-08 10:13:23 +0100 |
commit | 9b61a2bcfdba19f2492ae4f31d106affe2d0a255 (patch) | |
tree | 8c4a7f416e857deb317a75d52ab1487009a73181 /core | |
parent | f072eea41cb3cca68a2070449b4e0b806420f140 (diff) |
zynqmp: drivers: PM firmware
These routines call TF-A exported SiP services that implement IPI
protocol for communication with PMUFW (Platform Management Unit).
To access eFuses, PMUFW should be built with -DENABLE_EFUSE_ACCESS=1.
Notice however that certain eFuses will not be available unless the
Xilskey library linked to the PMUFW is compiled removing some of those
security restrictions.
Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
Signed-off-by: Jorge Ramirez-Ortiz <jorge@foundries.io>
Acked-by: Etienne Carriere <etienne.carriere@linaro.org>
Diffstat (limited to 'core')
-rw-r--r-- | core/arch/arm/plat-zynqmp/conf.mk | 2 | ||||
-rw-r--r-- | core/drivers/sub.mk | 1 | ||||
-rw-r--r-- | core/drivers/zynqmp_pm.c | 163 | ||||
-rw-r--r-- | core/include/drivers/zynqmp_efuse.h | 21 | ||||
-rw-r--r-- | core/include/drivers/zynqmp_pm.h | 54 |
5 files changed, 241 insertions, 0 deletions
diff --git a/core/arch/arm/plat-zynqmp/conf.mk b/core/arch/arm/plat-zynqmp/conf.mk index 5316fc00..ed773445 100644 --- a/core/arch/arm/plat-zynqmp/conf.mk +++ b/core/arch/arm/plat-zynqmp/conf.mk @@ -28,6 +28,8 @@ CFG_SHMEM_SIZE ?= 0x10000000 CFG_WITH_STATS ?= y CFG_CRYPTO_WITH_CE ?= y +CFG_ZYNQMP_PM ?= $(CFG_ARM64_core) + ifeq ($(CFG_ZYNQMP_CSU_AES),y) $(call force,CFG_ZYNQMP_CSUDMA,y,Mandated by CFG_ZYNQMP_CSU_AES) $(call force,CFG_DT,y,Mandated by CFG_ZYNQMP_CSU_AES) diff --git a/core/drivers/sub.mk b/core/drivers/sub.mk index 2b22232b..9edec347 100644 --- a/core/drivers/sub.mk +++ b/core/drivers/sub.mk @@ -41,6 +41,7 @@ srcs-$(CFG_IMX_SC) += imx_mu.c srcs-$(CFG_ZYNQMP_CSU_PUF) += zynqmp_csu_puf.c srcs-$(CFG_ZYNQMP_CSUDMA) += zynqmp_csudma.c srcs-$(CFG_ZYNQMP_CSU_AES) += zynqmp_csu_aes.c +srcs-$(CFG_ZYNQMP_PM) += zynqmp_pm.c subdirs-y += crypto subdirs-$(CFG_BNXT_FW) += bnxt diff --git a/core/drivers/zynqmp_pm.c b/core/drivers/zynqmp_pm.c new file mode 100644 index 00000000..59526fc8 --- /dev/null +++ b/core/drivers/zynqmp_pm.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (C) 2021 Foundries.io Ltd + */ + +#include <arm.h> +#include <drivers/zynqmp_pm.h> +#include <kernel/cache_helpers.h> +#include <kernel/thread.h> +#include <mm/core_memprot.h> +#include <string.h> +#include <tee/cache.h> +#include <tee_api_types.h> +#include <types_ext.h> +#include <utee_defines.h> + +/* + * For additional details about ZynqMP specific SMC ID's and PM request + * handling in TF-A check + * https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842107/Arm+Trusted+Firmware + */ +#define EFUSE_ACCESS_SMC 0xC2000035 +#define VERSION_ACCESS_SMC 0xC2000018 + +#define EFUSE_NOT_ENABLED 29 +#define VERSION_MASK GENMASK_32(3, 0) + +static uint32_t zynqmp_sip_call(uint32_t pm_api_id, uint32_t arg0, + uint32_t arg1, uint32_t arg2, uint32_t arg3, + uint32_t *payload) +{ + struct thread_smc_args args = { + .a0 = pm_api_id, + .a1 = reg_pair_to_64(arg1, arg0), + .a2 = reg_pair_to_64(arg3, arg2), + }; + + thread_smccc(&args); + + if (payload) { + switch (pm_api_id) { + case EFUSE_ACCESS_SMC: + *payload = args.a0 >> 32; + break; + case VERSION_ACCESS_SMC: + *payload = args.a1 & VERSION_MASK; + break; + default: + break; + } + } + + return args.a0; +} + +/* + * Stores all required details to read/write eFuse memory. + * @src: Physical address of the buffer to store the data to be + * written/read (in LE) + * @size: number of 32-bit words to be read/written + * @offset: offset in bytes to be read from/written to + * @flag: EFUSE_READ - represents eFuse read operation + * EFUSE_WRITE - represents eFuse write operation + * @pufuserfuse:0 - represents non-PUF eFuses, offset is used for read/write + * 1 - represents PUF user eFuse row number. + */ +struct xilinx_efuse { + uint64_t src; + uint32_t size; + uint32_t offset; + uint32_t flag; + uint32_t pufuserfuse; +}; + +enum efuse_op { EFUSE_READ = 0, EFUSE_WRITE = 1 }; + +#define EFUSE_ELT(__x) \ + [__x] = { \ + .offset = ZYNQMP_EFUSE_##__x##_OFFSET, \ + .bytes = ZYNQMP_EFUSE_##__x##_LENGTH, \ + } + +static const struct { + uint32_t offset; + uint32_t bytes; +} efuse_tbl[] = { + EFUSE_ELT(DNA), + EFUSE_ELT(IP_DISABLE), + EFUSE_ELT(MISC_USER_CTRL), + EFUSE_ELT(SEC_CTRL), +}; + +static TEE_Result efuse_op(enum efuse_op op, uint8_t *buf, size_t buf_sz, + enum zynqmp_efuse_id id, bool puf) +{ + struct xilinx_efuse efuse = { }; + paddr_t addr = 0; + uint32_t res = 0; + + if (!buf) + return TEE_ERROR_BAD_PARAMETERS; + + if (id >= ARRAY_SIZE(efuse_tbl)) { + EMSG("Invalid efuse"); + return TEE_ERROR_BAD_PARAMETERS; + } + + if (!IS_ALIGNED((uintptr_t)buf, CACHELINE_LEN)) { + EMSG("Buffer should be cache aligned"); + return TEE_ERROR_BAD_PARAMETERS; + } + + if (!IS_ALIGNED(buf_sz, CACHELINE_LEN)) + return TEE_ERROR_BAD_PARAMETERS; + + efuse.size = efuse_tbl[id].bytes / sizeof(uint32_t); + efuse.offset = efuse_tbl[id].offset; + efuse.src = virt_to_phys(buf); + efuse.pufuserfuse = puf; + efuse.flag = op; + + cache_operation(TEE_CACHECLEAN, buf, buf_sz); + cache_operation(TEE_CACHECLEAN, &efuse, sizeof(efuse)); + + addr = virt_to_phys(&efuse); + + res = zynqmp_sip_call(EFUSE_ACCESS_SMC, addr >> 32, addr, 0, 0, NULL); + if (res) { + if (res == EFUSE_NOT_ENABLED) + EMSG("eFuse access is not enabled"); + else + EMSG("Error in eFuse access %#"PRIx32, res); + + return TEE_ERROR_GENERIC; + } + + if (op == EFUSE_READ) + return cache_operation(TEE_CACHEINVALIDATE, buf, buf_sz); + + return TEE_ERROR_NOT_IMPLEMENTED; +} + +TEE_Result zynqmp_efuse_read(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id, + bool puf) +{ + return efuse_op(EFUSE_READ, buf, sz, id, puf); +} + +TEE_Result zynqmp_soc_version(uint32_t *version) +{ + uint32_t res = 0; + + if (!version) + return TEE_ERROR_BAD_PARAMETERS; + + res = zynqmp_sip_call(VERSION_ACCESS_SMC, 0, 0, 0, 0, version); + if (res) { + EMSG("Failed to retrieve version"); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} diff --git a/core/include/drivers/zynqmp_efuse.h b/core/include/drivers/zynqmp_efuse.h new file mode 100644 index 00000000..0064f72a --- /dev/null +++ b/core/include/drivers/zynqmp_efuse.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021 Foundries.io Ltd + */ + +#ifndef __DRIVERS_ZYNQMP_EFUSE_H__ +#define __DRIVERS_ZYNQMP_EFUSE_H__ + +#define ZYNQMP_EFUSE_DNA_OFFSET 0xc +#define ZYNQMP_EFUSE_DNA_LENGTH 12 + +#define ZYNQMP_EFUSE_IP_DISABLE_OFFSET 0x18 +#define ZYNQMP_EFUSE_IP_DISABLE_LENGTH 4 + +#define ZYNQMP_EFUSE_MISC_USER_CTRL_OFFSET 0x40 +#define ZYNQMP_EFUSE_MISC_USER_CTRL_LENGTH 4 + +#define ZYNQMP_EFUSE_SEC_CTRL_OFFSET 0x58 +#define ZYNQMP_EFUSE_SEC_CTRL_LENGTH 4 + +#endif /*__DRIVERS_ZYNQMP_EFUSE_H__*/ diff --git a/core/include/drivers/zynqmp_pm.h b/core/include/drivers/zynqmp_pm.h new file mode 100644 index 00000000..643984e0 --- /dev/null +++ b/core/include/drivers/zynqmp_pm.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021 Foundries.io Ltd + */ + +#ifndef __DRIVERS_ZYNQMP_PM_H__ +#define __DRIVERS_ZYNQMP_PM_H__ + +#include <drivers/zynqmp_efuse.h> +#include <platform_config.h> +#include <tee_api_types.h> +#include <util.h> + +/* + * Information about accessing eFuses and the Physically Uncloneable Function + * (PUF) Support can be found at + * https://www.xilinx.com/support/documentation/application_notes/xapp1319-zynq-usp-prog-nvm.pdf + */ +#define ZYNQMP_NONPUF_EFUSE 0 +#define ZYNQMP_PUF_EFUSE 1 + +/* List of eFuse identifiers */ +enum zynqmp_efuse_id { + DNA = 0, IP_DISABLE, MISC_USER_CTRL, SEC_CTRL, +}; + +/* Valid bytes in the eFuse */ +#define ZYNQMP_EFUSE_LEN(_id) ZYNQMP_EFUSE_##_id##_LENGTH + +/* Memory required to access the eFuse */ +#define ZYNQMP_EFUSE_MEM(_id) (ROUNDUP(ZYNQMP_EFUSE_LEN(_id), CACHELINE_LEN)) + +/* Alignment required in the buffers used to read the eFuse */ +#define __aligned_efuse __aligned(CACHELINE_LEN) + +/* + * Read eFuse memory + * @buf: buffer, where eFuse date will be stored. The data is returned + * in LE byte order. The buffer address must be cached aligned + * @buf_sz: buffer size in bytes, must be a multiple of the cacheline size + * @id: eFuse identifier. + * @puf: is eFuse PUF, ZYNQMP_PUF_EFUSE/ZYNQMP_NONPUF_EFUSE + * Return a TEE_Result compliant status + */ +TEE_Result zynqmp_efuse_read(uint8_t *buf, size_t buf_sz, + enum zynqmp_efuse_id id, bool puf); + +/* + * Read the SoC version number: + * Different eFuse bitfields carry different meaning depending on this version. + */ +TEE_Result zynqmp_soc_version(uint32_t *version); + +#endif /*__DRIVERS_ZYNQMP_PM_H__*/ |