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)®ION_NAME(Image$$, ER_VENEER, $$Base),
56 (uint32_t)®ION_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)®ION_NAME(Image$$, TFM_UNPRIV_CODE_START, $$RO$$Base),
66 (uint32_t)®ION_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)®ION_NAME(Image$$, TFM_APP_CODE_START, $$Base),
76 (uint32_t)®ION_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)®ION_NAME(Image$$, TFM_APP_RW_STACK_START, $$Base),
86 (uint32_t)®ION_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)®ION_NAME(Image$$, TFM_SP_META_PTR, $$ZI$$Base),
97 (uint32_t)®ION_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, ®ion_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