1 /*
2 * Copyright (c) 2023-2024, Arm Limited. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /**
18 * \file integrity_checker_drv.c
19 * \brief Driver for Arm Integrity Checker.
20 */
21
22 #include "integrity_checker_drv.h"
23
24 #include "tfm_hal_device_header.h"
25 #include "fatal_error.h"
26
27 #include <stdbool.h>
28
29 struct _integrity_checker_reg_map_t {
30 __IM uint32_t icbc;
31 /*!< Offset: 0x000 (R/ ) Integrity Checker Build Configuration Register */
32 __IOM uint32_t icc;
33 /*!< Offset: 0x004 (R/W) Integrity Checker Configuration Register */
34 __IM uint32_t icis;
35 /*!< Offset: 0x008 (R/ ) Integrity Checker Interrupt Status Register */
36 __IOM uint32_t icie;
37 /*!< Offset: 0x00C (R/W) Integrity Checker Interrupt Enable Register */
38 __IOM uint32_t icae;
39 /*!< Offset: 0x010 (R/W) Integrity Checker Alarm Enable Register */
40 __IOM uint32_t icic;
41 /*!< Offset: 0x014 (R/W) Integrity Checker Interrupt Clear Register */
42 __IOM uint32_t icda;
43 /*!< Offset: 0x018 (R/W) Integrity Checker Data Address Register */
44 __IOM uint32_t icdl;
45 /*!< Offset: 0x01C (R/W) Integrity Checker Data Length Register */
46 __IOM uint32_t iceva;
47 /*!< Offset: 0x020 (R/W) Integrity Checker Expected Value Address Register */
48 __IOM uint32_t iccva;
49 /*!< Offset: 0x024 (R/W) Integrity Checker Computed Value Address Register */
50 __IM uint32_t iccval[8];
51 /*!< Offset: 0x028 (R/ ) Integrity Checker Computed Value Register */
52 __IOM uint32_t reserved_0[0x3E1];
53 /*!< Offset: 0x48-0xFCC Reserved */
54 __IM uint32_t pidr4;
55 /*!< Offset: 0xFD0 (R/ ) Peripheral ID 4 */
56 __IOM uint32_t reserved_1[3];
57 /*!< Offset: 0xFD4-0xFDC Reserved */
58 __IM uint32_t pidr0;
59 /*!< Offset: 0xFE0 (R/ ) Peripheral ID 0 */
60 __IM uint32_t pidr1;
61 /*!< Offset: 0xFE4 (R/ ) Peripheral ID 1 */
62 __IM uint32_t pidr2;
63 /*!< Offset: 0xFE8 (R/ ) Peripheral ID 2 */
64 __IM uint32_t pidr3;
65 /*!< Offset: 0xFEC (R/ ) Peripheral ID 3 */
66 __IM uint32_t cidr0;
67 /*!< Offset: 0xFF0 (R/ ) Component ID 0 */
68 __IM uint32_t cidr1;
69 /*!< Offset: 0xFF4 (R/ ) Component ID 1 */
70 __IM uint32_t cidr2;
71 /*!< Offset: 0xFF8 (R/ ) Component ID 2 */
72 __IM uint32_t cidr3;
73 /*!< Offset: 0xFFC (R/ ) Component ID 3 */
74 };
75
76 enum ic_aprot {
77 IC_APROT_SECURE_UNPRIVILEGED = 0x0,
78 IC_APROT_SECURE_PRIVILEGED = 0x1,
79 IC_APROT_NON_SECURE_UNPRIVILEGED = 0x2,
80 IC_APROT_NON_SECURE_PRIVILEGED = 0x3,
81 };
82
remap_addr(struct integrity_checker_dev_t * dev,uintptr_t addr)83 static uintptr_t remap_addr(struct integrity_checker_dev_t *dev, uintptr_t addr)
84 {
85 uint32_t idx;
86 const integrity_checker_remap_region_t *region;
87
88 for (idx = 0; idx < INTEGRITY_CHECKER_CONFIG_REMAP_REGION_AM; idx++) {
89 region = &dev->cfg->remap_regions[idx];
90 if (addr >= region->region_base
91 && addr < region->region_base + region->region_size) {
92 return (addr - region->region_base) + region->remap_base
93 + (region->remap_cpusel_offset * dev->cfg->remap_cpusel);
94 }
95 }
96
97 return addr;
98 }
99
100 static const size_t mode_sizes[3] = {
101 INTEGRITY_CHECKER_OUTPUT_SIZE_ZERO_COUNT,
102 INTEGRITY_CHECKER_OUTPUT_SIZE_CRC32,
103 INTEGRITY_CHECKER_OUTPUT_SIZE_SHA256,
104 };
105
check_mode_is_supported(struct integrity_checker_dev_t * dev,enum integrity_checker_mode_t mode,bool is_compute)106 static enum integrity_checker_error_t check_mode_is_supported(
107 struct integrity_checker_dev_t *dev,
108 enum integrity_checker_mode_t mode,
109 bool is_compute)
110 {
111 struct _integrity_checker_reg_map_t* p_integrity_checker =
112 (struct _integrity_checker_reg_map_t*)dev->cfg->base;
113 uint32_t bitmask = (1 << (mode + 3 * (is_compute)));
114
115 if (mode > INTEGRITY_CHECKER_MODE_SHA256) {
116 FATAL_ERR(INTEGRITY_CHECKER_ERROR_UNSUPPORTED_MODE);
117 return INTEGRITY_CHECKER_ERROR_UNSUPPORTED_MODE;
118 }
119
120 if (p_integrity_checker->icbc & bitmask) {
121 return INTEGRITY_CHECKER_ERROR_NONE;
122 } else {
123 FATAL_ERR(INTEGRITY_CHECKER_ERROR_UNSUPPORTED_MODE);
124 return INTEGRITY_CHECKER_ERROR_UNSUPPORTED_MODE;
125 }
126 }
127
init_integrity_checker(struct integrity_checker_dev_t * dev,uint32_t * iccval)128 static void init_integrity_checker(struct integrity_checker_dev_t *dev,
129 uint32_t *iccval)
130 {
131 struct _integrity_checker_reg_map_t* p_integrity_checker =
132 (struct _integrity_checker_reg_map_t*)dev->cfg->base;
133
134 /* Set MatchTriggerDisable, as it is mandatory. */
135 *iccval |= 1 << 5;
136
137 /* Set EncompvalOut so the integrity checker writes the value pointer in
138 * compute mode.
139 */
140 *iccval |= 1 << 6;
141
142 *iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 7;
143 *iccval |= (IC_APROT_SECURE_PRIVILEGED & 0b11) << 9;
144
145 /* Disable all alarms */
146 p_integrity_checker->icae = 0;
147
148 /* Enable and clear all interrupts */
149 p_integrity_checker->icie = 0xFF;
150 p_integrity_checker->icic = 0xFF;
151 }
152
integrity_checker_compute_value(struct integrity_checker_dev_t * dev,enum integrity_checker_mode_t mode,const uint32_t * data,size_t size,uint32_t * value,size_t value_size,size_t * value_len)153 enum integrity_checker_error_t integrity_checker_compute_value(struct integrity_checker_dev_t *dev,
154 enum integrity_checker_mode_t mode,
155 const uint32_t *data, size_t size,
156 uint32_t *value, size_t value_size,
157 size_t *value_len)
158 {
159 uint32_t __ALIGNED(INTEGRITY_CHECKER_REQUIRED_ALIGNMENT)
160 temp_val[INTEGRITY_CHECKER_OUTPUT_SIZE_SHA256 / sizeof(uint32_t)] = {0};
161 uint32_t *value_ptr = value;
162 struct _integrity_checker_reg_map_t* p_integrity_checker =
163 (struct _integrity_checker_reg_map_t*)dev->cfg->base;
164 enum integrity_checker_error_t err;
165
166 uint32_t iccval = 0;
167
168 err = check_mode_is_supported(dev, mode, true);
169 if (err != INTEGRITY_CHECKER_ERROR_NONE) {
170 return err;
171 }
172
173 if (value_size != mode_sizes[mode]) {
174 FATAL_ERR(INTEGRITY_CHECKER_ERROR_OUTPUT_BUFFER_TOO_SMALL);
175 return INTEGRITY_CHECKER_ERROR_OUTPUT_BUFFER_TOO_SMALL;
176 }
177
178 if (((uintptr_t)data % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
179 FATAL_ERR(INTEGRITY_CHECKER_ERROR_INVALID_ALIGNMENT);
180 return INTEGRITY_CHECKER_ERROR_INVALID_ALIGNMENT;
181 }
182
183 if ((size % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
184 FATAL_ERR(INTEGRITY_CHECKER_ERROR_INVALID_LENGTH);
185 return INTEGRITY_CHECKER_ERROR_INVALID_LENGTH;
186 }
187
188 if (((uintptr_t)value % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
189 value_ptr = temp_val;
190 }
191
192 init_integrity_checker(dev, &iccval);
193
194 /* Set algorithm */
195 iccval |= (mode & 0b111) << 1;
196
197 /* Set to compute mode */
198 iccval |= 1 << 4;
199
200 /* Configure input data. Size is in words */
201 p_integrity_checker->icda = remap_addr(dev, (uint32_t)data);
202 p_integrity_checker->icdl = size / INTEGRITY_CHECKER_REQUIRED_ALIGNMENT;
203
204 /* Set output address */
205 p_integrity_checker->iccva = remap_addr(dev, (uint32_t)value_ptr);
206
207 /* Start integrity checker */
208 iccval |= 1;
209
210 p_integrity_checker->icc = iccval;
211
212 /* Poll for any interrupts */
213 while(!p_integrity_checker->icis) {}
214
215 /* Check for any unusual error interrupts */
216 if (p_integrity_checker->icis & (~0b11)) {
217 FATAL_ERR(INTEGRITY_CHECKER_ERROR_OPERATION_FAILED);
218 return INTEGRITY_CHECKER_ERROR_OPERATION_FAILED;
219 }
220
221 if (value_ptr != value) {
222 for (int idx = 0; idx < mode_sizes[mode] / sizeof(uint32_t); idx++) {
223 value[idx] = value_ptr[idx];
224 }
225 }
226
227 if (value_len != NULL) {
228 *value_len = mode_sizes[mode];
229 }
230
231 return INTEGRITY_CHECKER_ERROR_NONE;
232 }
233
integrity_checker_check_value(struct integrity_checker_dev_t * dev,enum integrity_checker_mode_t mode,const uint32_t * data,size_t size,const uint32_t * value,size_t value_size)234 enum integrity_checker_error_t integrity_checker_check_value(struct integrity_checker_dev_t *dev,
235 enum integrity_checker_mode_t mode,
236 const uint32_t *data, size_t size,
237 const uint32_t *value, size_t value_size)
238 {
239 uint32_t __ALIGNED(INTEGRITY_CHECKER_REQUIRED_ALIGNMENT)
240 temp_val[INTEGRITY_CHECKER_OUTPUT_SIZE_SHA256 / sizeof(uint32_t)] = {0};
241 uint32_t *value_ptr = (uint32_t *)value;
242 struct _integrity_checker_reg_map_t* p_integrity_checker =
243 (struct _integrity_checker_reg_map_t*)dev->cfg->base;
244 enum integrity_checker_error_t err;
245 uint32_t iccval = 0;
246
247 err = check_mode_is_supported(dev, mode, false);
248 if (err != INTEGRITY_CHECKER_ERROR_NONE) {
249 return err;
250 }
251
252 if (value_size != mode_sizes[mode]) {
253 FATAL_ERR(INTEGRITY_CHECKER_ERROR_VALUE_BUFFER_TOO_SMALL);
254 return INTEGRITY_CHECKER_ERROR_VALUE_BUFFER_TOO_SMALL;
255 }
256
257 if (((uintptr_t)data % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
258 FATAL_ERR(INTEGRITY_CHECKER_ERROR_INVALID_ALIGNMENT);
259 return INTEGRITY_CHECKER_ERROR_INVALID_ALIGNMENT;
260 }
261
262 if ((size % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
263 FATAL_ERR(INTEGRITY_CHECKER_ERROR_INVALID_LENGTH);
264 return INTEGRITY_CHECKER_ERROR_INVALID_LENGTH;
265 }
266
267 if (((uintptr_t)value % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0
268 || (value_size % INTEGRITY_CHECKER_REQUIRED_ALIGNMENT) != 0) {
269 for (int idx = 0; idx < value_size / sizeof(uint32_t); idx++) {
270 temp_val[idx] = value[idx];
271 }
272 value_ptr = temp_val;
273 }
274
275 init_integrity_checker(dev, &iccval);
276
277 /* Set algorithm */
278 iccval |= (mode & 0b111) << 1;
279
280 /* Configure input data. Size is in words */
281 p_integrity_checker->icda = remap_addr(dev, (uint32_t)data);
282 p_integrity_checker->icdl = size / INTEGRITY_CHECKER_REQUIRED_ALIGNMENT;
283
284 /* Set compare address */
285 p_integrity_checker->iceva = remap_addr(dev, (uint32_t)value_ptr);
286
287 /* Start integrity checker */
288 iccval |= 1;
289
290 p_integrity_checker->icc = iccval;
291
292 /* Poll for any interrupts */
293 while(!p_integrity_checker->icis) {}
294
295 /* Check for any unusual error interrupts */
296 if (p_integrity_checker->icis & (~0b11)) {
297 FATAL_ERR(INTEGRITY_CHECKER_ERROR_OPERATION_FAILED);
298 return INTEGRITY_CHECKER_ERROR_OPERATION_FAILED;
299 } else if (p_integrity_checker->icis & (0b10)) {
300 /* Check for comparison failure */
301 NONFATAL_ERR(INTEGRITY_CHECKER_ERROR_COMPARISON_FAILED);
302 return INTEGRITY_CHECKER_ERROR_COMPARISON_FAILED;
303 }
304
305 return INTEGRITY_CHECKER_ERROR_NONE;
306 }
307