1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (C) 2021 Foundries.io Ltd
4  */
5 
6 #include <arm.h>
7 #include <drivers/zynqmp_pm.h>
8 #include <kernel/cache_helpers.h>
9 #include <kernel/thread.h>
10 #include <mm/core_memprot.h>
11 #include <string.h>
12 #include <tee/cache.h>
13 #include <tee_api_types.h>
14 #include <types_ext.h>
15 #include <utee_defines.h>
16 
17 /*
18  * For additional details about ZynqMP specific SMC ID's and PM request
19  * handling in TF-A check
20  * https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842107/Arm+Trusted+Firmware
21  */
22 #define EFUSE_ACCESS_SMC	0xC2000035
23 #define VERSION_ACCESS_SMC	0xC2000018
24 
25 #define EFUSE_NOT_ENABLED	29
26 #define VERSION_MASK		GENMASK_32(3, 0)
27 
zynqmp_sip_call(uint32_t pm_api_id,uint32_t arg0,uint32_t arg1,uint32_t arg2,uint32_t arg3,uint32_t * payload)28 static uint32_t zynqmp_sip_call(uint32_t pm_api_id, uint32_t arg0,
29 				uint32_t arg1, uint32_t arg2, uint32_t arg3,
30 				uint32_t *payload)
31 {
32 	struct thread_smc_args args = {
33 		.a0 = pm_api_id,
34 		.a1 = reg_pair_to_64(arg1, arg0),
35 		.a2 = reg_pair_to_64(arg3, arg2),
36 	};
37 
38 	thread_smccc(&args);
39 
40 	if (payload) {
41 		switch (pm_api_id) {
42 		case EFUSE_ACCESS_SMC:
43 			*payload = args.a0 >> 32;
44 			break;
45 		case VERSION_ACCESS_SMC:
46 			*payload = args.a1 & VERSION_MASK;
47 			break;
48 		default:
49 			break;
50 		}
51 	}
52 
53 	return args.a0;
54 }
55 
56 /*
57  * Stores all required details to read/write eFuse memory.
58  * @src:        Physical address of the buffer to store the data to be
59  *              written/read (in LE)
60  * @size:       number of 32-bit words to be read/written
61  * @offset:     offset in bytes to be read from/written to
62  * @flag:       EFUSE_READ  - represents eFuse read operation
63  *              EFUSE_WRITE - represents eFuse write operation
64  * @pufuserfuse:0 - represents non-PUF eFuses, offset is used for read/write
65  *              1 - represents PUF user eFuse row number.
66  */
67 struct xilinx_efuse {
68 	uint64_t src;
69 	uint32_t size;
70 	uint32_t offset;
71 	uint32_t flag;
72 	uint32_t pufuserfuse;
73 };
74 
75 enum efuse_op { EFUSE_READ = 0, EFUSE_WRITE = 1 };
76 
77 #define EFUSE_ELT(__x) \
78 	[__x] = { \
79 		.offset = ZYNQMP_EFUSE_##__x##_OFFSET, \
80 		.bytes = ZYNQMP_EFUSE_##__x##_LENGTH, \
81 	}
82 
83 static const struct {
84 	uint32_t offset;
85 	uint32_t bytes;
86 } efuse_tbl[] = {
87 	EFUSE_ELT(DNA),
88 	EFUSE_ELT(IP_DISABLE),
89 	EFUSE_ELT(MISC_USER_CTRL),
90 	EFUSE_ELT(SEC_CTRL),
91 };
92 
efuse_op(enum efuse_op op,uint8_t * buf,size_t buf_sz,enum zynqmp_efuse_id id,bool puf)93 static TEE_Result efuse_op(enum efuse_op op, uint8_t *buf, size_t buf_sz,
94 			   enum zynqmp_efuse_id id, bool puf)
95 {
96 	struct xilinx_efuse efuse = { };
97 	paddr_t addr = 0;
98 	uint32_t res = 0;
99 
100 	if (!buf)
101 		return TEE_ERROR_BAD_PARAMETERS;
102 
103 	if (id >= ARRAY_SIZE(efuse_tbl)) {
104 		EMSG("Invalid efuse");
105 		return TEE_ERROR_BAD_PARAMETERS;
106 	}
107 
108 	if (!IS_ALIGNED((uintptr_t)buf, CACHELINE_LEN)) {
109 		EMSG("Buffer should be cache aligned");
110 		return TEE_ERROR_BAD_PARAMETERS;
111 	}
112 
113 	if (!IS_ALIGNED(buf_sz, CACHELINE_LEN))
114 		return TEE_ERROR_BAD_PARAMETERS;
115 
116 	efuse.size = efuse_tbl[id].bytes / sizeof(uint32_t);
117 	efuse.offset = efuse_tbl[id].offset;
118 	efuse.src = virt_to_phys(buf);
119 	efuse.pufuserfuse = puf;
120 	efuse.flag = op;
121 
122 	cache_operation(TEE_CACHECLEAN, buf, buf_sz);
123 	cache_operation(TEE_CACHECLEAN, &efuse, sizeof(efuse));
124 
125 	addr = virt_to_phys(&efuse);
126 
127 	res = zynqmp_sip_call(EFUSE_ACCESS_SMC, addr >> 32, addr, 0, 0, NULL);
128 	if (res) {
129 		if (res == EFUSE_NOT_ENABLED)
130 			EMSG("eFuse access is not enabled");
131 		else
132 			EMSG("Error in eFuse access %#"PRIx32, res);
133 
134 		return TEE_ERROR_GENERIC;
135 	}
136 
137 	if (op == EFUSE_READ)
138 		return cache_operation(TEE_CACHEINVALIDATE, buf, buf_sz);
139 
140 	return TEE_ERROR_NOT_IMPLEMENTED;
141 }
142 
zynqmp_efuse_read(uint8_t * buf,size_t sz,enum zynqmp_efuse_id id,bool puf)143 TEE_Result zynqmp_efuse_read(uint8_t *buf, size_t sz, enum zynqmp_efuse_id id,
144 			     bool puf)
145 {
146 	return efuse_op(EFUSE_READ, buf, sz, id, puf);
147 }
148 
zynqmp_soc_version(uint32_t * version)149 TEE_Result zynqmp_soc_version(uint32_t *version)
150 {
151 	uint32_t res = 0;
152 
153 	if (!version)
154 		return TEE_ERROR_BAD_PARAMETERS;
155 
156 	res = zynqmp_sip_call(VERSION_ACCESS_SMC, 0, 0, 0, 0, version);
157 	if (res) {
158 		EMSG("Failed to retrieve version");
159 		return TEE_ERROR_GENERIC;
160 	}
161 
162 	return TEE_SUCCESS;
163 }
164