Alexandru Gagniuc | 0ea6163 | 2021-07-15 14:19:25 -0500 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Simple API for configuring TrustZone memory restrictions for TZC400 |
| 4 | */ |
| 5 | |
| 6 | #define LOG_CATEGORY LOGC_ARCH |
| 7 | |
| 8 | #include <linux/iopoll.h> |
| 9 | #include <mach/tzc.h> |
| 10 | |
| 11 | #define TZC_TIMEOUT_US 100 |
| 12 | |
| 13 | #define TZC_BUILD_CONFIG 0x00 |
| 14 | #define TZC_ACTION 0x04 |
| 15 | #define TZC_ACTION_NONE 0 |
| 16 | #define TZC_ACTION_ERR 1 |
| 17 | #define TZC_ACTION_INT 2 |
| 18 | #define TZC_ACTION_INT_ERR 3 |
| 19 | #define TZC_GATE_KEEPER 0x08 |
| 20 | |
| 21 | #define TZC_REGION0_OFFSET 0x100 |
| 22 | #define TZC_REGION_CFG_SIZE 0x20 |
| 23 | #define TZC_REGION1_OFFSET 0x120 |
| 24 | #define TZC_REGION_BASE 0x00 |
| 25 | #define TZC_REGION_TOP 0x08 |
| 26 | #define TZC_REGION_ATTRIBUTE 0x10 |
| 27 | #define TZC_REGION_ACCESS 0x14 |
| 28 | |
| 29 | static uint32_t tzc_read(uintptr_t tzc, size_t reg) |
| 30 | { |
| 31 | return readl(tzc + reg); |
| 32 | } |
| 33 | |
| 34 | static void tzc_write(uintptr_t tzc, size_t reg, uint32_t val) |
| 35 | { |
| 36 | writel(val, tzc + reg); |
| 37 | } |
| 38 | |
| 39 | static uint16_t tzc_config_get_active_filters(const struct tzc_region *cfg) |
| 40 | { |
| 41 | uint16_t active_filters = 0; |
| 42 | |
| 43 | for ( ; cfg->top != 0; cfg++) |
| 44 | active_filters |= cfg->filters_mask; |
| 45 | |
| 46 | return active_filters; |
| 47 | } |
| 48 | |
| 49 | int tzc_configure(uintptr_t tzc, const struct tzc_region *cfg) |
| 50 | { |
| 51 | uintptr_t region = tzc + TZC_REGION1_OFFSET; |
| 52 | uint32_t nsid, attr_reg, active_filters; |
| 53 | int ret; |
| 54 | |
| 55 | active_filters = tzc_config_get_active_filters(cfg); |
| 56 | if (active_filters == 0) |
| 57 | return -EINVAL; |
| 58 | |
| 59 | ret = tzc_disable_filters(tzc, active_filters); |
| 60 | if (ret < 0) |
| 61 | return ret; |
| 62 | |
| 63 | for ( ; cfg->top != 0; cfg++, region += TZC_REGION_CFG_SIZE) { |
| 64 | attr_reg = (cfg->sec_mode & 0x03) << 30; |
| 65 | attr_reg |= (cfg->filters_mask & 0x03) << 0; |
| 66 | nsid = cfg->nsec_id & 0xffff; |
| 67 | nsid |= nsid << 16; |
| 68 | |
| 69 | tzc_write(region, TZC_REGION_BASE, cfg->base); |
| 70 | tzc_write(region, TZC_REGION_TOP, cfg->top); |
| 71 | tzc_write(region, TZC_REGION_ACCESS, nsid); |
| 72 | tzc_write(region, TZC_REGION_ATTRIBUTE, attr_reg); |
| 73 | } |
| 74 | |
| 75 | tzc_write(tzc, TZC_ACTION, TZC_ACTION_ERR); |
| 76 | return tzc_enable_filters(tzc, active_filters); |
| 77 | } |
| 78 | |
| 79 | int tzc_disable_filters(uintptr_t tzc, uint16_t filters_mask) |
| 80 | { |
| 81 | uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER); |
| 82 | uint32_t filter_status = filters_mask << 16; |
| 83 | |
| 84 | gate &= ~filters_mask; |
| 85 | tzc_write(tzc, TZC_GATE_KEEPER, gate); |
| 86 | |
| 87 | return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate, |
| 88 | (gate & filter_status) == 0, TZC_TIMEOUT_US); |
| 89 | } |
| 90 | |
| 91 | int tzc_enable_filters(uintptr_t tzc, uint16_t filters_mask) |
| 92 | { |
| 93 | uint32_t gate = tzc_read(tzc, TZC_GATE_KEEPER); |
| 94 | uint32_t filter_status = filters_mask << 16; |
| 95 | |
| 96 | gate |= filters_mask; |
| 97 | tzc_write(tzc, TZC_GATE_KEEPER, gate); |
| 98 | |
| 99 | return readl_poll_timeout(tzc + TZC_GATE_KEEPER, gate, |
| 100 | (gate & filter_status) == filter_status, |
| 101 | TZC_TIMEOUT_US); |
| 102 | } |
| 103 | |
| 104 | static const char *sec_access_str_from_attr(uint32_t attr) |
| 105 | { |
| 106 | const char *const sec_mode[] = { "none", "RO ", "WO ", "RW " }; |
| 107 | |
| 108 | return sec_mode[(attr >> 30) & 0x03]; |
| 109 | } |
| 110 | |
| 111 | void tzc_dump_config(uintptr_t tzc) |
| 112 | { |
| 113 | uint32_t build_config, base, top, attr, nsaid; |
| 114 | int num_regions, i; |
| 115 | uintptr_t region; |
| 116 | |
| 117 | build_config = tzc_read(tzc, TZC_BUILD_CONFIG); |
| 118 | num_regions = ((build_config >> 0) & 0x1f) + 1; |
| 119 | |
| 120 | for (i = 0; i < num_regions; i++) { |
| 121 | region = tzc + TZC_REGION0_OFFSET + i * TZC_REGION_CFG_SIZE; |
| 122 | |
| 123 | base = tzc_read(region, TZC_REGION_BASE); |
| 124 | top = tzc_read(region, TZC_REGION_TOP); |
| 125 | attr = tzc_read(region, TZC_REGION_ATTRIBUTE); |
| 126 | nsaid = tzc_read(region, TZC_REGION_ACCESS); |
| 127 | |
| 128 | if (attr == 0 && nsaid == 0) |
| 129 | continue; |
| 130 | |
| 131 | log_info("TZC region %u: %08x->%08x - filters 0x%x\n", |
| 132 | i, base, top, (attr >> 0) & 0xf); |
| 133 | log_info("\t Secure access %s NSAID %08x\n", |
| 134 | sec_access_str_from_attr(attr), nsaid); |
| 135 | } |
| 136 | } |