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