aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJorge Ramirez-Ortiz <jorge@foundries.io>2021-10-07 23:02:13 +0200
committerJérôme Forissier <jerome@forissier.org>2021-11-08 10:13:23 +0100
commit9b61a2bcfdba19f2492ae4f31d106affe2d0a255 (patch)
tree8c4a7f416e857deb317a75d52ab1487009a73181 /core
parentf072eea41cb3cca68a2070449b4e0b806420f140 (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.mk2
-rw-r--r--core/drivers/sub.mk1
-rw-r--r--core/drivers/zynqmp_pm.c163
-rw-r--r--core/include/drivers/zynqmp_efuse.h21
-rw-r--r--core/include/drivers/zynqmp_pm.h54
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__*/