| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright (C) 2017 Marvell International Ltd. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <initcall.h> |
| #include <io.h> |
| #include <kernel/panic.h> |
| #include <mm/core_memprot.h> |
| #include <util.h> |
| |
| #define PHY_2_VIR(addr) ((vaddr_t)phys_to_virt((addr), MEM_AREA_IO_SEC)) |
| |
| #define MCU_MC_CONTROL_0_REG PHY_2_VIR(MCU_BASE + 0x044) |
| #define TRUSTZONE_LOCK BIT(31) |
| |
| #define MCU_TZ_RANGE_HIGH_REG(x) PHY_2_VIR(MCU_BASE + 0x84 + ((x) << 3)) |
| #define MCU_TZ_RANGE_LOW_REG(x) PHY_2_VIR(MCU_BASE + 0x80 + ((x) << 3)) |
| |
| #define RW_PERM 0x0 |
| #define RO_PERM 0x1 |
| #define WO_PERM 0x2 |
| #define ABORT_PERM 0x3 |
| |
| #define MAX_RANGE_NUM 16 |
| #define INVALID_SIZE_CODE 0xff |
| |
| #ifdef TEE_RES_CFG_16M |
| #define RSVD_SEC_MEM (SIZE_8M + SIZE_8M) |
| #elif defined(TEE_RES_CFG_24M) |
| #define RSVD_SEC_MEM (SIZE_8M + SIZE_8M + SIZE_8M) |
| #elif defined(TEE_RES_CFG_8M) |
| #define RSVD_SEC_MEM SIZE_8M |
| #else |
| #error "no reserved secure memory defined." |
| #endif |
| |
| #define RA_ADDR TZDRAM_BASE |
| #define RA_SIZE TZDRAM_SIZE |
| #define RA_PERM ABORT_PERM |
| |
| #define TZ_IS_VALID(data) ((data) & (0x1)) |
| #define TZ_SET_VALID(data) ((data) |= (0x1)) |
| |
| #define TZ_GET_PERM(data, ret) ((ret) = (((data) & (0x3 << 1)) >> 1)) |
| #define TZ_SET_PERM(data, val) \ |
| do { \ |
| (data) &= (~(0x3 << 1)); \ |
| (data) |= (((val) & 0x3) << 1); \ |
| } while (0) |
| |
| #define TZ_GET_RZ_EN(data, ret) ((ret) = (((data) & (0x1 << 3)) >> 3)) |
| #define TZ_SET_RZ_EN(data, val) \ |
| do { \ |
| (data) &= (~(0x1 << 3)); \ |
| (data) |= (((val) & 0x1) << 3); \ |
| } while (0) |
| |
| #define TZ_GET_AREA_LEN_CODE(data, ret) ((ret) = (((data) & (0x1F << 7)) >> 7)) |
| |
| #define TZ_SET_AREA_LEN_CODE(data, val) \ |
| do { \ |
| (data) &= (~(0x1F << 7)); \ |
| (data) |= (((val) & 0x1F) << 7); \ |
| } while (0) |
| |
| #define TZ_GET_START_ADDR_L(data, ret) \ |
| ((ret) = (((data) & 0xFFFFF000))) |
| |
| #define TZ_SET_START_ADDR_L(data, val) \ |
| do { \ |
| (data) &= (~0xFFFFF000); \ |
| (data) |= (((val) & 0xFFFFF000)); \ |
| } while (0) |
| |
| #define TZ_GET_UR_PERM(data, val) ((ret) = (((data) & (0x3 << 4)) >> 4)) |
| #define TZ_SET_UR_PERM(data, val) \ |
| do { \ |
| (data) &= (~(0x3 << 4)); \ |
| (data) |= (((val) & 0x3) << 4); \ |
| } while (0) |
| |
| #define TZ_GET_UR_RZ_EN(data, val) \ |
| ((ret) = (((data) & (0x1 << 6)) >> 6)) |
| |
| #define TZ_SET_UR_RZ_EN(data, val) \ |
| do { \ |
| (data) &= (~(0x1 << 6)); \ |
| (data) |= (((val) & 0x1) << 6); \ |
| } while (0) |
| |
| /* armada mini region size is 1M */ |
| #define RANGE_SIZE_TO_CODE(size, code, i) \ |
| do { \ |
| (code) = INVALID_SIZE_CODE; \ |
| for ((i) = 8; (i) <= 0x1f; (i)++) { \ |
| if (((uint32_t)0x1 << (i)) == ((size) >> 12)) { \ |
| (code) = (i); \ |
| break; \ |
| } \ |
| } \ |
| } while (0) |
| |
| #define RANGE_CODE_TO_SIZE_K(code, sizek) ((sizek) = ((4) << (code))) |
| |
| #define TZ_LOCK_MC(x) \ |
| do { \ |
| (x) = io_read32(MCU_MC_CONTROL_0_REG); \ |
| (x) |= (TRUSTZONE_LOCK); \ |
| io_write32(MCU_MC_CONTROL_0_REG, (x)); \ |
| } while (0) |
| |
| #define _IS_ALIGNED(_addr, _algn) (!((_addr) & ((_algn) - 1))) |
| |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, MCU_BASE, CORE_MMU_PGDIR_SIZE); |
| register_phys_mem_pgdir(MEM_AREA_IO_SEC, MC_SCR_REGISTER, CORE_MMU_PGDIR_SIZE); |
| |
| static int32_t _find_valid_range(void) |
| { |
| uint32_t i; |
| uint32_t tmp; |
| |
| for (i = 0; i < MAX_RANGE_NUM; i++) { |
| tmp = io_read32(MCU_TZ_RANGE_LOW_REG(i)); |
| if (!TZ_IS_VALID(tmp)) |
| return i; |
| } |
| return -1; |
| } |
| |
| static int32_t set_range(uint32_t addr, uint32_t size, uint32_t perm) |
| { |
| uint32_t data; |
| uint32_t sizecode; |
| int32_t valid_range; |
| uint32_t i; |
| |
| if (!_IS_ALIGNED(addr, SIZE_1M)) { |
| EMSG("region addr(0x%" PRIx32 ") is not aligned with 1M!", |
| addr); |
| return -1; |
| } |
| |
| if (!_IS_ALIGNED(size, SIZE_1M)) { |
| EMSG("region size(0x%" PRIx32 ") is not aligned with 1M!", |
| size); |
| return -1; |
| } |
| |
| if (!_IS_ALIGNED(addr, size)) { |
| EMSG("region size(0x%" PRIx32 |
| ") not align with addr(0x%" PRIx32 ")", |
| size, addr); |
| return -1; |
| } |
| |
| RANGE_SIZE_TO_CODE(size, sizecode, i); |
| if (sizecode == INVALID_SIZE_CODE) { |
| EMSG("not valid region size(2^n)! size:0x%" PRIx32, size); |
| return -1; |
| } |
| |
| valid_range = _find_valid_range(); |
| if (valid_range == -1) { |
| EMSG("ERR: can't find valid range!"); |
| return -1; |
| } |
| |
| data = io_read32(MCU_TZ_RANGE_LOW_REG(valid_range)); |
| |
| TZ_SET_VALID(data); |
| TZ_SET_PERM(data, perm); |
| TZ_SET_AREA_LEN_CODE(data, sizecode); |
| TZ_SET_START_ADDR_L(data, addr); |
| |
| if (!valid_range) { |
| /* Set Undefine Range RW */ |
| TZ_SET_UR_PERM(data, RW_PERM); |
| TZ_SET_UR_RZ_EN(data, 0); |
| } |
| |
| io_write32(MCU_TZ_RANGE_LOW_REG(valid_range), data); |
| |
| return 0; |
| } |
| |
| static void _dump_range(void) |
| { |
| uint32_t i; |
| uint32_t tmp; |
| uint32_t sizek; |
| uint32_t sizecode_read; |
| uint32_t __maybe_unused sizem; |
| uint32_t __maybe_unused addr_read; |
| uint32_t __maybe_unused perm_read; |
| |
| for (i = 0; i < MAX_RANGE_NUM; i++) { |
| tmp = io_read32(MCU_TZ_RANGE_LOW_REG(i)); |
| |
| if (TZ_IS_VALID(tmp)) { |
| TZ_GET_PERM(tmp, perm_read); |
| TZ_GET_AREA_LEN_CODE(tmp, sizecode_read); |
| TZ_GET_START_ADDR_L(tmp, addr_read); |
| |
| DMSG("Range Num%" PRIu32 |
| ": Reg 0x%" PRIx64 " = 0x%" PRIx32, |
| i, MCU_TZ_RANGE_LOW_REG(i), tmp); |
| DMSG("AddrL: 0x%08" PRIx32, addr_read); |
| RANGE_CODE_TO_SIZE_K(sizecode_read, sizek); |
| sizem = sizek >> 10; |
| DMSG("Size: %" PRIu32 "K, %" PRIu32 "M", sizek, sizem); |
| DMSG("Perm: %" PRIu32, perm_read); |
| } |
| } |
| } |
| |
| static uint32_t _find_granule(uint32_t addr, uint32_t size) |
| { |
| /* max supported granule for armada is 8TB |
| * but 2GB is far enough here |
| */ |
| uint32_t max_granule = SIZE_2G; |
| |
| while (max_granule >= SIZE_4K) { /* min granule is 4kB */ |
| if (max_granule <= size && _IS_ALIGNED(addr, max_granule)) |
| return max_granule; |
| |
| max_granule >>= 1; |
| } |
| |
| return 0; /* cannot find a valid granule */ |
| } |
| |
| static void _set_range(uint32_t addr, uint32_t size, uint32_t perm) |
| { |
| uint32_t rgn_addr = addr; |
| uint32_t rgn_size = size; |
| uint32_t p; |
| |
| while (rgn_size) { |
| p = _find_granule(rgn_addr, rgn_size); |
| if (!p) |
| panic("cannot find a suitable granule!"); |
| if (set_range(rgn_addr, p, perm)) |
| panic("set_range failed!"); |
| |
| rgn_addr += p; |
| rgn_size -= p; |
| } |
| } |
| |
| static TEE_Result init_sec_perf(void) |
| { |
| uint32_t tmp; |
| |
| /* MC_SCR config: deny NS access to MC registers */ |
| tmp = io_read32(PHY_2_VIR(MC_SCR_REGISTER)); |
| tmp |= 0x1; |
| io_write32(PHY_2_VIR(MC_SCR_REGISTER), tmp); |
| |
| /* Set Secure Memory Region */ |
| DMSG("sec-rgn size: ra = 0x%08" PRIx32 ", size = 0x%" PRIx32, |
| RA_ADDR, RA_SIZE); |
| _set_range(RA_ADDR, RA_SIZE, RA_PERM); |
| |
| /* Close TZ register modification */ |
| TZ_LOCK_MC(tmp); |
| |
| _dump_range(); |
| |
| return TEE_SUCCESS; |
| } |
| |
| service_init(init_sec_perf); |