1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright 2017-2020 NXP 4 * All rights reserved. 5 * 6 * Peng Fan <peng.fan@nxp.com> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright notice, 12 * this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright notice, 15 * this list of conditions and the following disclaimer in the documentation 16 * and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <assert.h> 32 #include <drivers/tzc380.h> 33 #include <io.h> 34 #include <kernel/panic.h> 35 #include <mm/core_memprot.h> 36 #include <mm/core_mmu.h> 37 #include <stddef.h> 38 #include <trace.h> 39 #include <util.h> 40 41 /* 42 * Implementation defined values used to validate inputs later. 43 * Filters : max of 4 ; 0 to 3 44 * Regions : max of 9 ; 0 to 8 45 * Address width : Values between 32 to 64 46 */ 47 struct tzc_instance { 48 vaddr_t base; 49 uint8_t addr_width; 50 uint8_t num_regions; 51 }; 52 53 static struct tzc_instance tzc; 54 55 static uint32_t tzc_read_build_config(vaddr_t base) 56 { 57 return io_read32(base + BUILD_CONFIG_OFF); 58 } 59 60 static void tzc_write_action(vaddr_t base, enum tzc_action action) 61 { 62 io_write32(base + ACTION_OFF, action); 63 } 64 65 static uint32_t tzc_read_action(vaddr_t base) 66 { 67 return io_read32(base + ACTION_OFF); 68 } 69 70 static void tzc_write_region_base_low(vaddr_t base, uint32_t region, 71 uint32_t val) 72 { 73 io_write32(base + REGION_SETUP_LOW_OFF(region), val); 74 } 75 76 static void tzc_write_region_base_high(vaddr_t base, uint32_t region, 77 uint32_t val) 78 { 79 io_write32(base + REGION_SETUP_HIGH_OFF(region), val); 80 } 81 82 static uint32_t tzc_read_region_attributes(vaddr_t base, uint32_t region) 83 { 84 return io_read32(base + REGION_ATTRIBUTES_OFF(region)); 85 } 86 87 static void tzc_write_region_attributes(vaddr_t base, uint32_t region, 88 uint32_t val) 89 { 90 io_write32(base + REGION_ATTRIBUTES_OFF(region), val); 91 } 92 93 void tzc_init(vaddr_t base) 94 { 95 uint32_t tzc_build; 96 97 assert(base); 98 tzc.base = base; 99 100 /* Save values we will use later. */ 101 tzc_build = tzc_read_build_config(tzc.base); 102 tzc.addr_width = ((tzc_build >> BUILD_CONFIG_AW_SHIFT) & 103 BUILD_CONFIG_AW_MASK) + 1; 104 tzc.num_regions = ((tzc_build >> BUILD_CONFIG_NR_SHIFT) & 105 BUILD_CONFIG_NR_MASK) + 1; 106 } 107 108 /* 109 * There are two modes of operation for the region security 110 * permissions, with or without security inversion. 111 * Check TZC380 "2.2.5 Region security permissions" for 112 * more details. 113 */ 114 void tzc_security_inversion_en(vaddr_t base) 115 { 116 io_write32(base + SECURITY_INV_EN_OFF, 1); 117 } 118 119 /* 120 * Enable a single region. Sometimes we could not use tzc_configure_region 121 * to enable the region, when security inversion is on. 122 * When need security inversion, we need to first configure 123 * region address and attribute, then configure security inversion, 124 * then enable the regions. 125 */ 126 void tzc_region_enable(uint8_t region) 127 { 128 uint32_t val; 129 130 val = tzc_read_region_attributes(tzc.base, region); 131 val |= TZC_ATTR_REGION_EN_MASK; 132 tzc_write_region_attributes(tzc.base, region, val); 133 } 134 135 /* 136 * Dump info when TZC380 catchs an unallowed access with TZC 137 * interrupt enabled. 138 */ 139 void tzc_fail_dump(void) 140 { 141 vaddr_t base __maybe_unused = core_mmu_get_va(tzc.base, 142 MEM_AREA_IO_SEC, 143 TZC400_REG_SIZE); 144 145 EMSG("Fail address Low 0x%" PRIx32, 146 io_read32(base + FAIL_ADDRESS_LOW_OFF)); 147 EMSG("Fail address High 0x%" PRIx32, 148 io_read32(base + FAIL_ADDRESS_HIGH_OFF)); 149 EMSG("Fail Control 0x%" PRIx32, io_read32(base + FAIL_CONTROL_OFF)); 150 EMSG("Fail Id 0x%" PRIx32, io_read32(base + FAIL_ID)); 151 } 152 153 void tzc_int_clear(void) 154 { 155 vaddr_t base = core_mmu_get_va(tzc.base, MEM_AREA_IO_SEC, 156 TZC400_REG_SIZE); 157 158 io_write32(base + INT_CLEAR, 0); 159 } 160 161 static uint32_t addr_low(vaddr_t addr) 162 { 163 return (uint32_t)addr; 164 } 165 166 static uint32_t addr_high(vaddr_t addr __maybe_unused) 167 { 168 #if (UINTPTR_MAX == UINT64_MAX) 169 return addr >> 32; 170 #else 171 return 0; 172 #endif 173 } 174 175 176 /* 177 * `tzc_configure_region` is used to program regions into the TrustZone 178 * controller. 179 */ 180 void tzc_configure_region(uint8_t region, vaddr_t region_base, uint32_t attr) 181 { 182 assert(tzc.base); 183 184 assert(region < tzc.num_regions); 185 186 /* 187 * For region 0, this high/low/size/en field is Read Only (RO). 188 * So should not configure those field for region 0. 189 */ 190 if (region) { 191 tzc_write_region_base_low(tzc.base, region, 192 addr_low(region_base)); 193 tzc_write_region_base_high(tzc.base, region, 194 addr_high(region_base)); 195 tzc_write_region_attributes(tzc.base, region, attr); 196 } else { 197 tzc_write_region_attributes(tzc.base, region, 198 attr & TZC_ATTR_SP_MASK); 199 } 200 } 201 202 void tzc_set_action(enum tzc_action action) 203 { 204 assert(tzc.base); 205 206 /* 207 * - Currently no handler is provided to trap an error via interrupt 208 * or exception. 209 * - The interrupt action has not been tested. 210 */ 211 tzc_write_action(tzc.base, action); 212 } 213 214 uint32_t tzc_get_action(void) 215 { 216 assert(tzc.base); 217 218 return tzc_read_action(tzc.base); 219 } 220 221 int tzc_auto_configure(vaddr_t addr, vaddr_t size, uint32_t attr, 222 uint8_t region) 223 { 224 uint64_t sub_region_size = 0; 225 uint64_t area = 0; 226 uint8_t lregion = region; 227 uint64_t region_size = 0; 228 vaddr_t sub_address = 0; 229 vaddr_t address = addr; 230 uint64_t lsize = size; 231 uint32_t mask = 0; 232 int i = 0; 233 uint8_t pow = 0; 234 235 assert(tzc.base); 236 237 /* 238 * TZC380 RM 239 * For region_attributes_<n> registers, region_size: 240 * Note: The AXI address width, that is AXI_ADDRESS_MSB+1, controls the 241 * upper limit value of the field. 242 */ 243 pow = tzc.addr_width; 244 245 while (lsize != 0 && pow > 15) { 246 region_size = 1ULL << pow; 247 248 /* Case region fits alignment and covers requested area */ 249 if ((address % region_size == 0) && 250 ((address + lsize) % region_size == 0)) { 251 tzc_configure_region(lregion, address, 252 TZC_ATTR_REGION_SIZE(pow - 1) | 253 TZC_ATTR_REGION_EN_MASK | 254 attr); 255 lregion++; 256 address += region_size; 257 lsize -= region_size; 258 pow = tzc.addr_width; 259 continue; 260 } 261 262 /* Cover area using several subregions */ 263 sub_region_size = region_size / 8; 264 if (address % sub_region_size == 0 && 265 lsize > 2 * sub_region_size) { 266 sub_address = (address / region_size) * region_size; 267 mask = 0; 268 for (i = 0; i < 8; i++) { 269 area = (i + 1) * sub_region_size; 270 if (sub_address + area <= address || 271 sub_address + area > address + lsize) { 272 mask |= TZC_ATTR_SUBREGION_DIS(i); 273 } else { 274 address += sub_region_size; 275 lsize -= sub_region_size; 276 } 277 } 278 tzc_configure_region(lregion, sub_address, 279 TZC_ATTR_REGION_SIZE(pow - 1) | 280 TZC_ATTR_REGION_EN_MASK | 281 mask | attr); 282 lregion++; 283 pow = tzc.addr_width; 284 continue; 285 } 286 pow--; 287 } 288 assert(lsize == 0); 289 assert(address == addr + size); 290 return lregion; 291 } 292 293 /* 294 * `region_lockdown` is used to lockdown the TZC380 configuration to prevent 295 * unintended overwrites of the configuration. Returns TEE_ERROR_SECURITY in 296 * case the lockdown fails. 297 */ 298 TEE_Result tzc_regions_lockdown(void) 299 { 300 uint32_t val = 0; 301 uint32_t check = 0; 302 303 val = LOCKDOWN_RANGE_ENABLE | (tzc.num_regions - 1); 304 io_write32(tzc.base + LOCKDOWN_RANGE_OFF, val); 305 check = io_read32(tzc.base + LOCKDOWN_RANGE_OFF); 306 if (check != val) 307 return TEE_ERROR_SECURITY; 308 309 val = LOCKDOWN_SELECT_RANGE_ENABLE; 310 io_write32(tzc.base + LOCKDOWN_SELECT_OFF, val); 311 check = io_read32(tzc.base + LOCKDOWN_SELECT_OFF); 312 if (check != val) 313 return TEE_ERROR_SECURITY; 314 315 return TEE_SUCCESS; 316 } 317 318 #if TRACE_LEVEL >= TRACE_DEBUG 319 320 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region) 321 { 322 return io_read32(base + REGION_SETUP_LOW_OFF(region)); 323 } 324 325 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region) 326 { 327 return io_read32(base + REGION_SETUP_HIGH_OFF(region)); 328 } 329 330 #define REGION_MAX 16 331 void tzc_dump_state(void) 332 { 333 uint32_t n; 334 uint32_t temp_32reg, temp_32reg_h; 335 336 DMSG("TZC380 configuration:"); 337 DMSG("security_inversion_en %x", 338 io_read32(tzc.base + SECURITY_INV_EN_OFF)); 339 for (n = 0; n <= REGION_MAX; n++) { 340 temp_32reg = tzc_read_region_attributes(tzc.base, n); 341 if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK)) 342 continue; 343 344 DMSG(""); 345 DMSG("region %d", n); 346 temp_32reg = tzc_read_region_base_low(tzc.base, n); 347 temp_32reg_h = tzc_read_region_base_high(tzc.base, n); 348 DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg); 349 temp_32reg = tzc_read_region_attributes(tzc.base, n); 350 DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT); 351 DMSG("region size: %x", (temp_32reg & TZC_REGION_SIZE_MASK) >> 352 TZC_REGION_SIZE_SHIFT); 353 } 354 DMSG("Lockdown select: %"PRIx32, 355 io_read32(tzc.base + LOCKDOWN_SELECT_OFF)); 356 DMSG("Lockdown range: %"PRIx32, 357 io_read32(tzc.base + LOCKDOWN_RANGE_OFF)); 358 DMSG("Action register: %"PRIx32, tzc_get_action()); 359 DMSG("exit"); 360 } 361 362 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */ 363