1 /*
2  * Copyright (c) 2020-2024, Arm Limited. All rights reserved.
3  * Copyright (c) 2022 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 #include <arm_cmse.h>
12 #include <stddef.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include "array.h"
16 #include "tfm_hal_device_header.h"
17 #include "Driver_Common.h"
18 #include "mmio_defs.h"
19 #include "mpu_armv8m_drv.h"
20 #include "region.h"
21 #include "target_cfg.h"
22 #include "tfm_hal_defs.h"
23 #include "tfm_hal_isolation.h"
24 #include "tfm_peripherals_def.h"
25 #include "load/partition_defs.h"
26 #include "load/asset_defs.h"
27 #include "load/spm_load_api.h"
28 
29 /* It can be retrieved from the MPU_TYPE register. */
30 #define MPU_REGION_NUM                  8
31 #define PROT_BOUNDARY_VAL \
32     ((1U << HANDLE_ATTR_PRIV_POS) & HANDLE_ATTR_PRIV_MASK)
33 
34 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
35 static uint32_t n_configured_regions = 0;
36 struct mpu_armv8m_dev_t dev_mpu_s = { MPU_BASE };
37 
38 REGION_DECLARE(Image$$, ER_VENEER, $$Base);
39 REGION_DECLARE(Image$$, VENEER_ALIGN, $$Limit);
40 REGION_DECLARE(Image$$, TFM_UNPRIV_CODE_START, $$RO$$Base);
41 REGION_DECLARE(Image$$, TFM_UNPRIV_CODE_END, $$RO$$Limit);
42 REGION_DECLARE(Image$$, TFM_APP_CODE_START, $$Base);
43 REGION_DECLARE(Image$$, TFM_APP_CODE_END, $$Base);
44 REGION_DECLARE(Image$$, TFM_APP_RW_STACK_START, $$Base);
45 REGION_DECLARE(Image$$, TFM_APP_RW_STACK_END, $$Base);
46 #ifdef CONFIG_TFM_PARTITION_META
47 REGION_DECLARE(Image$$, TFM_SP_META_PTR, $$ZI$$Base);
48 REGION_DECLARE(Image$$, TFM_SP_META_PTR_END, $$ZI$$Limit);
49 #endif /* CONFIG_TFM_PARTITION_META */
50 
51 const struct mpu_armv8m_region_cfg_t region_cfg[] = {
52     /* Veneer region */
53     {
54         0, /* will be updated before using */
55         (uint32_t)&REGION_NAME(Image$$, ER_VENEER, $$Base),
56         (uint32_t)&REGION_NAME(Image$$, VENEER_ALIGN, $$Limit) - 1,
57         MPU_ARMV8M_MAIR_ATTR_CODE_IDX,
58         MPU_ARMV8M_XN_EXEC_OK,
59         MPU_ARMV8M_AP_RO_PRIV_UNPRIV,
60         MPU_ARMV8M_SH_NONE
61     },
62     /* TFM Core unprivileged code region */
63     {
64         0, /* will be updated before using */
65         (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_CODE_START, $$RO$$Base),
66         (uint32_t)&REGION_NAME(Image$$, TFM_UNPRIV_CODE_END, $$RO$$Limit) - 1,
67         MPU_ARMV8M_MAIR_ATTR_CODE_IDX,
68         MPU_ARMV8M_XN_EXEC_OK,
69         MPU_ARMV8M_AP_RO_PRIV_UNPRIV,
70         MPU_ARMV8M_SH_NONE
71     },
72     /* RO region */
73     {
74         0, /* will be updated before using */
75         (uint32_t)&REGION_NAME(Image$$, TFM_APP_CODE_START, $$Base),
76         (uint32_t)&REGION_NAME(Image$$, TFM_APP_CODE_END, $$Base) - 1,
77         MPU_ARMV8M_MAIR_ATTR_CODE_IDX,
78         MPU_ARMV8M_XN_EXEC_OK,
79         MPU_ARMV8M_AP_RO_PRIV_UNPRIV,
80         MPU_ARMV8M_SH_NONE
81     },
82     /* RW, ZI and stack as one region */
83     {
84         0, /* will be updated before using */
85         (uint32_t)&REGION_NAME(Image$$, TFM_APP_RW_STACK_START, $$Base),
86         (uint32_t)&REGION_NAME(Image$$, TFM_APP_RW_STACK_END, $$Base) - 1,
87         MPU_ARMV8M_MAIR_ATTR_DATA_IDX,
88         MPU_ARMV8M_XN_EXEC_NEVER,
89         MPU_ARMV8M_AP_RW_PRIV_UNPRIV,
90         MPU_ARMV8M_SH_NONE
91     },
92 #ifdef CONFIG_TFM_PARTITION_META
93     /* TFM partition metadata pointer region */
94     {
95         0, /* will be updated before using */
96         (uint32_t)&REGION_NAME(Image$$, TFM_SP_META_PTR, $$ZI$$Base),
97         (uint32_t)&REGION_NAME(Image$$, TFM_SP_META_PTR_END, $$ZI$$Limit) - 1,
98         MPU_ARMV8M_MAIR_ATTR_DATA_IDX,
99         MPU_ARMV8M_XN_EXEC_NEVER,
100         MPU_ARMV8M_AP_RW_PRIV_UNPRIV,
101         MPU_ARMV8M_SH_NONE
102     }
103 #endif
104 };
105 #endif /* CONFIG_TFM_ENABLE_MEMORY_PROTECT */
106 
tfm_hal_set_up_static_boundaries(uintptr_t * p_spm_boundary)107 enum tfm_hal_status_t tfm_hal_set_up_static_boundaries(
108                                             uintptr_t *p_spm_boundary)
109 {
110 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
111     struct mpu_armv8m_region_cfg_t localcfg;
112 #endif
113     /* Set up isolation boundaries between SPE and NSPE */
114     sau_and_idau_cfg();
115     if (mpc_init_cfg() != ARM_DRIVER_OK) {
116         return TFM_HAL_ERROR_GENERIC;
117     }
118     ppc_init_cfg();
119 
120     /* Set up static isolation boundaries inside SPE */
121 #ifdef CONFIG_TFM_ENABLE_MEMORY_PROTECT
122     int32_t i;
123 
124     mpu_armv8m_clean(&dev_mpu_s);
125 
126     if (ARRAY_SIZE(region_cfg) > MPU_REGION_NUM) {
127         return TFM_HAL_ERROR_GENERIC;
128     }
129     for (i = 0; i < ARRAY_SIZE(region_cfg); i++) {
130         memcpy(&localcfg, &region_cfg[i], sizeof(localcfg));
131         localcfg.region_nr = i;
132         if (mpu_armv8m_region_enable(&dev_mpu_s,
133             (struct mpu_armv8m_region_cfg_t *)&localcfg)
134             != MPU_ARMV8M_OK) {
135             return TFM_HAL_ERROR_GENERIC;
136         }
137     }
138     n_configured_regions = i;
139 
140     mpu_armv8m_enable(&dev_mpu_s, PRIVILEGED_DEFAULT_ENABLE,
141                       HARDFAULT_NMI_ENABLE);
142 #endif /* CONFIG_TFM_ENABLE_MEMORY_PROTECT */
143 
144     *p_spm_boundary = (uintptr_t)PROT_BOUNDARY_VAL;
145 
146     return TFM_HAL_SUCCESS;
147 }
148 
149 /*
150  * Implementation of tfm_hal_bind_boundary() on AN519:
151  *
152  * The API encodes some attributes into a handle and returns it to SPM.
153  * The attributes include isolation boundaries, privilege, and MMIO information.
154  * When scheduler switches running partitions, SPM compares the handle between
155  * partitions to know if boundary update is necessary. If update is required,
156  * SPM passes the handle to platform to do platform settings and update
157  * isolation boundaries.
158  */
tfm_hal_bind_boundary(const struct partition_load_info_t * p_ldinf,uintptr_t * p_boundary)159 enum tfm_hal_status_t tfm_hal_bind_boundary(
160                                     const struct partition_load_info_t *p_ldinf,
161                                     uintptr_t *p_boundary)
162 {
163     uint32_t i, j;
164     bool privileged;
165     bool ns_agent;
166     uint32_t partition_attrs = 0;
167     const struct asset_desc_t *p_asset;
168     struct platform_data_t *plat_data_ptr;
169 #if TFM_ISOLATION_LEVEL == 2
170     struct mpu_armv8m_region_cfg_t localcfg;
171 #endif
172     if (!p_ldinf || !p_boundary) {
173         return TFM_HAL_ERROR_GENERIC;
174     }
175 
176 #if TFM_ISOLATION_LEVEL == 1
177     privileged = true;
178 #else
179     privileged = IS_PSA_ROT(p_ldinf);
180 #endif
181 
182     ns_agent = IS_NS_AGENT(p_ldinf);
183     p_asset = LOAD_INFO_ASSET(p_ldinf);
184 
185     /*
186      * Validate if the named MMIO of partition is allowed by the platform.
187      * Otherwise, skip validation.
188      *
189      * NOTE: Need to add validation of numbered MMIO if platform requires.
190      */
191     for (i = 0; i < p_ldinf->nassets; i++) {
192         if (!(p_asset[i].attr & ASSET_ATTR_NAMED_MMIO)) {
193             continue;
194         }
195         for (j = 0; j < ARRAY_SIZE(partition_named_mmio_list); j++) {
196             if (p_asset[i].dev.dev_ref == partition_named_mmio_list[j]) {
197                 break;
198             }
199         }
200 
201         if (j == ARRAY_SIZE(partition_named_mmio_list)) {
202             /* The MMIO asset is not in the allowed list of platform. */
203             return TFM_HAL_ERROR_GENERIC;
204         }
205         /* Assume PPC & MPC settings are required even under level 1 */
206         plat_data_ptr = REFERENCE_TO_PTR(p_asset[i].dev.dev_ref,
207                                          struct platform_data_t *);
208 
209         if (plat_data_ptr->periph_ppc_bank != PPC_SP_DO_NOT_CONFIGURE) {
210             ppc_configure_to_secure(plat_data_ptr->periph_ppc_bank,
211                                     plat_data_ptr->periph_ppc_loc);
212             if (privileged) {
213                 ppc_clr_secure_unpriv(plat_data_ptr->periph_ppc_bank,
214                                       plat_data_ptr->periph_ppc_loc);
215             } else {
216                 ppc_en_secure_unpriv(plat_data_ptr->periph_ppc_bank,
217                                      plat_data_ptr->periph_ppc_loc);
218             }
219         }
220 #if TFM_ISOLATION_LEVEL == 2
221         /*
222          * Static boundaries are set. Set up MPU region for MMIO.
223          * Setup regions for unprivileged assets only.
224          */
225         if (!privileged) {
226             localcfg.region_base = plat_data_ptr->periph_start;
227             localcfg.region_limit = plat_data_ptr->periph_limit;
228             localcfg.region_attridx = MPU_ARMV8M_MAIR_ATTR_DEVICE_IDX;
229             localcfg.attr_access = MPU_ARMV8M_AP_RW_PRIV_UNPRIV;
230             localcfg.attr_sh = MPU_ARMV8M_SH_NONE;
231             localcfg.attr_exec = MPU_ARMV8M_XN_EXEC_NEVER;
232             localcfg.region_nr = n_configured_regions++;
233 
234             if (mpu_armv8m_region_enable(&dev_mpu_s, &localcfg)
235                 != MPU_ARMV8M_OK) {
236                 return TFM_HAL_ERROR_GENERIC;
237             }
238         }
239 #endif
240     }
241 
242     partition_attrs = ((uint32_t)privileged << HANDLE_ATTR_PRIV_POS) &
243                         HANDLE_ATTR_PRIV_MASK;
244     partition_attrs |= ((uint32_t)ns_agent << HANDLE_ATTR_NS_POS) &
245                         HANDLE_ATTR_NS_MASK;
246     *p_boundary = (uintptr_t)partition_attrs;
247 
248     return TFM_HAL_SUCCESS;
249 }
250 
tfm_hal_activate_boundary(const struct partition_load_info_t * p_ldinf,uintptr_t boundary)251 enum tfm_hal_status_t tfm_hal_activate_boundary(
252                              const struct partition_load_info_t *p_ldinf,
253                              uintptr_t boundary)
254 {
255     CONTROL_Type ctrl;
256     bool privileged = !!((uint32_t)boundary & HANDLE_ATTR_PRIV_MASK);
257 
258     /* Privileged level is required to be set always */
259     ctrl.w = __get_CONTROL();
260     ctrl.b.nPRIV = privileged ? 0 : 1;
261     __set_CONTROL(ctrl.w);
262 
263     return TFM_HAL_SUCCESS;
264 }
265 
tfm_hal_memory_check(uintptr_t boundary,uintptr_t base,size_t size,uint32_t access_type)266 enum tfm_hal_status_t tfm_hal_memory_check(uintptr_t boundary, uintptr_t base,
267                                            size_t size, uint32_t access_type)
268 {
269     int flags = 0;
270 
271     /* If size is zero, this indicates an empty buffer and base is ignored */
272     if (size == 0) {
273         return TFM_HAL_SUCCESS;
274     }
275 
276     if (!base) {
277         return TFM_HAL_ERROR_INVALID_INPUT;
278     }
279 
280     if ((access_type & TFM_HAL_ACCESS_READWRITE) == TFM_HAL_ACCESS_READWRITE) {
281         flags |= CMSE_MPU_READWRITE;
282     } else if (access_type & TFM_HAL_ACCESS_READABLE) {
283         flags |= CMSE_MPU_READ;
284     } else {
285         return TFM_HAL_ERROR_INVALID_INPUT;
286     }
287 
288     if (!((uint32_t)boundary & HANDLE_ATTR_PRIV_MASK)) {
289         flags |= CMSE_MPU_UNPRIV;
290     }
291 
292     if ((uint32_t)boundary & HANDLE_ATTR_NS_MASK) {
293         CONTROL_Type ctrl;
294         ctrl.w = __TZ_get_CONTROL_NS();
295         if (ctrl.b.nPRIV == 1) {
296             flags |= CMSE_MPU_UNPRIV;
297         } else {
298             flags &= ~CMSE_MPU_UNPRIV;
299         }
300         flags |= CMSE_NONSECURE;
301     }
302 
303     if (cmse_check_address_range((void *)base, size, flags) != NULL) {
304         return TFM_HAL_SUCCESS;
305     } else {
306         return TFM_HAL_ERROR_MEM_FAULT;
307     }
308 }
309 
tfm_hal_boundary_need_switch(uintptr_t boundary_from,uintptr_t boundary_to)310 bool tfm_hal_boundary_need_switch(uintptr_t boundary_from,
311                                   uintptr_t boundary_to)
312 {
313     if (boundary_from == boundary_to) {
314         return false;
315     }
316 
317     if (((uint32_t)boundary_from & HANDLE_ATTR_PRIV_MASK) &&
318         ((uint32_t)boundary_to & HANDLE_ATTR_PRIV_MASK)) {
319         return false;
320     }
321     return true;
322 }
323