1 /*
2 * Copyright (c) 2019-2022 Cypress Semiconductor Corporation (an Infineon
3 * company) or an affiliate of Cypress Semiconductor Corporation. All rights
4 * reserved.
5 * Copyright (c) 2021, Arm Limited. All rights reserved.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 #include <string.h>
20
21 #include "driver_smpu.h"
22
23 #include "internal_status_code.h"
24 #include "flash_layout.h"
25 #include "nv_counters.h"
26 #include "pc_config.h"
27 #include "region_defs.h"
28 #include "RTE_Device.h"
29 #include "smpu_config.h"
30 #include "tfm_spm_log.h"
31 #include "tfm_hal_its.h"
32 #ifdef TFM_PARTITION_PROTECTED_STORAGE
33 #include "tfm_hal_ps.h"
34 #endif
35 #include "tfm_multi_core.h"
36
37 #include "cy_prot.h"
38
39 /* Affect all 8 subregions */
40 #define ALL_ENABLED 0
41 #define SMPU_NAME_MAX_SIZE 18
42
43 struct smpu_resources {
44 PROT_SMPU_SMPU_STRUCT_Type *smpu;
45 cy_stc_smpu_cfg_t slave_config;
46 cy_stc_smpu_cfg_t master_config;
47 };
48
smpu_name(const SMPU_Resources * smpu_dev)49 static const char * smpu_name(const SMPU_Resources *smpu_dev)
50 {
51 switch ((int)smpu_dev->smpu) {
52 case (int)PROT_SMPU_SMPU_STRUCT0:
53 return "SMPU 0";
54 case (int)PROT_SMPU_SMPU_STRUCT1:
55 return "SMPU 1";
56 case (int)PROT_SMPU_SMPU_STRUCT2:
57 return "SMPU 2";
58 case (int)PROT_SMPU_SMPU_STRUCT3:
59 return "SMPU 3";
60 case (int)PROT_SMPU_SMPU_STRUCT4:
61 return "SMPU 4";
62 case (int)PROT_SMPU_SMPU_STRUCT5:
63 return "SMPU 5";
64 case (int)PROT_SMPU_SMPU_STRUCT6:
65 return "SMPU 6";
66 case (int)PROT_SMPU_SMPU_STRUCT7:
67 return "SMPU 7";
68 case (int)PROT_SMPU_SMPU_STRUCT8:
69 return "SMPU 8";
70 case (int)PROT_SMPU_SMPU_STRUCT9:
71 return "SMPU 9";
72 case (int)PROT_SMPU_SMPU_STRUCT10:
73 return "SMPU 10";
74 case (int)PROT_SMPU_SMPU_STRUCT11:
75 return "SMPU 11";
76 case (int)PROT_SMPU_SMPU_STRUCT12:
77 return "SMPU 12";
78 case (int)PROT_SMPU_SMPU_STRUCT13:
79 return "SMPU 13";
80 case (int)PROT_SMPU_SMPU_STRUCT14:
81 return "SMPU 14";
82 case (int)PROT_SMPU_SMPU_STRUCT15:
83 return "SMPU 15";
84 default:
85 return "Unrecognised SMPU";
86 }
87 }
88
is_runtime(const SMPU_Resources * smpu_dev)89 static bool is_runtime(const SMPU_Resources *smpu_dev)
90 {
91 if ((smpu_dev->slave_config.address == SMPU_DYNAMIC_BASE) &&
92 (smpu_dev->slave_config.regionSize == SMPU_DYNAMIC_REGIONSIZE) &&
93 (smpu_dev->slave_config.subregions == SMPU_DYNAMIC_SUBREGIONS))
94 return true;
95 return false;
96 }
97
is_whole_power_of_two(size_t size)98 static bool is_whole_power_of_two(size_t size)
99 {
100 return ((size - 1) & size) == 0;
101 }
102
is_aligned(uint32_t base,size_t size)103 static bool is_aligned(uint32_t base, size_t size)
104 {
105 return (base % size) == 0;
106 }
107
108 /* size must be a whole power of two, >= 4 */
bytes_to_regionsize(size_t size)109 static cy_en_prot_size_t bytes_to_regionsize(size_t size)
110 {
111 int ret = 1;
112
113 while (REGIONSIZE_TO_BYTES(ret) < size)
114 ret += 1;
115 return (cy_en_prot_size_t)ret;
116 }
117
round_up_to_power_of_two(uint32_t x)118 static uint32_t round_up_to_power_of_two(uint32_t x)
119 {
120 x--;
121 x |= x >> 1;
122 x |= x >> 2;
123 x |= x >> 4;
124 x |= x >> 8;
125 x |= x >> 16;
126 x++;
127 return x;
128 }
129
round_down_to_power_of_two(uint32_t x)130 static uint32_t round_down_to_power_of_two(uint32_t x)
131 {
132 x |= x >> 1;
133 x |= x >> 2;
134 x |= x >> 4;
135 x |= x >> 8;
136 x |= x >> 16;
137 x = x - (x >> 1);
138 return x;
139 }
140
round_down_to_multiple(uint32_t base,uint32_t regionSize)141 static uint32_t round_down_to_multiple(uint32_t base, uint32_t regionSize)
142 {
143 return (regionSize * (base / regionSize));
144 }
145
146 /* Maps 0..7 to CY_PROT_SUBREGION_DIS0..CY_PROT_SUBREGION_DIS7 */
subregion_num_to_mask(int num)147 static uint32_t subregion_num_to_mask(int num) {
148 return CY_PROT_SUBREGION_DIS0 << num;
149 }
150
151 #define MIN_REGIONSIZE 256
152 /* The largest regionSize is actually 4GB, but that doesn't fit in a uint32_t */
153 #define MAX_REGIONSIZE (1 * 1024 * 1024 * 1024)
154 #define NUM_SUBREGIONS 8
155
calc_smpu_params(uint32_t base,size_t size,cy_stc_smpu_cfg_t * slave_config)156 static cy_en_prot_status_t calc_smpu_params(uint32_t base,
157 size_t size,
158 cy_stc_smpu_cfg_t *slave_config)
159 {
160 /* Simplest case - base is a multiple of the size,
161 * and size is a whole power of two, and >= 256
162 */
163 if (is_whole_power_of_two(size) &&
164 is_aligned(base, size) &&
165 (size >= MIN_REGIONSIZE)) {
166 slave_config->address = (void *)base;
167 slave_config->regionSize = bytes_to_regionsize(size);
168 slave_config->subregions = ALL_ENABLED;
169
170 return CY_PROT_SUCCESS;
171 } else {
172 /* Try to find a regionSize that could work */
173 uint32_t low_rs = round_up_to_power_of_two(size);
174 uint32_t high_rs = round_down_to_power_of_two(size * NUM_SUBREGIONS);
175 uint32_t regionSize;
176
177 if (low_rs < MIN_REGIONSIZE)
178 low_rs = MIN_REGIONSIZE;
179
180 if (size > MAX_REGIONSIZE / NUM_SUBREGIONS)
181 high_rs = MAX_REGIONSIZE;
182
183 for (regionSize = low_rs; regionSize <= high_rs; regionSize <<= 1)
184 {
185 const uint32_t sub_size = regionSize / NUM_SUBREGIONS;
186 uint32_t address;
187 int n;
188 uint32_t mask = 0;
189
190 /* Would this work with the base address ? */
191 if (base % sub_size) {
192 /* TODO If we get here, are we guaranteed to fail ? */
193 continue;
194 }
195
196 /* is the size 1..8 * subregion size ? */
197 for (n = 1; n <= NUM_SUBREGIONS; n++) {
198 if (size == n * sub_size) {
199 break;
200 }
201 }
202 if (n > NUM_SUBREGIONS) {
203 continue;
204 }
205
206 /* What would the base address be? */
207 address = round_down_to_multiple(base, regionSize);
208
209 /* Would the upper limit of the SMPU region cover what we need? */
210 if (address + regionSize < base + size) {
211 continue;
212 }
213
214 /* Calculate the SMPU config */
215 slave_config->address = (void *)address;
216 slave_config->regionSize = bytes_to_regionsize(regionSize);
217
218 /* Figure out which subregions to disable */
219 for (int num = 0; num < NUM_SUBREGIONS; num += 1) {
220 if (address + num * sub_size < base) {
221 /* Disable this subregion */
222 mask |= subregion_num_to_mask(num);
223 } else if (address + num * sub_size >= base + size) {
224 /* Disable this subregion */
225 mask |= subregion_num_to_mask(num);
226 }
227 }
228 slave_config->subregions = mask;
229
230 return CY_PROT_SUCCESS;
231 }
232
233 /* If we get here, we failed */
234 return CY_PROT_FAILURE;
235 }
236 }
237
get_region(const PROT_SMPU_SMPU_STRUCT_Type * smpu,uint32_t * base,size_t * size)238 static cy_en_prot_status_t get_region(const PROT_SMPU_SMPU_STRUCT_Type *smpu,
239 uint32_t *base, size_t *size)
240 {
241 cy_en_prot_status_t ret = CY_PROT_SUCCESS;
242
243 /* Figure out the base, size, and subregion mask to use */
244 if (smpu == ITS_SMPU_STRUCT) {
245 struct tfm_hal_its_fs_info_t its_fs_info;
246 /* Retrieve the ITS region definition */
247 tfm_hal_its_fs_info(&its_fs_info);
248 *base = its_fs_info.flash_area_addr;
249 *size = its_fs_info.flash_area_size;
250 } else if (smpu == NVC_SMPU_STRUCT) {
251 /* Retrieve the OTP / NV area info */
252 *base = FLASH_OTP_NV_COUNTERS_AREA_OFFSET;
253 *size = FLASH_OTP_NV_COUNTERS_AREA_SIZE;
254 #ifdef TFM_PARTITION_PROTECTED_STORAGE
255 } else if (smpu == PS_SMPU_STRUCT) {
256 struct tfm_hal_ps_fs_info_t ps_fs_info;
257 /* Retrieve the PS region definition */
258 tfm_hal_ps_fs_info(&ps_fs_info);
259 *base = ps_fs_info.flash_area_addr;
260 *size = ps_fs_info.flash_area_size;
261 #endif
262 } else {
263 /* We don't know where to get the region definition */
264 ret = CY_PROT_FAILURE;
265 }
266 /* flash driver uses offsets rather than absolute addresses,
267 * so we need to add the flash base address here.
268 */
269 *base += FLASH_BASE_ADDRESS;
270
271 return ret;
272 }
273
populate_region(const PROT_SMPU_SMPU_STRUCT_Type * smpu,cy_stc_smpu_cfg_t * slave_config)274 static cy_en_prot_status_t populate_region(const PROT_SMPU_SMPU_STRUCT_Type *smpu,
275 cy_stc_smpu_cfg_t *slave_config)
276 {
277 cy_en_prot_status_t ret;
278 uint32_t base;
279 size_t size;
280
281 ret = get_region(smpu, &base, &size);
282
283 if (ret == CY_PROT_SUCCESS) {
284 /* And figure out how to configure the SMPU region */
285 ret = calc_smpu_params(base, size, slave_config);
286 }
287
288 return ret;
289 }
290
print_smpu_config(const cy_stc_smpu_cfg_t * slave_config)291 static void print_smpu_config(const cy_stc_smpu_cfg_t *slave_config)
292 {
293 SPMLOG_INFMSGVAL(" Address = ", (uintptr_t)slave_config->address);
294 SPMLOG_INFMSGVAL(" Size (bytes) = ",
295 REGIONSIZE_TO_BYTES(slave_config->regionSize));
296 if (slave_config->subregions == ALL_ENABLED) {
297 SPMLOG_INFMSG(" All subregions enabled\r\n");
298 } else {
299 SPMLOG_INFMSGVAL("\tsubregion size (bytes) = ",
300 REGIONSIZE_TO_BYTES(slave_config->regionSize)/8);
301 for (int i=0; i<8; i++) {
302 if (slave_config->subregions & (1<<i)) {
303 SPMLOG_INFMSGVAL("\tDisabled subregion ", i);
304 } else {
305 SPMLOG_INFMSGVAL("\tEnabled subregion ", i);
306 }
307 }
308 }
309 }
310
SMPU_Read_Region(const PROT_SMPU_SMPU_STRUCT_Type * smpu,uint32_t * address,uint32_t * size,uint32_t * subregions,uint32_t * att0_reg)311 static cy_en_prot_status_t SMPU_Read_Region(const PROT_SMPU_SMPU_STRUCT_Type *smpu,
312 uint32_t *address,
313 uint32_t *size,
314 uint32_t *subregions,
315 uint32_t *att0_reg)
316 {
317 uint32_t reg = smpu->ATT0;
318
319 /* Return a copy of ATT0 if requested */
320 if (att0_reg) {
321 *att0_reg = reg;
322 }
323
324 if (!_FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_ENABLED, reg)) {
325 /* Disabled SMPU */
326 return CY_PROT_FAILURE;
327 }
328
329 *size = REGIONSIZE_TO_BYTES(_FLD2VAL(PROT_SMPU_SMPU_STRUCT_ATT0_REGION_SIZE, reg));
330 reg = smpu->ADDR0;
331 *subregions = _FLD2VAL(PROT_SMPU_SMPU_STRUCT_ADDR0_SUBREGION_DISABLE, reg);
332 *address = _FLD2VAL(PROT_SMPU_SMPU_STRUCT_ADDR0_ADDR24, reg) << 8;
333 /* Some address bits may be ignored */
334 *address -= *address % *size;
335 return CY_PROT_SUCCESS;
336 }
337
dump_smpu(const PROT_SMPU_SMPU_STRUCT_Type * smpu)338 static void dump_smpu(const PROT_SMPU_SMPU_STRUCT_Type *smpu)
339 {
340 uint32_t base;
341 size_t size;
342 uint32_t size32;
343 uint32_t subregions;
344
345 if (CY_PROT_SUCCESS == get_region(smpu, &base, &size)) {
346 SPMLOG_INFMSGVAL(" Wanted address = ", base);
347 SPMLOG_INFMSGVAL(" Wanted size (bytes) = ", size);
348 } else {
349 SPMLOG_ERRMSG(" Unsupported dynamic SMPU region\r\n");
350 }
351
352 if (SMPU_Read_Region(smpu, &base, &size32, &subregions, NULL) == CY_PROT_SUCCESS) {
353 SPMLOG_INFMSGVAL(" Configured address = ", base);
354 SPMLOG_INFMSGVAL(" Configured size (bytes) = ", size32);
355
356 if (subregions == ALL_ENABLED) {
357 SPMLOG_INFMSG(" All subregions enabled\r\n");
358 } else {
359 SPMLOG_INFMSGVAL("\tsubregion size (bytes) = ", size32/8);
360 for (int i=0; i<8; i++) {
361 if (subregions & (1<<i)) {
362 SPMLOG_INFMSGVAL("\tDisabled subregion ", i);
363 } else {
364 SPMLOG_INFMSGVAL("\tEnabled subregion ", i);
365 }
366 }
367 }
368 } else {
369 SPMLOG_ERRMSG("SMPU slave is disabled\r\n");
370 }
371 }
372
373 /*
374 * For the given Protection Context, check whether the specified
375 * SMPU controls access to the specified memory range for the specified PC.
376 * If so, set *p_attr accordingly.
377 */
SMPU_Covers_Region(const void * p,size_t s,uint32_t pc,PROT_SMPU_SMPU_STRUCT_Type * smpu,struct mem_attr_info_t * p_attr)378 static bool SMPU_Covers_Region(const void *p, size_t s,
379 uint32_t pc,
380 PROT_SMPU_SMPU_STRUCT_Type *smpu,
381 struct mem_attr_info_t *p_attr)
382 {
383 bool pc_mismatch = false;
384 uint32_t address;
385 uint32_t size;
386 uint32_t subregions;
387 uint32_t att0;
388 cy_en_prot_status_t status = SMPU_Read_Region(smpu,
389 &address,
390 &size,
391 &subregions,
392 &att0);
393 if (status != CY_PROT_SUCCESS) {
394 /* Disabled SMPU */
395 return false;
396 }
397
398 /* Check PC */
399 if ((_FLD2VAL(PROT_SMPU_SMPU_STRUCT_ATT0_PC_MASK_15_TO_1, att0)
400 & (1 << (pc - 1))) == 0) {
401 /* pc does not match the mask */
402 if (_FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_PC_MATCH, att0)) {
403 /* This SMPU neither allows nor denies access for this PC */
404 return false;
405 }
406 /* In this case we need to check whether the address range is covered */
407 pc_mismatch = true;
408 }
409
410 /* And the address range */
411 if (check_address_range(p, s, address, size) == SPM_SUCCESS) {
412 if (pc_mismatch) {
413 /* Access denied - PC doesn't match */
414 p_attr->is_mpu_enabled = true;
415 p_attr->is_valid = true;
416 p_attr->is_xn = true;
417 p_attr->is_priv_rd_allow = false;
418 p_attr->is_priv_wr_allow = false;
419 p_attr->is_unpriv_rd_allow = false;
420 p_attr->is_unpriv_wr_allow = false;
421 } else {
422 p_attr->is_mpu_enabled = true;
423 p_attr->is_valid = true;
424 /* SMPU has separate PX and UX bits. Here we ignore UX */
425 p_attr->is_xn = !_FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_PX, att0);
426 p_attr->is_priv_rd_allow = _FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_PR, att0);
427 p_attr->is_priv_wr_allow = _FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_PW, att0);
428 p_attr->is_unpriv_rd_allow = _FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_UR, att0);
429 p_attr->is_unpriv_wr_allow = _FLD2BOOL(PROT_SMPU_SMPU_STRUCT_ATT0_UW, att0);
430 }
431 return true;
432 }
433
434 return false;
435 }
436
437 /* API functions */
438
SMPU_Get_Access_Rules(const void * p,size_t s,uint32_t pc,struct mem_attr_info_t * p_attr)439 void SMPU_Get_Access_Rules(const void *p, size_t s,
440 uint32_t pc,
441 struct mem_attr_info_t *p_attr)
442 {
443 /* Higher-numbered SMPUs have priority */
444 for (int i = CPUSS_PROT_SMPU_STRUCT_NR; i > 0; i--) {
445 if (SMPU_Covers_Region(p, s, pc, &PROT->SMPU.SMPU_STRUCT[i - 1], p_attr)) {
446 /* It's covered by this SMPU */
447 return;
448 }
449 }
450
451 /* no SMPU covers it */
452 p_attr->is_mpu_enabled = false;
453 p_attr->is_valid = true;
454 p_attr->is_xn = true;
455 p_attr->is_priv_rd_allow = true;
456 p_attr->is_priv_wr_allow = true;
457 p_attr->is_unpriv_rd_allow = true;
458 p_attr->is_unpriv_wr_allow = true;
459 }
460
SMPU_Print_Config(const SMPU_Resources * smpu_dev)461 void SMPU_Print_Config(const SMPU_Resources *smpu_dev)
462 {
463 char smpu_str[SMPU_NAME_MAX_SIZE] = {0};
464
465 strcpy(smpu_str, smpu_name(smpu_dev));
466 SPMLOG_INFMSG(smpu_str);
467 if (is_runtime(smpu_dev)) {
468 SPMLOG_INFMSG(" - configured algorithmically.\r\n");
469
470 dump_smpu(smpu_dev->smpu);
471 } else {
472 SPMLOG_INFMSG(" - configured at compile time.\r\n");
473
474 print_smpu_config(&smpu_dev->slave_config);
475 }
476 }
477
SMPU_Configure(const SMPU_Resources * smpu_dev)478 cy_en_prot_status_t SMPU_Configure(const SMPU_Resources *smpu_dev)
479 {
480 cy_en_prot_status_t ret;
481
482 if (is_runtime(smpu_dev)) {
483 cy_stc_smpu_cfg_t slave_config;
484
485 /* Start with a verbatim copy of the slave config */
486 memcpy(&slave_config, &smpu_dev->slave_config, sizeof(slave_config));
487
488 ret = populate_region(smpu_dev->smpu, &slave_config);
489 if (ret != CY_PROT_SUCCESS) {
490 return ret;
491 }
492
493 ret = Cy_Prot_ConfigSmpuSlaveStruct(smpu_dev->smpu,
494 &slave_config);
495 } else {
496 /* Use the slave config verbatim */
497 ret = Cy_Prot_ConfigSmpuSlaveStruct(smpu_dev->smpu,
498 &smpu_dev->slave_config);
499 }
500
501 if (ret != CY_PROT_SUCCESS) {
502 return ret;
503 }
504 ret = Cy_Prot_ConfigSmpuMasterStruct(smpu_dev->smpu,
505 &smpu_dev->master_config);
506 if (ret != CY_PROT_SUCCESS) {
507 return ret;
508 }
509 ret = Cy_Prot_EnableSmpuSlaveStruct(smpu_dev->smpu);
510 if (ret != CY_PROT_SUCCESS) {
511 return ret;
512 }
513 ret = Cy_Prot_EnableSmpuMasterStruct(smpu_dev->smpu);
514 return ret;
515 }
516
517 /* Only allow privileged secure PC=1 bus masters to change unconfigured SMPUs */
protect_unconfigured_smpus(void)518 cy_en_prot_status_t protect_unconfigured_smpus(void)
519 {
520 const cy_stc_smpu_cfg_t smpu_config = COMMON_SMPU_MASTER_CONFIG;
521 cy_en_prot_status_t ret = CY_PROT_SUCCESS;
522 int i;
523 uint32_t att0, att1;
524
525 for (i = 0; i < CPUSS_PROT_SMPU_STRUCT_NR; i++) {
526 att0 = PROT->SMPU.SMPU_STRUCT[i].ATT0;
527 att1 = PROT->SMPU.SMPU_STRUCT[i].ATT1;
528
529 if ((_FLD2VAL(PROT_SMPU_SMPU_STRUCT_ATT0_ENABLED, att0) == 0)
530 && (_FLD2VAL(PROT_SMPU_SMPU_STRUCT_ATT1_ENABLED, att1) == 0)) {
531
532 ret = Cy_Prot_ConfigSmpuMasterStruct(&PROT->SMPU.SMPU_STRUCT[i],
533 &smpu_config);
534 if (ret != CY_PROT_SUCCESS) {
535 break;
536 }
537 ret = Cy_Prot_EnableSmpuMasterStruct(&PROT->SMPU.SMPU_STRUCT[i]);
538 if (ret != CY_PROT_SUCCESS) {
539 break;
540 }
541 }
542 }
543
544 return ret;
545 }
546
547 /* Exported per-SMPU macros */
548 #define DEFINE_SMPU(N) const SMPU_Resources SMPU##N##_Resources = { \
549 .smpu = PROT_SMPU_SMPU_STRUCT##N, \
550 .slave_config = SMPU##N##_SLAVE_CONFIG, \
551 .master_config = SMPU##N##_MASTER_CONFIG, \
552 }; \
553
554 #if (RTE_SMPU0)
555 DEFINE_SMPU(0)
556 #endif /* RTE_SMPU0 */
557
558 #if (RTE_SMPU1)
559 DEFINE_SMPU(1)
560 #endif /* RTE_SMPU1 */
561
562 #if (RTE_SMPU2)
563 DEFINE_SMPU(2)
564 #endif /* RTE_SMPU2 */
565
566 #if (RTE_SMPU3)
567 DEFINE_SMPU(3)
568 #endif /* RTE_SMPU3 */
569
570 #if (RTE_SMPU4)
571 DEFINE_SMPU(4)
572 #endif /* RTE_SMPU4 */
573
574 #if (RTE_SMPU5)
575 DEFINE_SMPU(5)
576 #endif /* RTE_SMPU5 */
577
578 #if (RTE_SMPU6)
579 DEFINE_SMPU(6)
580 #endif /* RTE_SMPU6 */
581
582 #if (RTE_SMPU7)
583 DEFINE_SMPU(7)
584 #endif /* RTE_SMPU7 */
585
586 #if (RTE_SMPU8)
587 DEFINE_SMPU(8)
588 #endif /* RTE_SMPU8 */
589
590 #if (RTE_SMPU9)
591 DEFINE_SMPU(9)
592 #endif /* RTE_SMPU9 */
593
594 #if (RTE_SMPU10)
595 DEFINE_SMPU(10)
596 #endif /* RTE_SMPU10 */
597
598 #if (RTE_SMPU11)
599 DEFINE_SMPU(11)
600 #endif /* RTE_SMPU11 */
601
602 #if (RTE_SMPU12)
603 DEFINE_SMPU(12)
604 #endif /* RTE_SMPU12 */
605
606 #if (RTE_SMPU13)
607 DEFINE_SMPU(13)
608 #endif /* RTE_SMPU13 */
609
610 /* Note that SMPUs 14 and 15 are fixed by romboot */
611