Per Åstrand | 79929ff | 2021-01-26 14:42:43 +0100 | [diff] [blame] | 1 | /* |
Anton Moberg | fa3e51b | 2021-03-31 11:05:02 +0200 | [diff] [blame^] | 2 | * Copyright (c) 2016-2021 Arm Limited. All rights reserved. |
Per Åstrand | 79929ff | 2021-01-26 14:42:43 +0100 | [diff] [blame] | 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | #include "mpc_sie_drv.h" |
| 17 | |
| 18 | #include <stddef.h> |
| 19 | #include <stdbool.h> |
| 20 | |
| 21 | #include "cmsis_compiler.h" |
| 22 | |
| 23 | /* Values for hardware version in PIDR0 reg */ |
| 24 | #define SIE200 0x60 |
| 25 | #define SIE300 0x65 |
| 26 | |
| 27 | #define MPC_SIE_BLK_CFG_OFFSET 5U |
| 28 | |
| 29 | /* Defines with numbering (eg: SIE300) are only relevant to the given SIE |
| 30 | * version. Defines without the numbering are applicable to all SIE versions. |
| 31 | */ |
| 32 | |
| 33 | /* CTRL register bit indexes */ |
| 34 | #define MPC_SIE200_CTRL_SEC_RESP (1UL << 4UL) /* MPC fault triggers a |
| 35 | * bus error |
| 36 | */ |
| 37 | #define MPC_SIE300_CTRL_GATE_REQ (1UL << 6UL) /* Request for gating |
| 38 | * incoming transfers |
| 39 | */ |
| 40 | #define MPC_SIE300_CTRL_GATE_ACK (1UL << 7UL) /* Acknowledge for gating |
| 41 | * incoming transfers |
| 42 | */ |
| 43 | #define MPC_SIE_CTRL_AUTOINCREMENT (1UL << 8UL) /* BLK_IDX auto increment */ |
| 44 | #define MPC_SIE300_CTRL_SEC_RESP (1UL << 16UL) /* Response type when SW |
| 45 | * asks to gate the transfer |
| 46 | */ |
| 47 | #define MPC_SIE300_CTRL_GATE_PRESENT (1UL << 23UL) /* Gating feature present */ |
| 48 | #define MPC_SIE_CTRL_SEC_LOCK_DOWN (1UL << 31UL) /* MPC Security lock down */ |
| 49 | |
| 50 | /* PIDR register bit masks */ |
| 51 | #define MPC_PIDR0_SIE_VERSION_MASK ((1UL << 8UL) - 1UL) |
| 52 | |
| 53 | /* ARM MPC interrupt */ |
| 54 | #define MPC_SIE_INT_BIT (1UL) |
| 55 | |
| 56 | /* Error code returned by the internal driver functions */ |
| 57 | enum mpc_sie_intern_error_t { |
| 58 | MPC_SIE_INTERN_ERR_NONE = MPC_SIE_ERR_NONE, |
| 59 | MPC_SIE_INTERN_ERR_NOT_IN_RANGE = MPC_SIE_ERR_NOT_IN_RANGE, |
| 60 | MPC_SIE_INTERN_ERR_NOT_ALIGNED = MPC_SIE_ERR_NOT_ALIGNED, |
| 61 | MPC_SIE_INTERN_ERR_INVALID_RANGE = MPC_SIE_ERR_INVALID_RANGE, |
| 62 | MPC_INTERN_ERR_RANGE_SEC_ATTR_NON_COMPATIBLE = |
| 63 | MPC_SIE_ERR_RANGE_SEC_ATTR_NON_COMPATIBLE, |
| 64 | /* Calculated block index |
| 65 | * is higher than the maximum allowed by the MPC. It should never |
| 66 | * happen unless the controlled ranges of the MPC are misconfigured |
| 67 | * in the driver or if the IP has not enough LUTs to cover the |
| 68 | * range, due to wrong reported block size for example. |
| 69 | */ |
| 70 | MPC_SIE_INTERN_ERR_BLK_IDX_TOO_HIGH = -1, |
| 71 | |
| 72 | }; |
| 73 | |
| 74 | /* ARM MPC memory mapped register access structure */ |
| 75 | struct mpc_sie_reg_map_t { |
| 76 | volatile uint32_t ctrl; /* (R/W) MPC Control */ |
| 77 | volatile uint32_t reserved[3];/* Reserved */ |
| 78 | volatile uint32_t blk_max; /* (R/ ) Maximum value of block based index */ |
| 79 | volatile uint32_t blk_cfg; /* (R/ ) Block configuration */ |
| 80 | volatile uint32_t blk_idx; /* (R/W) Index value for accessing block |
| 81 | * based look up table |
| 82 | */ |
| 83 | volatile uint32_t blk_lutn; /* (R/W) Block based gating |
| 84 | * Look Up Table (LUT) |
| 85 | */ |
| 86 | volatile uint32_t int_stat; /* (R/ ) Interrupt state */ |
| 87 | volatile uint32_t int_clear; /* ( /W) Interrupt clear */ |
| 88 | volatile uint32_t int_en; /* (R/W) Interrupt enable */ |
| 89 | volatile uint32_t int_info1; /* (R/ ) Interrupt information 1 */ |
| 90 | volatile uint32_t int_info2; /* (R/ ) Interrupt information 2 */ |
| 91 | volatile uint32_t int_set; /* ( /W) Interrupt set. Debug purpose only */ |
| 92 | volatile uint32_t reserved2[998]; /* Reserved */ |
| 93 | volatile uint32_t pidr4; /* (R/ ) Peripheral ID 4 */ |
| 94 | volatile uint32_t pidr5; /* (R/ ) Peripheral ID 5 */ |
| 95 | volatile uint32_t pidr6; /* (R/ ) Peripheral ID 6 */ |
| 96 | volatile uint32_t pidr7; /* (R/ ) Peripheral ID 7 */ |
| 97 | volatile uint32_t pidr0; /* (R/ ) Peripheral ID 0 */ |
| 98 | volatile uint32_t pidr1; /* (R/ ) Peripheral ID 1 */ |
| 99 | volatile uint32_t pidr2; /* (R/ ) Peripheral ID 2 */ |
| 100 | volatile uint32_t pidr3; /* (R/ ) Peripheral ID 3 */ |
| 101 | volatile uint32_t cidr0; /* (R/ ) Component ID 0 */ |
| 102 | volatile uint32_t cidr1; /* (R/ ) Component ID 1 */ |
| 103 | volatile uint32_t cidr2; /* (R/ ) Component ID 2 */ |
| 104 | volatile uint32_t cidr3; /* (R/ ) Component ID 3 */ |
| 105 | }; |
| 106 | |
| 107 | /* |
| 108 | * Checks if the address is controlled by the MPC and returns |
| 109 | * the range index in which it is contained. |
| 110 | * |
| 111 | * \param[in] dev MPC device to initialize \ref mpc_sie_dev_t |
| 112 | * \param[in] addr Address to check if it is controlled by MPC. |
| 113 | * \param[out] addr_range Range index in which it is contained. |
| 114 | * |
| 115 | * \return True if the base is controller by the range list, false otherwise. |
| 116 | */ |
| 117 | static uint32_t is_ctrl_by_range_list( |
| 118 | struct mpc_sie_dev_t* dev, |
| 119 | uint32_t addr, |
| 120 | const struct mpc_sie_memory_range_t** addr_range) |
| 121 | { |
| 122 | uint32_t i; |
| 123 | const struct mpc_sie_memory_range_t* range; |
| 124 | |
| 125 | for(i = 0; i < dev->data->nbr_of_ranges; i++) { |
| 126 | range = dev->data->range_list[i]; |
| 127 | if(addr >= range->base && addr <= range->limit) { |
| 128 | *addr_range = range; |
| 129 | return 1; |
| 130 | } |
| 131 | } |
| 132 | return 0; |
| 133 | } |
| 134 | |
| 135 | /* |
| 136 | * Gets the masks selecting the bits in the LUT of the MPC corresponding |
| 137 | * to the base address (included) up to the limit address (included) |
| 138 | * |
| 139 | * \param[in] mpc_dev The MPC device. |
| 140 | * \param[in] base Address in a range controlled by this MPC |
| 141 | * (included), aligned on block size. |
| 142 | * \param[in] limit Address in a range controlled by this MPC |
| 143 | * (included), aligned on block size. |
| 144 | * \param[out] range Memory range in which the base address and |
| 145 | * limit are. |
| 146 | * \param[out] first_word_idx Index of the first touched word in the LUT. |
| 147 | * \param[out] nr_words Number of words used in the LUT. If 1, only |
| 148 | * first_word_mask is valid and limit_word_mask |
| 149 | * must not be used. |
| 150 | * \param[out] first_word_mask First word mask in the LUT will be stored here. |
| 151 | * \param[out] limit_word_mask Limit word mask in the LUT will be stored here. |
| 152 | * |
| 153 | * \return Returns error code as specified in \ref mpc_sie_intern_error_t |
| 154 | */ |
| 155 | static enum mpc_sie_intern_error_t get_lut_masks( |
| 156 | struct mpc_sie_dev_t* dev, |
| 157 | const uint32_t base, const uint32_t limit, |
| 158 | const struct mpc_sie_memory_range_t** range, |
| 159 | uint32_t *first_word_idx, |
| 160 | uint32_t *nr_words, |
| 161 | uint32_t *first_word_mask, |
| 162 | uint32_t *limit_word_mask) |
| 163 | { |
| 164 | const struct mpc_sie_memory_range_t* base_range; |
| 165 | uint32_t block_size; |
| 166 | uint32_t base_block_idx; |
| 167 | uint32_t base_word_idx; |
| 168 | uint32_t blk_max; |
| 169 | const struct mpc_sie_memory_range_t* limit_range; |
| 170 | uint32_t limit_block_idx; |
| 171 | uint32_t limit_word_idx; |
| 172 | uint32_t mask; |
| 173 | uint32_t norm_base; |
| 174 | uint32_t norm_limit; |
| 175 | struct mpc_sie_reg_map_t* p_mpc = |
| 176 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 177 | |
| 178 | /* |
| 179 | * Check that the addresses are within the controlled regions |
| 180 | * of this MPC |
| 181 | */ |
| 182 | if(!is_ctrl_by_range_list(dev, base, &base_range) || |
| 183 | !is_ctrl_by_range_list(dev, limit, &limit_range)) { |
| 184 | return MPC_SIE_INTERN_ERR_NOT_IN_RANGE; |
| 185 | } |
| 186 | |
| 187 | /* Base and limit should be part of the same range */ |
| 188 | if(base_range != limit_range) { |
| 189 | return MPC_SIE_INTERN_ERR_INVALID_RANGE; |
| 190 | } |
| 191 | *range = base_range; |
| 192 | |
| 193 | block_size = (1 << (p_mpc->blk_cfg + MPC_SIE_BLK_CFG_OFFSET)); |
| 194 | |
| 195 | /* Base and limit+1 addresses must be aligned on the MPC block size */ |
| 196 | if(base % block_size || (limit+1) % block_size) { |
| 197 | return MPC_SIE_INTERN_ERR_NOT_ALIGNED; |
| 198 | } |
| 199 | |
| 200 | /* |
| 201 | * Get a normalized address that is an offset from the beginning |
| 202 | * of the lowest range controlled by the MPC |
| 203 | */ |
| 204 | norm_base = (base - base_range->base) + base_range->range_offset; |
| 205 | norm_limit = (limit - base_range->base) + base_range->range_offset; |
| 206 | |
| 207 | /* |
| 208 | * Calculate block index and to which 32 bits word it belongs |
| 209 | */ |
| 210 | limit_block_idx = norm_limit/block_size; |
| 211 | limit_word_idx = limit_block_idx/32; |
| 212 | |
| 213 | base_block_idx = norm_base/block_size; |
| 214 | base_word_idx = base_block_idx/32; |
| 215 | |
| 216 | if(base_block_idx > limit_block_idx) { |
| 217 | return MPC_SIE_INTERN_ERR_INVALID_RANGE; |
| 218 | } |
| 219 | |
| 220 | /* Transmit the information to the caller */ |
| 221 | *nr_words = limit_word_idx - base_word_idx + 1; |
| 222 | *first_word_idx = base_word_idx; |
| 223 | |
| 224 | /* Limit to the highest block that can be configured */ |
| 225 | blk_max = p_mpc->blk_max; |
| 226 | |
| 227 | if((limit_word_idx > blk_max) || (base_word_idx > blk_max)) { |
| 228 | return MPC_SIE_INTERN_ERR_BLK_IDX_TOO_HIGH; |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | * Create the mask for the first word to only select the limit N bits |
| 233 | */ |
| 234 | *first_word_mask = ~((1 << (base_block_idx % 32)) - 1); |
| 235 | |
| 236 | /* |
| 237 | * Create the mask for the limit word to select only the first M bits. |
| 238 | */ |
| 239 | *limit_word_mask = (1 << ((limit_block_idx+1) % 32)) - 1; |
| 240 | /* |
| 241 | * If limit_word_mask is 0, it means that the limit touched block index is |
| 242 | * the limit in its word, so the limit word mask has all its bits selected |
| 243 | */ |
| 244 | if(*limit_word_mask == 0) { |
| 245 | *limit_word_mask = 0xFFFFFFFF; |
| 246 | } |
| 247 | |
| 248 | /* |
| 249 | * If the blocks to configure are all packed in one word, only |
| 250 | * touch this word. |
| 251 | * Code using the computed masks should test if this mask |
| 252 | * is non-zero, and if so, only use this one instead of the limit_word_mask |
| 253 | * and first_word_mask. |
| 254 | * As the only bits that are the same in both masks are the 1 that we want |
| 255 | * to select, just use XOR to extract them. |
| 256 | */ |
| 257 | if(base_word_idx == limit_word_idx) { |
| 258 | mask = ~(*first_word_mask ^ *limit_word_mask); |
| 259 | *first_word_mask = mask; |
| 260 | *limit_word_mask = mask; |
| 261 | } |
| 262 | |
| 263 | return MPC_SIE_INTERN_ERR_NONE; |
| 264 | } |
| 265 | |
| 266 | enum mpc_sie_error_t mpc_sie_init(struct mpc_sie_dev_t* dev, |
| 267 | const struct mpc_sie_memory_range_t** range_list, |
| 268 | uint8_t nbr_of_ranges) |
| 269 | { |
| 270 | if((range_list == NULL) || (nbr_of_ranges == 0)) { |
| 271 | return MPC_SIE_INVALID_ARG; |
| 272 | } |
| 273 | |
| 274 | dev->data->sie_version = get_sie_version(dev); |
| 275 | |
| 276 | if ((dev->data->sie_version != SIE200) && |
| 277 | (dev->data->sie_version != SIE300)) { |
| 278 | return MPC_SIE_UNSUPPORTED_HARDWARE_VERSION; |
| 279 | } |
| 280 | |
| 281 | dev->data->range_list = range_list; |
| 282 | dev->data->nbr_of_ranges = nbr_of_ranges; |
| 283 | dev->data->is_initialized = true; |
| 284 | |
| 285 | return MPC_SIE_ERR_NONE; |
| 286 | } |
| 287 | |
| 288 | enum mpc_sie_error_t mpc_sie_get_block_size(struct mpc_sie_dev_t* dev, |
| 289 | uint32_t* blk_size) |
| 290 | { |
| 291 | struct mpc_sie_reg_map_t* p_mpc = |
| 292 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 293 | |
| 294 | if(dev->data->is_initialized != true) { |
| 295 | return MPC_SIE_NOT_INIT; |
| 296 | } |
| 297 | |
| 298 | if(blk_size == 0) { |
| 299 | return MPC_SIE_INVALID_ARG; |
| 300 | } |
| 301 | |
| 302 | /* Calculate the block size in byte according to the manual */ |
| 303 | *blk_size = (1 << (p_mpc->blk_cfg + MPC_SIE_BLK_CFG_OFFSET)); |
| 304 | |
| 305 | return MPC_SIE_ERR_NONE; |
| 306 | } |
| 307 | |
| 308 | enum mpc_sie_error_t mpc_sie_config_region(struct mpc_sie_dev_t* dev, |
| 309 | const uint32_t base, |
| 310 | const uint32_t limit, |
| 311 | enum mpc_sie_sec_attr_t attr) |
| 312 | { |
| 313 | enum mpc_sie_intern_error_t error; |
| 314 | uint32_t first_word_idx; |
| 315 | uint32_t first_word_mask; |
| 316 | uint32_t i; |
| 317 | uint32_t limit_word_mask; |
| 318 | uint32_t limit_word_idx; |
| 319 | uint32_t nr_words; |
| 320 | const struct mpc_sie_memory_range_t* range; |
| 321 | uint32_t word_value; |
| 322 | struct mpc_sie_reg_map_t* p_mpc = |
| 323 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 324 | |
| 325 | if(dev->data->is_initialized != true) { |
| 326 | return MPC_SIE_NOT_INIT; |
| 327 | } |
| 328 | |
| 329 | /* Get the bitmasks used to select the bits in the LUT */ |
| 330 | error = get_lut_masks(dev, base, limit, &range, &first_word_idx, &nr_words, |
| 331 | &first_word_mask, &limit_word_mask); |
| 332 | |
| 333 | limit_word_idx = first_word_idx + nr_words - 1; |
| 334 | |
| 335 | if(error != MPC_SIE_INTERN_ERR_NONE) { |
| 336 | /* Map internal error code lower than 0 to a generic errpr */ |
| 337 | if(error < 0) { |
| 338 | return MPC_SIE_ERR_INVALID_RANGE; |
| 339 | } |
| 340 | return (enum mpc_sie_error_t)error; |
| 341 | } |
| 342 | |
| 343 | /* |
| 344 | * The memory range should allow accesses in with the wanted security |
| 345 | * attribute if it requires special attribute for successful accesses |
| 346 | */ |
| 347 | if(range->attr != attr) { |
| 348 | return MPC_SIE_ERR_RANGE_SEC_ATTR_NON_COMPATIBLE; |
| 349 | } |
| 350 | |
| 351 | /* |
| 352 | * Starts changing actual configuration so issue DMB to ensure every |
| 353 | * transaction has completed by now |
| 354 | */ |
| 355 | __DMB(); |
| 356 | |
| 357 | /* Set the block index to the first word that will be updated */ |
| 358 | p_mpc->blk_idx = first_word_idx; |
| 359 | |
| 360 | /* If only one word needs to be touched in the LUT */ |
| 361 | if(nr_words == 1) { |
| 362 | word_value = p_mpc->blk_lutn; |
| 363 | if(attr == MPC_SIE_SEC_ATTR_NONSECURE) { |
| 364 | word_value |= first_word_mask; |
| 365 | } else { |
| 366 | word_value &= ~first_word_mask; |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | * Set the index again because full word read or write could have |
| 371 | * incremented it |
| 372 | */ |
| 373 | p_mpc->blk_idx = first_word_idx; |
| 374 | p_mpc->blk_lutn = word_value; |
| 375 | |
| 376 | /* Commit the configuration change */ |
| 377 | __DSB(); |
| 378 | __ISB(); |
| 379 | |
| 380 | return MPC_SIE_ERR_NONE; |
| 381 | } |
| 382 | |
| 383 | /* First word */ |
| 384 | word_value = p_mpc->blk_lutn; |
| 385 | if(attr == MPC_SIE_SEC_ATTR_NONSECURE) { |
| 386 | word_value |= first_word_mask; |
| 387 | } else { |
| 388 | word_value &= ~first_word_mask; |
| 389 | } |
| 390 | /* |
| 391 | * Set the index again because full word read or write could have |
| 392 | * incremented it |
| 393 | */ |
| 394 | p_mpc->blk_idx = first_word_idx; |
| 395 | /* Partially configure the first word */ |
| 396 | p_mpc->blk_lutn = word_value; |
| 397 | |
| 398 | /* Fully configure the intermediate words if there are any */ |
| 399 | for(i=first_word_idx+1; i<limit_word_idx; i++) { |
| 400 | p_mpc->blk_idx = i; |
| 401 | if(attr == MPC_SIE_SEC_ATTR_NONSECURE) { |
| 402 | p_mpc->blk_lutn = 0xFFFFFFFF; |
| 403 | } else { |
| 404 | p_mpc->blk_lutn = 0x00000000; |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | /* Partially configure the limit word */ |
| 409 | p_mpc->blk_idx = limit_word_idx; |
| 410 | word_value = p_mpc->blk_lutn; |
| 411 | if(attr == MPC_SIE_SEC_ATTR_NONSECURE) { |
| 412 | word_value |= limit_word_mask; |
| 413 | } else { |
| 414 | word_value &= ~limit_word_mask; |
| 415 | } |
| 416 | p_mpc->blk_idx = limit_word_idx; |
| 417 | p_mpc->blk_lutn = word_value; |
| 418 | |
| 419 | /* Commit the configuration change */ |
| 420 | __DSB(); |
| 421 | __ISB(); |
| 422 | |
| 423 | return MPC_SIE_ERR_NONE; |
| 424 | } |
| 425 | |
| 426 | enum mpc_sie_error_t mpc_sie_get_region_config( |
| 427 | struct mpc_sie_dev_t* dev, |
| 428 | uint32_t base, uint32_t limit, |
| 429 | enum mpc_sie_sec_attr_t* attr) |
| 430 | { |
| 431 | enum mpc_sie_sec_attr_t attr_prev; |
| 432 | uint32_t block_size; |
| 433 | uint32_t block_size_mask; |
| 434 | enum mpc_sie_intern_error_t error; |
| 435 | uint32_t first_word_idx; |
| 436 | uint32_t first_word_mask; |
| 437 | uint32_t i; |
| 438 | uint32_t limit_word_idx; |
| 439 | uint32_t limit_word_mask; |
| 440 | uint32_t nr_words; |
| 441 | struct mpc_sie_reg_map_t* p_mpc = |
| 442 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 443 | const struct mpc_sie_memory_range_t* range; |
| 444 | uint32_t word_value; |
| 445 | |
| 446 | if(dev->data->is_initialized != true) { |
| 447 | return MPC_SIE_NOT_INIT; |
| 448 | } |
| 449 | |
| 450 | if(attr == 0) { |
| 451 | return MPC_SIE_INVALID_ARG; |
| 452 | } |
| 453 | |
| 454 | /* |
| 455 | * Initialize the security attribute to mixed in case of early |
| 456 | * termination of this function. A caller that does not check the |
| 457 | * returned error will act as if it does not know anything about the |
| 458 | * region queried, which is the safest bet |
| 459 | */ |
| 460 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 461 | |
| 462 | /* |
| 463 | * If the base and limit are not aligned, align them and make sure |
| 464 | * that the resulting region fully includes the original region |
| 465 | */ |
| 466 | block_size = (1 << (p_mpc->blk_cfg + MPC_SIE_BLK_CFG_OFFSET)); |
| 467 | |
| 468 | block_size_mask = block_size - 1; |
| 469 | base &= ~(block_size_mask); |
| 470 | limit &= ~(block_size_mask); |
| 471 | limit += block_size - 1; /* Round to the upper block address, |
| 472 | * and then remove one to get the preceding |
| 473 | * address. |
| 474 | */ |
| 475 | |
| 476 | /* Get the bitmasks used to select the bits in the LUT */ |
| 477 | error = get_lut_masks(dev, base, limit, &range, &first_word_idx, &nr_words, |
| 478 | &first_word_mask, &limit_word_mask); |
| 479 | |
| 480 | limit_word_idx = first_word_idx+nr_words - 1; |
| 481 | |
| 482 | if(error != MPC_SIE_INTERN_ERR_NONE) { |
| 483 | /* Map internal error code lower than 0 to generic error */ |
| 484 | if(error < 0) { |
| 485 | return MPC_SIE_ERR_INVALID_RANGE; |
| 486 | } |
| 487 | return (enum mpc_sie_error_t)error; |
| 488 | } |
| 489 | |
| 490 | /* Set the block index to the first word that will be updated */ |
| 491 | p_mpc->blk_idx = first_word_idx; |
| 492 | |
| 493 | /* If only one word needs to be touched in the LUT */ |
| 494 | if(nr_words == 1) { |
| 495 | word_value = p_mpc->blk_lutn; |
| 496 | word_value &= first_word_mask; |
| 497 | if(word_value == 0) { |
| 498 | *attr = MPC_SIE_SEC_ATTR_SECURE; |
| 499 | /* |
| 500 | * If there are differences between the mask and the word value, |
| 501 | * it means that the security attributes of blocks are mixed |
| 502 | */ |
| 503 | } else if(word_value ^ first_word_mask) { |
| 504 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 505 | } else { |
| 506 | *attr = MPC_SIE_SEC_ATTR_NONSECURE; |
| 507 | } |
| 508 | return MPC_SIE_ERR_NONE; |
| 509 | } |
| 510 | |
| 511 | /* Get the partial configuration of the first word */ |
| 512 | word_value = p_mpc->blk_lutn & first_word_mask; |
| 513 | if(word_value == 0x00000000) { |
| 514 | *attr = MPC_SIE_SEC_ATTR_SECURE; |
| 515 | } else if(word_value ^ first_word_mask) { |
| 516 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 517 | /* |
| 518 | * Bail out as the security attribute will be the same regardless |
| 519 | * of the configuration of other blocks |
| 520 | */ |
| 521 | return MPC_SIE_ERR_NONE; |
| 522 | } else { |
| 523 | *attr = MPC_SIE_SEC_ATTR_NONSECURE; |
| 524 | } |
| 525 | /* |
| 526 | * Store the current found attribute, to check that all the blocks indeed |
| 527 | * have the same security attribute. |
| 528 | */ |
| 529 | attr_prev = *attr; |
| 530 | |
| 531 | /* Get the configuration of the intermediate words if there are any */ |
| 532 | for(i=first_word_idx+1; i<limit_word_idx; i++) { |
| 533 | p_mpc->blk_idx = i; |
| 534 | word_value = p_mpc->blk_lutn; |
| 535 | if(word_value == 0x00000000) { |
| 536 | *attr = MPC_SIE_SEC_ATTR_SECURE; |
| 537 | } else if(word_value == 0xFFFFFFFF) { |
| 538 | *attr = MPC_SIE_SEC_ATTR_NONSECURE; |
| 539 | } else { |
| 540 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 541 | return MPC_SIE_ERR_NONE; |
| 542 | } |
| 543 | |
| 544 | /* If the attribute is different than the one found before, bail out */ |
| 545 | if(*attr != attr_prev) { |
| 546 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 547 | return MPC_SIE_ERR_NONE; |
| 548 | } |
| 549 | attr_prev = *attr; |
| 550 | } |
| 551 | |
| 552 | /* Get the partial configuration of the limit word */ |
| 553 | p_mpc->blk_idx = limit_word_idx; |
| 554 | word_value = p_mpc->blk_lutn & limit_word_mask; |
| 555 | if(word_value == 0x00000000) { |
| 556 | *attr = MPC_SIE_SEC_ATTR_SECURE; |
| 557 | } else if(word_value ^ first_word_mask) { |
| 558 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 559 | return MPC_SIE_ERR_NONE; |
| 560 | } else { |
| 561 | *attr = MPC_SIE_SEC_ATTR_NONSECURE; |
| 562 | } |
| 563 | |
| 564 | if(*attr != attr_prev) { |
| 565 | *attr = MPC_SIE_SEC_ATTR_MIXED; |
| 566 | return MPC_SIE_ERR_NONE; |
| 567 | } |
| 568 | |
| 569 | return MPC_SIE_ERR_NONE; |
| 570 | } |
| 571 | |
| 572 | enum mpc_sie_error_t mpc_sie_get_ctrl(struct mpc_sie_dev_t* dev, |
| 573 | uint32_t* ctrl_val) |
| 574 | { |
| 575 | struct mpc_sie_reg_map_t* p_mpc = |
| 576 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 577 | |
| 578 | if(dev->data->is_initialized != true) { |
| 579 | return MPC_SIE_NOT_INIT; |
| 580 | } |
| 581 | |
| 582 | if(ctrl_val == 0) { |
| 583 | return MPC_SIE_INVALID_ARG; |
| 584 | } |
| 585 | |
| 586 | *ctrl_val = p_mpc->ctrl; |
| 587 | |
| 588 | return MPC_SIE_ERR_NONE; |
| 589 | } |
| 590 | |
| 591 | enum mpc_sie_error_t mpc_sie_set_ctrl(struct mpc_sie_dev_t* dev, |
| 592 | uint32_t mpc_ctrl) |
| 593 | { |
| 594 | struct mpc_sie_reg_map_t* p_mpc = |
| 595 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 596 | |
| 597 | if(dev->data->is_initialized != true) { |
| 598 | return MPC_SIE_NOT_INIT; |
| 599 | } |
| 600 | |
| 601 | p_mpc->ctrl = mpc_ctrl; |
| 602 | |
| 603 | return MPC_SIE_ERR_NONE; |
| 604 | } |
| 605 | |
| 606 | enum mpc_sie_error_t mpc_sie_get_sec_resp(struct mpc_sie_dev_t* dev, |
| 607 | enum mpc_sie_sec_resp_t* sec_rep) |
| 608 | { |
| 609 | struct mpc_sie_reg_map_t* p_mpc = |
| 610 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 611 | bool gating_present = false; |
| 612 | |
| 613 | if(dev->data->is_initialized != true) { |
| 614 | return MPC_SIE_NOT_INIT; |
| 615 | } |
| 616 | |
| 617 | if(sec_rep == NULL) { |
| 618 | return MPC_SIE_INVALID_ARG; |
| 619 | } |
| 620 | |
| 621 | if (dev->data->sie_version == SIE200) { |
| 622 | if(p_mpc->ctrl & MPC_SIE200_CTRL_SEC_RESP) { |
| 623 | *sec_rep = MPC_SIE_RESP_BUS_ERROR; |
| 624 | } else { |
| 625 | *sec_rep = MPC_SIE_RESP_RAZ_WI; |
| 626 | } |
| 627 | |
| 628 | } else if (dev->data->sie_version == SIE300) { |
| 629 | mpc_sie_is_gating_present(dev, &gating_present); |
| 630 | if (!gating_present) { |
| 631 | return MPC_SIE_ERR_GATING_NOT_PRESENT; |
| 632 | } |
| 633 | |
| 634 | if(p_mpc->ctrl & MPC_SIE300_CTRL_SEC_RESP) { |
| 635 | /* MPC returns a BUS ERROR response */ |
| 636 | *sec_rep = MPC_SIE_RESP_BUS_ERROR; |
| 637 | } else { |
| 638 | /* MPC sets the ready signals LOW, which stalls any transactions */ |
| 639 | *sec_rep = MPC_SIE_RESP_WAIT_GATING_DISABLED; |
| 640 | } |
| 641 | } else { |
| 642 | return MPC_SIE_UNSUPPORTED_HARDWARE_VERSION; |
| 643 | } |
| 644 | |
| 645 | return MPC_SIE_ERR_NONE; |
| 646 | } |
| 647 | |
| 648 | enum mpc_sie_error_t mpc_sie_set_sec_resp(struct mpc_sie_dev_t* dev, |
| 649 | enum mpc_sie_sec_resp_t sec_rep) |
| 650 | { |
| 651 | struct mpc_sie_reg_map_t* p_mpc = |
| 652 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 653 | bool gating_present = false; |
| 654 | |
| 655 | if(dev->data->is_initialized != true) { |
| 656 | return MPC_SIE_NOT_INIT; |
| 657 | } |
| 658 | |
| 659 | if (dev->data->sie_version == SIE200) { |
| 660 | if (sec_rep == MPC_SIE_RESP_BUS_ERROR) { |
| 661 | p_mpc->ctrl |= MPC_SIE200_CTRL_SEC_RESP; |
| 662 | } else if (sec_rep == MPC_SIE_RESP_RAZ_WI) { |
| 663 | p_mpc->ctrl &= ~MPC_SIE200_CTRL_SEC_RESP; |
| 664 | } else { |
| 665 | return MPC_SIE_INVALID_ARG; |
| 666 | } |
| 667 | |
| 668 | } else if (dev->data->sie_version == SIE300) { |
| 669 | mpc_sie_is_gating_present(dev, &gating_present); |
| 670 | if (!gating_present) { |
| 671 | return MPC_SIE_ERR_GATING_NOT_PRESENT; |
| 672 | } |
| 673 | |
| 674 | if (sec_rep == MPC_SIE_RESP_BUS_ERROR) { |
| 675 | p_mpc->ctrl |= MPC_SIE300_CTRL_SEC_RESP; |
| 676 | } else if (sec_rep == MPC_SIE_RESP_WAIT_GATING_DISABLED) { |
| 677 | p_mpc->ctrl &= ~MPC_SIE300_CTRL_SEC_RESP; |
| 678 | } else { |
| 679 | return MPC_SIE_INVALID_ARG; |
| 680 | } |
| 681 | |
| 682 | } else { |
| 683 | return MPC_SIE_UNSUPPORTED_HARDWARE_VERSION; |
| 684 | } |
| 685 | |
| 686 | return MPC_SIE_ERR_NONE; |
| 687 | } |
| 688 | |
| 689 | enum mpc_sie_error_t mpc_sie_irq_enable(struct mpc_sie_dev_t* dev) |
| 690 | { |
| 691 | struct mpc_sie_reg_map_t* p_mpc = |
| 692 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 693 | |
| 694 | if(dev->data->is_initialized != true) { |
| 695 | return MPC_SIE_NOT_INIT; |
| 696 | } |
| 697 | |
| 698 | p_mpc->int_en |= MPC_SIE_INT_BIT; |
| 699 | |
| 700 | return MPC_SIE_ERR_NONE; |
| 701 | } |
| 702 | |
| 703 | void mpc_sie_irq_disable(struct mpc_sie_dev_t* dev) |
| 704 | { |
| 705 | struct mpc_sie_reg_map_t* p_mpc = |
| 706 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 707 | |
| 708 | p_mpc->int_en &= ~MPC_SIE_INT_BIT; |
| 709 | } |
| 710 | |
| 711 | void mpc_sie_clear_irq(struct mpc_sie_dev_t* dev) |
| 712 | { |
| 713 | struct mpc_sie_reg_map_t* p_mpc = |
| 714 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 715 | |
| 716 | p_mpc->int_clear = MPC_SIE_INT_BIT; |
| 717 | } |
| 718 | |
| 719 | uint32_t mpc_sie_irq_state(struct mpc_sie_dev_t* dev) |
| 720 | { |
| 721 | struct mpc_sie_reg_map_t* p_mpc = |
| 722 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 723 | |
| 724 | return (p_mpc->int_stat & MPC_SIE_INT_BIT); |
| 725 | } |
| 726 | |
| 727 | enum mpc_sie_error_t mpc_sie_lock_down(struct mpc_sie_dev_t* dev) |
| 728 | { |
| 729 | struct mpc_sie_reg_map_t* p_mpc = |
| 730 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 731 | |
| 732 | if(dev->data->is_initialized != true) { |
| 733 | return MPC_SIE_NOT_INIT; |
| 734 | } |
| 735 | |
| 736 | p_mpc->ctrl |= (MPC_SIE_CTRL_AUTOINCREMENT |
| 737 | | MPC_SIE_CTRL_SEC_LOCK_DOWN); |
| 738 | |
| 739 | return MPC_SIE_ERR_NONE; |
| 740 | } |
| 741 | |
| 742 | enum mpc_sie_error_t mpc_sie_is_gating_present(struct mpc_sie_dev_t* dev, |
| 743 | bool* gating_present) |
| 744 | { |
| 745 | struct mpc_sie_reg_map_t* p_mpc = |
| 746 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 747 | |
| 748 | if(dev->data->is_initialized != true) { |
| 749 | return MPC_SIE_NOT_INIT; |
| 750 | } |
| 751 | |
| 752 | if (dev->data->sie_version != SIE300) { |
| 753 | return MPC_SIE_UNSUPPORTED_HARDWARE_VERSION; |
| 754 | } |
| 755 | |
| 756 | *gating_present = (bool)(p_mpc->ctrl & MPC_SIE300_CTRL_GATE_PRESENT); |
| 757 | |
| 758 | return MPC_SIE_ERR_NONE; |
| 759 | } |
| 760 | |
| 761 | uint32_t get_sie_version(struct mpc_sie_dev_t* dev) |
| 762 | { |
| 763 | struct mpc_sie_reg_map_t* p_mpc = |
| 764 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 765 | |
| 766 | return p_mpc->pidr0 & MPC_PIDR0_SIE_VERSION_MASK; |
| 767 | } |
| 768 | |
| 769 | bool mpc_sie_get_gate_ack(struct mpc_sie_dev_t* dev) |
| 770 | { |
| 771 | struct mpc_sie_reg_map_t* p_mpc = |
| 772 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 773 | |
| 774 | return (bool)(p_mpc->ctrl & MPC_SIE300_CTRL_GATE_ACK); |
| 775 | } |
| 776 | |
| 777 | void mpc_sie_request_gating(struct mpc_sie_dev_t* dev) |
| 778 | { |
| 779 | struct mpc_sie_reg_map_t* p_mpc = |
| 780 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 781 | |
| 782 | p_mpc->ctrl |= MPC_SIE300_CTRL_GATE_REQ; |
| 783 | } |
| 784 | |
| 785 | void mpc_sie_release_gating(struct mpc_sie_dev_t* dev) |
| 786 | { |
| 787 | struct mpc_sie_reg_map_t* p_mpc = |
| 788 | (struct mpc_sie_reg_map_t*)dev->cfg->base; |
| 789 | |
| 790 | p_mpc->ctrl &= ~MPC_SIE300_CTRL_GATE_REQ; |
| 791 | } |