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 read32(base + BUILD_CONFIG_OFF); 58 } 59 60 static void tzc_write_action(vaddr_t base, enum tzc_action action) 61 { 62 write32(action, base + ACTION_OFF); 63 } 64 65 static void tzc_write_region_base_low(vaddr_t base, uint32_t region, 66 uint32_t val) 67 { 68 write32(val, base + REGION_SETUP_LOW_OFF(region)); 69 } 70 71 static void tzc_write_region_base_high(vaddr_t base, uint32_t region, 72 uint32_t val) 73 { 74 write32(val, base + REGION_SETUP_HIGH_OFF(region)); 75 } 76 77 static uint32_t tzc_read_region_attributes(vaddr_t base, uint32_t region) 78 { 79 return read32(base + REGION_ATTRIBUTES_OFF(region)); 80 } 81 82 static void tzc_write_region_attributes(vaddr_t base, uint32_t region, 83 uint32_t val) 84 { 85 write32(val, base + REGION_ATTRIBUTES_OFF(region)); 86 } 87 88 void tzc_init(vaddr_t base) 89 { 90 uint32_t tzc_build; 91 92 assert(base); 93 tzc.base = base; 94 95 /* Save values we will use later. */ 96 tzc_build = tzc_read_build_config(tzc.base); 97 tzc.addr_width = ((tzc_build >> BUILD_CONFIG_AW_SHIFT) & 98 BUILD_CONFIG_AW_MASK) + 1; 99 tzc.num_regions = ((tzc_build >> BUILD_CONFIG_NR_SHIFT) & 100 BUILD_CONFIG_NR_MASK) + 1; 101 } 102 103 /* 104 * There are two modes of operation for the region security 105 * permissions, with or without security inversion. 106 * Check TZC380 "2.2.5 Region security permissions" for 107 * more details. 108 */ 109 void tzc_security_inversion_en(vaddr_t base) 110 { 111 write32(1, base + SECURITY_INV_EN_OFF); 112 } 113 114 /* 115 * Enable a single region. Sometimes we could not use tzc_configure_region 116 * to enable the region, when security inversion is on. 117 * When need security inversion, we need to first configure 118 * region address and attribute, then configure security inversion, 119 * then enable the regions. 120 */ 121 void tzc_region_enable(uint8_t region) 122 { 123 uint32_t val; 124 125 val = tzc_read_region_attributes(tzc.base, region); 126 val |= TZC_ATTR_REGION_EN_MASK; 127 tzc_write_region_attributes(tzc.base, region, val); 128 } 129 130 /* 131 * Dump info when TZC380 catchs an unallowed access with TZC 132 * interrupt enabled. 133 */ 134 void tzc_fail_dump(void) 135 { 136 vaddr_t base __maybe_unused = core_mmu_get_va(tzc.base, 137 MEM_AREA_IO_SEC); 138 139 EMSG("Fail address Low 0x%" PRIx32, 140 read32(base + FAIL_ADDRESS_LOW_OFF)); 141 EMSG("Fail address High 0x%" PRIx32, 142 read32(base + FAIL_ADDRESS_HIGH_OFF)); 143 EMSG("Fail Control 0x%" PRIx32, read32(base + FAIL_CONTROL_OFF)); 144 EMSG("Fail Id 0x%" PRIx32, read32(base + FAIL_ID)); 145 } 146 147 void tzc_int_clear(void) 148 { 149 vaddr_t base = core_mmu_get_va(tzc.base, MEM_AREA_IO_SEC); 150 151 write32(0, base + INT_CLEAR); 152 } 153 154 static uint32_t addr_low(vaddr_t addr) 155 { 156 return (uint32_t)addr; 157 } 158 159 static uint32_t addr_high(vaddr_t addr __maybe_unused) 160 { 161 #if (UINTPTR_MAX == UINT64_MAX) 162 return addr >> 32; 163 #else 164 return 0; 165 #endif 166 } 167 168 169 /* 170 * `tzc_configure_region` is used to program regions into the TrustZone 171 * controller. 172 */ 173 void tzc_configure_region(uint8_t region, vaddr_t region_base, uint32_t attr) 174 { 175 assert(tzc.base); 176 177 assert(region < tzc.num_regions); 178 179 /* 180 * For region 0, this high/low/size/en field is Read Only (RO). 181 * So should not configure those field for region 0. 182 */ 183 if (region) { 184 tzc_write_region_base_low(tzc.base, region, 185 addr_low(region_base)); 186 tzc_write_region_base_high(tzc.base, region, 187 addr_high(region_base)); 188 tzc_write_region_attributes(tzc.base, region, attr); 189 } else { 190 tzc_write_region_attributes(tzc.base, region, 191 attr & TZC_ATTR_SP_MASK); 192 } 193 } 194 195 void tzc_set_action(enum tzc_action action) 196 { 197 assert(tzc.base); 198 199 /* 200 * - Currently no handler is provided to trap an error via interrupt 201 * or exception. 202 * - The interrupt action has not been tested. 203 */ 204 tzc_write_action(tzc.base, action); 205 } 206 207 #if TRACE_LEVEL >= TRACE_DEBUG 208 209 static uint32_t tzc_read_region_base_low(vaddr_t base, uint32_t region) 210 { 211 return read32(base + REGION_SETUP_LOW_OFF(region)); 212 } 213 214 static uint32_t tzc_read_region_base_high(vaddr_t base, uint32_t region) 215 { 216 return read32(base + REGION_SETUP_HIGH_OFF(region)); 217 } 218 219 #define REGION_MAX 16 220 void tzc_dump_state(void) 221 { 222 uint32_t n; 223 uint32_t temp_32reg, temp_32reg_h; 224 225 DMSG("enter"); 226 DMSG("security_inversion_en %x\n", 227 read32(tzc.base + SECURITY_INV_EN_OFF)); 228 for (n = 0; n <= REGION_MAX; n++) { 229 temp_32reg = tzc_read_region_attributes(tzc.base, n); 230 if (!(temp_32reg & TZC_ATTR_REGION_EN_MASK)) 231 continue; 232 233 DMSG("\n"); 234 DMSG("region %d", n); 235 temp_32reg = tzc_read_region_base_low(tzc.base, n); 236 temp_32reg_h = tzc_read_region_base_high(tzc.base, n); 237 DMSG("region_base: 0x%08x%08x", temp_32reg_h, temp_32reg); 238 temp_32reg = tzc_read_region_attributes(tzc.base, n); 239 DMSG("region sp: %x", temp_32reg >> TZC_ATTR_SP_SHIFT); 240 DMSG("region size: %x\n", (temp_32reg & TZC_REGION_SIZE_MASK) >> 241 TZC_REGION_SIZE_SHIFT); 242 } 243 DMSG("exit"); 244 } 245 246 #endif /* CFG_TRACE_LEVEL >= TRACE_DEBUG */ 247