1 /*
2 * Copyright (c) 2020-2024, Arm Limited. All rights reserved.
3 * Copyright (c) 2022-2023 Cypress Semiconductor Corporation (an Infineon
4 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
5 * reserved.
6 *
7 * SPDX-License-Identifier: BSD-3-Clause
8 *
9 */
10
11 /*
12 * The current implementation in this file only supports isolation
13 * level 1 and level 2.
14 */
15
16 #include <arm_cmse.h>
17 #include <stddef.h>
18 #include <stdint.h>
19 #include <string.h>
20 #include <stdbool.h>
21 #include "array.h"
22 #include "tfm_hal_device_header.h"
23 #include "region.h"
24 #include "armv8m_mpu.h"
25 #include "common_target_cfg.h"
26 #include "tfm_hal_defs.h"
27 #include "tfm_hal_isolation.h"
28 #include "tfm_peripherals_def.h"
29 #include "load/spm_load_api.h"
30
31 #define PROT_BOUNDARY_VAL \
32 ((1U << HANDLE_ATTR_PRIV_POS) & HANDLE_ATTR_PRIV_MASK)
33 /* Boundary handle binding macros. */
34 #define HANDLE_ATTR_PRIV_POS 1U
35 #define HANDLE_ATTR_PRIV_MASK (0x1UL << HANDLE_ATTR_PRIV_POS)
36 #define HANDLE_ATTR_NS_POS 0U
37 #define HANDLE_ATTR_NS_MASK (0x1UL << HANDLE_ATTR_NS_POS)
38
39 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
40 static uint32_t n_configured_regions = 0;
41
42 #ifdef CONFIG_TFM_USE_TRUSTZONE
43 REGION_DECLARE(Image$$, ER_VENEER, $$Base);
44 REGION_DECLARE(Image$$, VENEER_ALIGN, $$Limit);
45 #endif /* CONFIG_TFM_USE_TRUSTZONE */
46 REGION_DECLARE(Image$$, TFM_UNPRIV_CODE_START, $$RO$$Base);
47 REGION_DECLARE(Image$$, TFM_UNPRIV_CODE_END, $$RO$$Limit);
48 REGION_DECLARE(Image$$, TFM_APP_CODE_START, $$Base);
49 REGION_DECLARE(Image$$, TFM_APP_CODE_END, $$Base);
50 REGION_DECLARE(Image$$, TFM_APP_RW_STACK_START, $$Base);
51 REGION_DECLARE(Image$$, TFM_APP_RW_STACK_END, $$Base);
52 #ifdef CONFIG_TFM_PARTITION_META
53 REGION_DECLARE(Image$$, TFM_SP_META_PTR, $$ZI$$Base);
54 REGION_DECLARE(Image$$, TFM_SP_META_PTR_END, $$ZI$$Limit);
55 #endif /* CONFIG_TFM_PARTITION_META */
56
57 #define ARM_MPU_NON_TRANSIENT ( 1U )
58 #define ARM_MPU_TRANSIENT ( 0U )
59 #define ARM_MPU_WRITE_BACK ( 1U )
60 #define ARM_MPU_WRITE_THROUGH ( 0U )
61 #define ARM_MPU_READ_ALLOCATE ( 1U )
62 #define ARM_MPU_NON_READ_ALLOCATE ( 0U )
63 #define ARM_MPU_WRITE_ALLOCATE ( 1U )
64 #define ARM_MPU_NON_WRITE_ALLOCATE ( 0U )
65 #define ARM_MPU_READ_ONLY ( 1U )
66 #define ARM_MPU_READ_WRITE ( 0U )
67 #define ARM_MPU_UNPRIVILEGED ( 1U )
68 #define ARM_MPU_PRIVILEGED ( 0U )
69 #define ARM_MPU_EXECUTE_NEVER ( 1U )
70 #define ARM_MPU_EXECUTE_OK ( 0U )
71 #define ARM_MPU_PRIVILEGE_EXECUTE_NEVER ( 1U )
72 #define ARM_MPU_PRIVILEGE_EXECUTE_OK ( 0U )
73
74 #endif /* CONFIG_TFM_ENABLE_MEMORY_PROTECT */
75
tfm_hal_set_up_static_boundaries(uintptr_t * p_spm_boundary)76 enum tfm_hal_status_t tfm_hal_set_up_static_boundaries(
77 uintptr_t *p_spm_boundary)
78 {
79 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
80 const ARM_MPU_Region_t mpu_region_attributes[] = {
81 #ifdef CONFIG_TFM_USE_TRUSTZONE
82 /* Veneer region
83 * Region Number 0, Non-shareable, Read-Only, Non-Privileged, Executable,
84 * Privilege Executable - if PXN available, Attribute set: 0
85 */
86 {
87 ARM_MPU_RBAR((uint32_t)®ION_NAME(Image$$, ER_VENEER, $$Base),
88 ARM_MPU_SH_NON,
89 ARM_MPU_READ_ONLY,
90 ARM_MPU_UNPRIVILEGED,
91 ARM_MPU_EXECUTE_OK),
92 #ifdef TFM_PXN_ENABLE
93 ARM_MPU_RLAR_PXN((uint32_t)®ION_NAME(Image$$, VENEER_ALIGN, $$Limit) - 1,
94 ARM_MPU_PRIVILEGE_EXECUTE_OK,
95 0)
96 #else
97 ARM_MPU_RLAR((uint32_t)®ION_NAME(Image$$, VENEER_ALIGN, $$Limit) - 1,
98 0)
99 #endif
100 },
101 #endif /* CONFIG_TFM_USE_TRUSTZONE */
102 /* TFM Core unprivileged code region
103 * Region Number 1, Non-shareable, Read-Only, Non-Privileged, Executable,
104 * Privilege Executable - if PXN available, Attribute set: 0
105 */
106 {
107 ARM_MPU_RBAR((uint32_t)®ION_NAME(Image$$, TFM_UNPRIV_CODE_START, $$RO$$Base),
108 ARM_MPU_SH_NON,
109 ARM_MPU_READ_ONLY,
110 ARM_MPU_UNPRIVILEGED,
111 ARM_MPU_EXECUTE_OK),
112 #ifdef TFM_PXN_ENABLE
113 ARM_MPU_RLAR_PXN((uint32_t)®ION_NAME(Image$$, TFM_UNPRIV_CODE_END, $$RO$$Limit) - 1,
114 ARM_MPU_PRIVILEGE_EXECUTE_OK,
115 0)
116 #else
117 ARM_MPU_RLAR((uint32_t)®ION_NAME(Image$$, TFM_UNPRIV_CODE_END, $$RO$$Limit) - 1,
118 0)
119 #endif
120 },
121 /* RO region
122 * Region Number 2, Non-shareable, Read-Only, Non-Privileged, Executable,
123 * PXN depends on isolation level, Attribute set: 0
124 */
125 {
126 ARM_MPU_RBAR((uint32_t)®ION_NAME(Image$$, TFM_APP_CODE_START, $$Base),
127 ARM_MPU_SH_NON,
128 ARM_MPU_READ_ONLY,
129 ARM_MPU_UNPRIVILEGED,
130 ARM_MPU_EXECUTE_OK),
131 #ifdef TFM_PXN_ENABLE
132 ARM_MPU_RLAR_PXN((uint32_t)®ION_NAME(Image$$, TFM_APP_CODE_END, $$Base) - 1,
133 #if TFM_ISOLATION_LEVEL == 1
134 ARM_MPU_PRIVILEGE_EXECUTE_OK,
135 #else
136 ARM_MPU_PRIVILEGE_EXECUTE_NEVER,
137 #endif
138 0)
139 #else
140 ARM_MPU_RLAR((uint32_t)®ION_NAME(Image$$, TFM_APP_CODE_END, $$Base) - 1,
141 0)
142 #endif
143 },
144 /* RW, ZI and stack as one region
145 * Region Number 3, Non-shareable, Read-Write, Non-Privileged, Execute Never
146 * Attribute set: 1, Privilege Execute Never - if PXN available
147 */
148 {
149 ARM_MPU_RBAR((uint32_t)®ION_NAME(Image$$, TFM_APP_RW_STACK_START, $$Base),
150 ARM_MPU_SH_NON,
151 ARM_MPU_READ_WRITE,
152 ARM_MPU_UNPRIVILEGED,
153 ARM_MPU_EXECUTE_NEVER),
154 #ifdef TFM_PXN_ENABLE
155 ARM_MPU_RLAR_PXN((uint32_t)®ION_NAME(Image$$, TFM_APP_RW_STACK_END, $$Base) - 1,
156 ARM_MPU_PRIVILEGE_EXECUTE_NEVER,
157 1)
158 #else
159 ARM_MPU_RLAR((uint32_t)®ION_NAME(Image$$, TFM_APP_RW_STACK_END, $$Base) - 1,
160 1)
161 #endif
162 },
163 #ifdef CONFIG_TFM_PARTITION_META
164 /* TFM partition metadata pointer region
165 * Region Number 4, Non-shareable, Read-Write, Non-Privileged, Execute Never
166 * Attribute set: 1, Privilege Execute Never - if PXN available
167 */
168 {
169 ARM_MPU_RBAR((uint32_t)®ION_NAME(Image$$, TFM_SP_META_PTR, $$ZI$$Base),
170 ARM_MPU_SH_NON,
171 ARM_MPU_READ_WRITE,
172 ARM_MPU_UNPRIVILEGED,
173 ARM_MPU_EXECUTE_NEVER),
174 #ifdef TFM_PXN_ENABLE
175 ARM_MPU_RLAR_PXN((uint32_t)®ION_NAME(Image$$, TFM_SP_META_PTR_END, $$ZI$$Limit) - 1,
176 ARM_MPU_PRIVILEGE_EXECUTE_NEVER,
177 1)
178 #else
179 ARM_MPU_RLAR((uint32_t)®ION_NAME(Image$$, TFM_SP_META_PTR_END, $$ZI$$Limit) - 1,
180 1)
181 #endif
182 },
183 #endif
184 /* Individual platforms may add further static MPU regions by defining
185 * PLATFORM_STATIC_MPU_REGIONS in their tfm_peripherals_def.h header.
186 */
187 #ifdef PLATFORM_STATIC_MPU_REGIONS
188 PLATFORM_STATIC_MPU_REGIONS
189 #endif
190 };
191 ARM_MPU_Region_t localcfg;
192 #endif /* CONFIG_TFM_ENABLE_MEMORY_PROTECT */
193 /* Set up isolation boundaries between SPE and NSPE */
194 sau_and_idau_cfg();
195 if (mpc_init_cfg() != TFM_PLAT_ERR_SUCCESS) {
196 return TFM_HAL_ERROR_GENERIC;
197 }
198 ppc_init_cfg();
199
200 /* Set up static isolation boundaries inside SPE */
201 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
202 int32_t i;
203
204 uint32_t mpu_region_num =
205 (MPU ->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
206
207 if (mpu_region_num < ARRAY_SIZE(mpu_region_attributes)) {
208 return TFM_HAL_ERROR_GENERIC;
209 }
210
211 /* Turn off MPU during configuration */
212 if ((MPU->CTRL & MPU_CTRL_ENABLE_Msk)) {
213 ARM_MPU_Disable();
214 }
215 /* Disable all regions */
216 for (i = 0; i < mpu_region_num; i++) {
217 ARM_MPU_ClrRegion(i);
218 }
219
220 /* Configure attribute registers
221 * Attr0 : Normal memory, Inner/Outer Cacheable, Write-Trough Read-Allocate
222 */
223 ARM_MPU_SetMemAttr(0,
224 ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(ARM_MPU_NON_TRANSIENT,
225 ARM_MPU_WRITE_THROUGH,
226 ARM_MPU_READ_ALLOCATE,
227 ARM_MPU_NON_WRITE_ALLOCATE),
228 ARM_MPU_ATTR_MEMORY_(ARM_MPU_NON_TRANSIENT,
229 ARM_MPU_WRITE_THROUGH,
230 ARM_MPU_READ_ALLOCATE,
231 ARM_MPU_NON_WRITE_ALLOCATE)));
232 /* Attr1 : Normal memory, Inner/Outer Cacheable, Write-Back R-W Allocate */
233 ARM_MPU_SetMemAttr(1,
234 ARM_MPU_ATTR(ARM_MPU_ATTR_MEMORY_(ARM_MPU_NON_TRANSIENT,
235 ARM_MPU_WRITE_BACK,
236 ARM_MPU_READ_ALLOCATE,
237 ARM_MPU_WRITE_ALLOCATE),
238 ARM_MPU_ATTR_MEMORY_(ARM_MPU_NON_TRANSIENT,
239 ARM_MPU_WRITE_BACK,
240 ARM_MPU_READ_ALLOCATE,
241 ARM_MPU_WRITE_ALLOCATE)));
242 /* Attr2 : Device memory, nGnRE */
243 ARM_MPU_SetMemAttr(2,
244 ARM_MPU_ATTR(ARM_MPU_ATTR_DEVICE,
245 ARM_MPU_ATTR_DEVICE_nGnRE));
246
247 /* Configure regions */
248 /* Note: CMSIS MPU API clears the lower 5 address bits without check */
249 for (i = 0; i < ARRAY_SIZE(mpu_region_attributes); i++) {
250 localcfg.RBAR = mpu_region_attributes[i].RBAR;
251 localcfg.RLAR = mpu_region_attributes[i].RLAR;
252 ARM_MPU_SetRegion(i, localcfg.RBAR, localcfg.RLAR);
253 }
254 n_configured_regions = i;
255
256 /* Enable MPU with the above configurations. Allow default memory map for
257 * privileged software and enable MPU during HardFault and NMI handlers.
258 */
259 ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
260 #endif /* CONFIG_TFM_ENABLE_MEMORY_PROTECT */
261
262 *p_spm_boundary = (uintptr_t)PROT_BOUNDARY_VAL;
263
264 return TFM_HAL_SUCCESS;
265 }
266
267 /*
268 * Implementation of tfm_hal_bind_boundary():
269 *
270 * The API encodes some attributes into a handle and returns it to SPM.
271 * The attributes include isolation boundaries, privilege, and MMIO information.
272 * When scheduler switches running partitions, SPM compares the handle between
273 * partitions to know if boundary update is necessary. If update is required,
274 * SPM passes the handle to platform to do platform settings and update
275 * isolation boundaries.
276 */
tfm_hal_bind_boundary(const struct partition_load_info_t * p_ldinf,uintptr_t * p_boundary)277 enum tfm_hal_status_t tfm_hal_bind_boundary(
278 const struct partition_load_info_t *p_ldinf,
279 uintptr_t *p_boundary)
280 {
281 uint32_t i, j;
282 bool privileged;
283 bool ns_agent_tz;
284 uint32_t partition_attrs = 0;
285 const struct asset_desc_t *p_asset;
286 struct platform_data_t *plat_data_ptr;
287 const uintptr_t* mmio_list;
288 size_t mmio_list_length;
289
290 #if TFM_ISOLATION_LEVEL == 2
291 ARM_MPU_Region_t local_mpu_region;
292 uint32_t mpu_region_num;
293 #endif
294 if (!p_ldinf || !p_boundary) {
295 return TFM_HAL_ERROR_GENERIC;
296 }
297
298 #if TFM_ISOLATION_LEVEL == 1
299 privileged = true;
300 #else
301 privileged = IS_PSA_ROT(p_ldinf);
302 #endif
303
304 ns_agent_tz = IS_NS_AGENT_TZ(p_ldinf);
305 p_asset = LOAD_INFO_ASSET(p_ldinf);
306
307 get_partition_named_mmio_list(&mmio_list, &mmio_list_length);
308
309 /*
310 * Validate if the named MMIO of partition is allowed by the platform.
311 * Otherwise, skip validation.
312 *
313 * NOTE: Need to add validation of numbered MMIO if platform requires.
314 */
315 for (i = 0; i < p_ldinf->nassets; i++) {
316 if (!(p_asset[i].attr & ASSET_ATTR_NAMED_MMIO)) {
317 continue;
318 }
319 for (j = 0; j < mmio_list_length; j++) {
320 if (p_asset[i].dev.dev_ref == mmio_list[j]) {
321 break;
322 }
323 }
324
325 if (j == mmio_list_length) {
326 /* The MMIO asset is not in the allowed list of platform. */
327 return TFM_HAL_ERROR_GENERIC;
328 }
329 /* Assume PPC & MPC settings are required even under level 1 */
330 plat_data_ptr = REFERENCE_TO_PTR(p_asset[i].dev.dev_ref,
331 struct platform_data_t *);
332
333 if (plat_data_ptr->periph_ppc_bank != PPC_SP_DO_NOT_CONFIGURE) {
334 ppc_configure_to_secure(plat_data_ptr->periph_ppc_bank,
335 plat_data_ptr->periph_ppc_mask);
336 if (privileged) {
337 ppc_clr_secure_unpriv(plat_data_ptr->periph_ppc_bank,
338 plat_data_ptr->periph_ppc_mask);
339 } else {
340 ppc_en_secure_unpriv(plat_data_ptr->periph_ppc_bank,
341 plat_data_ptr->periph_ppc_mask);
342 }
343 }
344 #if TFM_ISOLATION_LEVEL == 2
345 /*
346 * Static boundaries are set. Set up MPU region for MMIO.
347 * Setup regions for unprivileged assets only.
348 */
349 if (!privileged) {
350 mpu_region_num =
351 (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos;
352
353 /* There is a limited number of available MPU regions in v8M */
354 if (mpu_region_num <= n_configured_regions) {
355 return TFM_HAL_ERROR_GENERIC;
356 }
357 if ((plat_data_ptr->periph_start & ~MPU_RBAR_BASE_Msk) != 0) {
358 return TFM_HAL_ERROR_GENERIC;
359 }
360 if ((plat_data_ptr->periph_limit & ~MPU_RLAR_LIMIT_Msk) != 0x1F) {
361 return TFM_HAL_ERROR_GENERIC;
362 }
363
364 /* Turn off MPU during configuration */
365 if (MPU->CTRL & MPU_CTRL_ENABLE_Msk) {
366 ARM_MPU_Disable();
367 }
368
369 /* Assemble region base and limit address register contents. */
370 local_mpu_region.RBAR = ARM_MPU_RBAR(plat_data_ptr->periph_start,
371 ARM_MPU_SH_NON,
372 ARM_MPU_READ_WRITE,
373 ARM_MPU_UNPRIVILEGED,
374 ARM_MPU_EXECUTE_NEVER);
375 /* Attr2 contains required attribute set for device regions */
376 #ifdef TFM_PXN_ENABLE
377 local_mpu_region.RLAR = ARM_MPU_RLAR_PXN(plat_data_ptr->periph_limit,
378 ARM_MPU_PRIVILEGE_EXECUTE_NEVER,
379 2);
380 #else
381 local_mpu_region.RLAR = ARM_MPU_RLAR(plat_data_ptr->periph_limit,
382 2);
383 #endif
384
385 /* Configure device mpu region */
386 ARM_MPU_SetRegion(n_configured_regions,
387 local_mpu_region.RBAR,
388 local_mpu_region.RLAR);
389
390 n_configured_regions++;
391
392 /* Enable MPU with the new region added */
393 ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
394 }
395 #endif
396 }
397
398 partition_attrs = ((uint32_t)privileged << HANDLE_ATTR_PRIV_POS) &
399 HANDLE_ATTR_PRIV_MASK;
400 partition_attrs |= ((uint32_t)ns_agent_tz << HANDLE_ATTR_NS_POS) &
401 HANDLE_ATTR_NS_MASK;
402 *p_boundary = (uintptr_t)partition_attrs;
403
404 return TFM_HAL_SUCCESS;
405 }
406
tfm_hal_activate_boundary(const struct partition_load_info_t * p_ldinf,uintptr_t boundary)407 enum tfm_hal_status_t tfm_hal_activate_boundary(
408 const struct partition_load_info_t *p_ldinf,
409 uintptr_t boundary)
410 {
411 CONTROL_Type ctrl;
412 bool privileged = !!((uint32_t)boundary & HANDLE_ATTR_PRIV_MASK);
413
414 /* Privileged level is required to be set always */
415 ctrl.w = __get_CONTROL();
416 ctrl.b.nPRIV = privileged ? 0 : 1;
417 __set_CONTROL(ctrl.w);
418
419 return TFM_HAL_SUCCESS;
420 }
421
tfm_hal_memory_check(uintptr_t boundary,uintptr_t base,size_t size,uint32_t access_type)422 enum tfm_hal_status_t tfm_hal_memory_check(uintptr_t boundary, uintptr_t base,
423 size_t size, uint32_t access_type)
424 {
425 int flags = 0;
426
427 /* If size is zero, this indicates an empty buffer and base is ignored */
428 if (size == 0) {
429 return TFM_HAL_SUCCESS;
430 }
431
432 if (!base) {
433 return TFM_HAL_ERROR_INVALID_INPUT;
434 }
435
436 if ((access_type & TFM_HAL_ACCESS_READWRITE) == TFM_HAL_ACCESS_READWRITE) {
437 flags |= CMSE_MPU_READWRITE;
438 } else if (access_type & TFM_HAL_ACCESS_READABLE) {
439 flags |= CMSE_MPU_READ;
440 } else {
441 return TFM_HAL_ERROR_INVALID_INPUT;
442 }
443
444 if (access_type & TFM_HAL_ACCESS_NS) {
445 flags |= CMSE_NONSECURE;
446 }
447
448 if (!((uint32_t)boundary & HANDLE_ATTR_PRIV_MASK)) {
449 flags |= CMSE_MPU_UNPRIV;
450 }
451
452 /* This check is only done for ns_agent_tz */
453 if ((uint32_t)boundary & HANDLE_ATTR_NS_MASK) {
454 CONTROL_Type ctrl;
455 ctrl.w = __TZ_get_CONTROL_NS();
456 if (ctrl.b.nPRIV == 1) {
457 flags |= CMSE_MPU_UNPRIV;
458 } else {
459 flags &= ~CMSE_MPU_UNPRIV;
460 }
461 flags |= CMSE_NONSECURE;
462 }
463
464 if (cmse_check_address_range((void *)base, size, flags) != NULL) {
465 return TFM_HAL_SUCCESS;
466 } else {
467 return TFM_HAL_ERROR_MEM_FAULT;
468 }
469 }
470
tfm_hal_boundary_need_switch(uintptr_t boundary_from,uintptr_t boundary_to)471 bool tfm_hal_boundary_need_switch(uintptr_t boundary_from,
472 uintptr_t boundary_to)
473 {
474 if (boundary_from == boundary_to) {
475 return false;
476 }
477
478 if (((uint32_t)boundary_from & HANDLE_ATTR_PRIV_MASK) &&
479 ((uint32_t)boundary_to & HANDLE_ATTR_PRIV_MASK)) {
480 return false;
481 }
482 return true;
483 }
484