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)&REGION_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)&REGION_NAME(Image$$, VENEER_ALIGN, $$Limit) - 1,
94                          ARM_MPU_PRIVILEGE_EXECUTE_OK,
95                          0)
96         #else
97         ARM_MPU_RLAR((uint32_t)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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)&REGION_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