arm-trusted-firmware/drivers/arm/smmu/smmu_v3.c
Sona Mathew aaaf2cc313 refactor(cpufeat): add macro to simplify is_feat_xx_present
In this patch, we are trying to introduce the wrapper macro
CREATE_FEATURE_PRESENT to get the following capability and
align it for all the features:

-> is_feat_xx_present(): Does Hardware implement the feature.
-> uniformity in naming the function across multiple features.
-> improved readability

The is_feat_xx_present() is implemented to check if the hardware
implements the feature and does not take into account the
ENABLE_FEAT_XXX flag enabled/disabled in software.

- CREATE_FEATURE_PRESENT(name, idreg, shift, mask, idval)
The wrapper macro reduces the function to a single line and
creates the is_feat_xx_present function that checks the
id register based on the shift and mask values and compares
this against a determined idvalue.

Change-Id: I7b91d2c9c6fbe55f94c693aa1b2c50be54fb9ecc
Signed-off-by: Sona Mathew <sonarebecca.mathew@arm.com>
2024-05-02 12:16:16 -05:00

181 lines
5.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 firmwares 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;
}