1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright 2017 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 144 EMSG("Fail address Low 0x%" PRIx32, 145 io_read32(base + FAIL_ADDRESS_LOW_OFF)); 146 EMSG("Fail address High 0x%" PRIx32, 147 io_read32(base + FAIL_ADDRESS_HIGH_OFF)); 148 EMSG("Fail Control 0x%" PRIx32, io_read32(base + FAIL_CONTROL_OFF)); 149 EMSG("Fail Id 0x%" PRIx32, io_read32(base + FAIL_ID)); 150 } 151 152 void tzc_int_clear(void) 153 { 154 vaddr_t base = core_mmu_get_va(tzc.base, MEM_AREA_IO_SEC); 155 156 io_write32(base + INT_CLEAR, 0); 157 } 158 159 static uint32_t addr_low(vaddr_t addr) 160 { 161 return (uint32_t)addr; 162 } 163 164 static uint32_t addr_high(vaddr_t addr __maybe_unused) 165 { 166 #if (UINTPTR_MAX == UINT64_MAX) 167 return addr >> 32; 168 #else 169 return 0; 170 #endif 171 } 172 173 174 /* 175 * `tzc_configure_region` is used to program regions into the TrustZone 176 * controller. 177 */ 178 void tzc_configure_region(uint8_t region, vaddr_t region_base, uint32_t attr) 179 { 180 assert(tzc.base); 181 182 assert(region < tzc.num_regions); 183 184 /* 185 * For region 0, this high/low/size/en field is Read Only (RO). 186 * So should not configure those field for region 0. 187 */ 188 if (region) { 189 tzc_write_region_base_low(tzc.base, region, 190 addr_low(region_base)); 191 tzc_write_region_base_high(tzc.base, region, 192 addr_high(region_base)); 193 tzc_write_region_attributes(tzc.base, region, attr); 194 } else { 195 tzc_write_region_attributes(tzc.base, region, 196 attr & TZC_ATTR_SP_MASK); 197 } 198 } 199 200 void tzc_set_action(enum tzc_action action) 201 { 202 assert(tzc.base); 203 204 /* 205 * - Currently no handler is provided to trap an error via interrupt 206 * or exception. 207 * - The interrupt action has not been tested. 208 */ 209 tzc_write_action(tzc.base, action); 210 } 211 212 uint32_t tzc_get_action(void) 213 { 214 assert(tzc.base); 215 216 return tzc_read_action(tzc.base); 217 } 218 219 int tzc_auto_configure(vaddr_t addr, vaddr_t size, uint32_t attr, 220 uint8_t region) 221 { 222 uint64_t sub_region_size = 0; 223 uint64_t area = 0; 224 uint8_t lregion = region; 225 uint64_t region_size = 0; 226 vaddr_t sub_address = 0; 227 vaddr_t address = addr; 228 uint64_t lsize = size; 229 uint32_t mask = 0; 230 int i = 0; 231 uint8_t pow = TZC380_POW; 232 233 while (lsize != 0 && pow > 15) { 234 region_size = 1ULL << pow; 235 236 /* Case region fits alignment and covers requested area */ 237 if ((address % region_size == 0) && 238 ((address + lsize) % region_size == 0)) { 239 tzc_configure_region(lregion, address, 240 TZC_ATTR_REGION_SIZE(pow - 1) | 241 TZC_ATTR_REGION_EN_MASK | 242 attr); 243 lregion++; 244 address += region_size; 245 lsize -= region_size; 246 pow--; 247 continue; 248 } 249 250 /* Cover area using several subregions */ 251 sub_region_size = region_size / 8; 252 if (address % sub_region_size == 0 && 253 lsize > 2 * sub_region_size) { 254 sub_address = (address / region_size) * region_size; 255 mask = 0; 256 for (i = 0; i < 8; i++) { 257 area = (i + 1) * sub_region_size; 258 if (sub_address + area <= address || 259 sub_address + area > address + lsize) { 260 mask |= TZC_ATTR_SUBREGION_DIS(i); 261 } else { 262 address += sub_region_size; 263 lsize -= sub_region_size; 264 } 265 } 266 tzc_configure_region(lregion, sub_address, 267 TZC_ATTR_REGION_SIZE(pow - 1) | 268 TZC_ATTR_REGION_EN_MASK | 269 mask | attr); 270 lregion++; 271 pow--; 272 continue; 273 } 274 pow--; 275 } 276 assert(lsize == 0); 277 assert(address == addr + size); 278 return lregion; 279 } 280 281 #if TRACE_LEVEL >= TRACE_DEBUG 282 283 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region) 284 { 285 return io_read32(base + REGION_SETUP_LOW_OFF(region)); 286 } 287 288 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region) 289 { 290 return io_read32(base + REGION_SETUP_HIGH_OFF(region)); 291 } 292 293 #define REGION_MAX 16 294 void tzc_dump_state(void) 295 { 296 uint32_t n; 297 uint32_t temp_32reg, temp_32reg_h; 298 299 DMSG("enter"); 300 DMSG("security_inversion_en %x", 301 io_read32(tzc.base + SECURITY_INV_EN_OFF)); 302 for (n = 0; n <= REGION_MAX; n++) { 303 temp_32reg = tzc_read_region_attributes(tzc.base, n); 304 if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK)) 305 continue; 306 307 DMSG(""); 308 DMSG("region %d", n); 309 temp_32reg = tzc_read_region_base_low(tzc.base, n); 310 temp_32reg_h = tzc_read_region_base_high(tzc.base, n); 311 DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg); 312 temp_32reg = tzc_read_region_attributes(tzc.base, n); 313 DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT); 314 DMSG("region size: %x", (temp_32reg & TZC_REGION_SIZE_MASK) >> 315 TZC_REGION_SIZE_SHIFT); 316 } 317 DMSG("exit"); 318 } 319 320 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */ 321