1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
/*
* Copyright (c) 2017-2024, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/debug.h>
#include <cdefs.h>
#include <drivers/arm/smmu_v3.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <arch_features.h>
/* SMMU poll number of retries */
#define SMMU_POLL_TIMEOUT_US U(1000)
static int smmuv3_poll(uintptr_t smmu_reg, uint32_t mask,
uint32_t value)
{
uint32_t reg_val;
uint64_t timeout;
/* Set 1ms timeout value */
timeout = timeout_init_us(SMMU_POLL_TIMEOUT_US);
do {
reg_val = mmio_read_32(smmu_reg);
if ((reg_val & mask) == value)
return 0;
} while (!timeout_elapsed(timeout));
ERROR("Timeout polling SMMUv3 register @%p\n", (void *)smmu_reg);
ERROR("Read value 0x%x, expected 0x%x\n", reg_val,
value == 0U ? reg_val & ~mask : reg_val | mask);
return -1;
}
/*
* Abort all incoming transactions in order to implement a default
* deny policy on reset.
*/
int __init smmuv3_security_init(uintptr_t smmu_base)
{
/* Attribute update has completed when SMMU_(S)_GBPA.Update bit is 0 */
if (smmuv3_poll(smmu_base + SMMU_GBPA, SMMU_GBPA_UPDATE, 0U) != 0U)
return -1;
/*
* SMMU_(S)_CR0 resets to zero with all streams bypassing the SMMU,
* so just abort all incoming transactions.
*/
mmio_setbits_32(smmu_base + SMMU_GBPA,
SMMU_GBPA_UPDATE | SMMU_GBPA_ABORT);
if (smmuv3_poll(smmu_base + SMMU_GBPA, SMMU_GBPA_UPDATE, 0U) != 0U)
return -1;
/* Check if the SMMU supports secure state */
if ((mmio_read_32(smmu_base + SMMU_S_IDR1) &
SMMU_S_IDR1_SECURE_IMPL) == 0U)
return 0;
/* Abort all incoming secure transactions */
if (smmuv3_poll(smmu_base + SMMU_S_GBPA, SMMU_S_GBPA_UPDATE, 0U) != 0U)
return -1;
mmio_setbits_32(smmu_base + SMMU_S_GBPA,
SMMU_S_GBPA_UPDATE | SMMU_S_GBPA_ABORT);
return smmuv3_poll(smmu_base + SMMU_S_GBPA, SMMU_S_GBPA_UPDATE, 0U);
}
/* Initialize the SMMU by invalidating all secure caches and TLBs. */
int __init smmuv3_init(uintptr_t smmu_base)
{
/*
* Initiate invalidation of secure caches and TLBs if the SMMU
* supports secure state. If not, it's implementation defined
* as to how SMMU_S_INIT register is accessed.
* As per Arm SMMUv3 specification the SMMU_S_INIT register in a SMMU
* with RME implementation has following properties:
* a) all SMMU registers that are specified to be accessible only in
* the Secure physical address space are additionally accessible in
* Root physical address space.
* b) as GPT information is permitted to be cached in a TLB, the
* SMMU_S_INIT.INV_ALL operation also invalidates all GPT information
* cached in TLBs.
* Additionally, it is Root firmware’s responsibility to write to
* INV_ALL before enabling SMMU_ROOT_CR0.{ACCESSEN,GPCEN}.
*/
mmio_write_32(smmu_base + SMMU_S_INIT, SMMU_S_INIT_INV_ALL);
/* Wait for global invalidation operation to finish */
if (smmuv3_poll(smmu_base + SMMU_S_INIT,
SMMU_S_INIT_INV_ALL, 0U) != 0) {
return -1;
}
#if ENABLE_RME
if (is_feat_rme_present()) {
if ((mmio_read_32(smmu_base + SMMU_ROOT_IDR0) &
SMMU_ROOT_IDR0_ROOT_IMPL) == 0U) {
WARN("Skip SMMU GPC configuration.\n");
} else {
uint64_t gpccr_el3 = read_gpccr_el3();
uint64_t gptbr_el3 = read_gptbr_el3();
/* SMMU_ROOT_GPT_BASE_CFG[16] is RES0. */
gpccr_el3 &= ~(1UL << 16);
/*
* TODO: SMMU_ROOT_GPT_BASE_CFG is 64b in the spec,
* but SMMU model only accepts 32b access.
*/
mmio_write_32(smmu_base + SMMU_ROOT_GPT_BASE_CFG,
gpccr_el3);
/*
* pa_gpt_table_base[51:12] maps to GPTBR_EL3[39:0]
* whereas it maps to SMMU_ROOT_GPT_BASE[51:12]
* hence needs a 12 bit left shit.
*/
mmio_write_64(smmu_base + SMMU_ROOT_GPT_BASE,
gptbr_el3 << 12);
/*
* ACCESSEN=1: SMMU- and client-originated accesses are
* not terminated by this mechanism.
* GPCEN=1: All clients and SMMU-originated accesses,
* except GPT-walks, are subject to GPC.
*/
mmio_setbits_32(smmu_base + SMMU_ROOT_CR0,
SMMU_ROOT_CR0_GPCEN |
SMMU_ROOT_CR0_ACCESSEN);
/* Poll for ACCESSEN and GPCEN ack bits. */
if (smmuv3_poll(smmu_base + SMMU_ROOT_CR0ACK,
SMMU_ROOT_CR0_GPCEN |
SMMU_ROOT_CR0_ACCESSEN,
SMMU_ROOT_CR0_GPCEN |
SMMU_ROOT_CR0_ACCESSEN) != 0) {
WARN("Failed enabling SMMU GPC.\n");
/*
* Do not return in error, but fall back to
* invalidating all entries through the secure
* register file.
*/
}
}
}
#endif /* ENABLE_RME */
return 0;
}
int smmuv3_ns_set_abort_all(uintptr_t smmu_base)
{
/* Attribute update has completed when SMMU_GBPA.Update bit is 0 */
if (smmuv3_poll(smmu_base + SMMU_GBPA, SMMU_GBPA_UPDATE, 0U) != 0U) {
return -1;
}
/*
* Set GBPA's ABORT bit. Other GBPA fields are presumably ignored then,
* so simply preserve their value.
*/
mmio_setbits_32(smmu_base + SMMU_GBPA, SMMU_GBPA_UPDATE | SMMU_GBPA_ABORT);
if (smmuv3_poll(smmu_base + SMMU_GBPA, SMMU_GBPA_UPDATE, 0U) != 0U) {
return -1;
}
/* Disable the SMMU to engage the GBPA fields previously configured. */
mmio_clrbits_32(smmu_base + SMMU_CR0, SMMU_CR0_SMMUEN);
if (smmuv3_poll(smmu_base + SMMU_CR0ACK, SMMU_CR0_SMMUEN, 0U) != 0U) {
return -1;
}
return 0;
}
|