blob: b2f742f7eaac0434ea591263c77ceea824d24701 [file] [log] [blame]
/*
* SPDX-License-Identifier: BSD-3-Clause
* SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
*/
#include <arch_features.h>
#include <assert.h>
#include <errno.h>
#include <host_utils.h>
#include <limits.h>
#include <ripas.h>
#include <s2tt.h>
#include <s2tt_pvt_defs.h>
#include <s2tt_test_helpers.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <test_helpers.h>
#include <utils_def.h>
#include <xlat_defs.h>
static bool lpa2_enabled;
/*
* Helper function to perform any system register initialization
* needed for the tests.
*/
static void s2tt_test_helpers_arch_init(bool lpa2_en)
{
unsigned int retval __unused;
uint64_t id_aa64mmfr0_el0 = INPLACE(ID_AA64MMFR0_EL1_TGRAN4_2,
ID_AA64MMFR0_EL1_TGRAN4_2_TGRAN4);
/*
* Enable the platform. We don't need support for several CPUs
* on this testsuite, so keep it disabled for simplicity.
*/
test_helpers_rmm_start(false);
/*
* Reset the sysreg state so that we can setup
* custom values for the tests
*/
host_util_zero_sysregs_and_cbs();
/* Setup id_aa64mmfr0_el1 */
if (lpa2_en == true) {
id_aa64mmfr0_el0 |= INPLACE(ID_AA64MMFR0_EL1_PARANGE, 6UL) |
INPLACE(ID_AA64MMFR0_EL1_TGRAN4,
ID_AA64MMFR0_EL1_TGRAN4_LPA2);
} else {
id_aa64mmfr0_el0 |= INPLACE(ID_AA64MMFR0_EL1_PARANGE, 5UL) |
INPLACE(ID_AA64MMFR0_EL1_TGRAN4,
ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED);
}
lpa2_enabled = lpa2_en;
retval = host_util_set_default_sysreg_cb("id_aa64mmfr0_el1",
id_aa64mmfr0_el0);
assert(retval == 0);
/* Make sure current cpu id is 0 (primary processor) */
host_util_set_cpuid(0U);
test_helpers_expect_assert_fail(false);
}
void s2tt_test_helpers_setup(bool lpa2)
{
test_helpers_init();
s2tt_test_helpers_arch_init(lpa2);
}
unsigned long s2tt_test_helpers_lvl_mask(long level)
{
assert(level <= S2TT_TEST_HELPERS_MAX_LVL);
unsigned int levels = (unsigned int)(S2TT_TEST_HELPERS_MAX_LVL - level);
unsigned int lsb = GRANULE_SHIFT + (levels * S2TTE_STRIDE);
unsigned int msb = arch_feat_get_pa_width() - 1U;
return BIT_MASK_ULL(msb, lsb);
}
unsigned long s2tt_test_helpers_s2tte_oa_mask(void)
{
unsigned long mask = s2tt_test_helpers_lvl_mask(
S2TT_TEST_HELPERS_MAX_LVL);
if (is_feat_lpa2_4k_2_present() == true) {
mask |= MASK(S2TT_TEST_MSB);
mask &= ~MASK(S2TT_TEST_OA_MSB);
}
return mask;
}
unsigned long s2tt_test_helpers_s2tte_to_pa(unsigned long s2tte, long level)
{
unsigned long pa = s2tte & s2tt_test_helpers_lvl_mask(level);
if (is_feat_lpa2_4k_2_present() == true) {
pa &= ~MASK(S2TT_TEST_MSB);
pa |= INPLACE(S2TT_TEST_OA_MSB, EXTRACT(S2TT_TEST_MSB, s2tte));
}
return pa;
}
unsigned long s2tt_test_helpers_pa_to_s2tte(unsigned long pa, long level)
{
unsigned long s2tte;
pa &= s2tt_test_helpers_lvl_mask(level);
if (is_feat_lpa2_4k_2_present() == true) {
s2tte = pa | INPLACE(S2TT_TEST_MSB, EXTRACT(S2TT_TEST_OA_MSB, pa));
s2tte &= ~MASK(S2TT_TEST_OA_MSB);
} else {
s2tte = pa;
}
return s2tte;
}
unsigned long s2tt_test_helpers_gen_addr(long level, bool aligned)
{
unsigned int levels, lsb;
unsigned long addr;
/*
* We allow to get addresses aligned to one level below the minimum
* so we can use that address to create a table @level starting
* at such address.
*/
assert(level >= s2tt_test_helpers_min_table_lvl() - 1);
assert(level <= S2TT_TEST_HELPERS_MAX_LVL);
levels = S2TT_TEST_HELPERS_MAX_LVL - level;
lsb = GRANULE_SHIFT + (levels * S2TTE_STRIDE);
do {
unsigned int shift = (level <= S2TT_TEST_HELPERS_MIN_LVL_LPA2) ?
LM1_XLAT_ADDRESS_SHIFT : L0_XLAT_ADDRESS_SHIFT;
addr = test_helpers_get_rand_in_range((1UL << lsb), ULONG_MAX);
addr &= ((1UL << shift) - 1UL);
} while (addr == 0UL);
return aligned ? (addr & s2tt_test_helpers_lvl_mask(level)) : addr;
}
unsigned long s2tt_test_helpers_s2tte_to_attrs(unsigned long tte, bool ns)
{
unsigned long attrs_mask;
if (ns) {
attrs_mask = S2TTE_NS_ATTR_RMM | S2TT_DESC_TYPE_MASK | S2TTE_NS_ATTR_MASK;
if (!is_feat_lpa2_4k_2_present()) {
attrs_mask |= S2TTE_SH_MASK;
}
} else {
attrs_mask = ((is_feat_lpa2_4k_2_present() == true) ?
S2TTE_ATTRS_LPA2_MASK :
S2TTE_ATTRS_MASK) | S2TT_DESC_TYPE_MASK;
}
return tte & attrs_mask;
}
unsigned long s2tt_test_helpers_gen_ns_attrs(bool host, bool reserved)
{
unsigned long attrs;
bool done;
if (host == true) {
/* Generate a random set of attributes coming from the host */
do {
bool inv_attrs;
attrs = test_helpers_get_rand_in_range(0UL, ULONG_MAX);
attrs &= S2TTE_NS_ATTR_MASK;
/* Find out if we are done or not */
inv_attrs = ((attrs & S2TTE_MEMATTR_MASK) ==
S2TTE_MEMATTR_FWB_RESERVED);
done = (reserved == inv_attrs);
} while (!done);
} else {
/* Setup the NS TTE attributes that don't come from the host */
attrs = S2TTE_NS_ATTR_RMM;
}
return attrs;
}
unsigned long s2tt_test_helpers_gen_attrs(bool invalid, long level)
{
unsigned long attrs = (is_feat_lpa2_4k_2_present() == true) ?
S2TTE_ATTRS_LPA2 : S2TTE_ATTRS;
if (invalid == true) {
return attrs | S2TT_TEST_INVALID_DESC;
}
return ((level == S2TT_TEST_HELPERS_MAX_LVL) ?
S2TT_TEST_PAGE_DESC : S2TT_TEST_BLOCK_DESC) | attrs;
}
long s2tt_test_helpers_min_table_lvl(void)
{
if (is_feat_lpa2_4k_2_present() == true) {
return S2TT_TEST_HELPERS_MIN_LVL_LPA2;
}
return S2TT_TEST_HELPERS_MIN_LVL;
}
long s2tt_test_helpers_min_block_lvl(void)
{
return S2TT_MIN_BLOCK_LEVEL;
}
unsigned long s2tt_test_helpers_get_entry_va_space_size(long level)
{
assert(level >= s2tt_test_helpers_min_table_lvl());
assert(level <= S2TT_TEST_HELPERS_MAX_LVL);
unsigned long levels = S2TT_TEST_HELPERS_MAX_LVL - level;
return 1UL << (GRANULE_SHIFT + (S2TTE_STRIDE * levels));
}
unsigned long s2tt_test_helpers_get_idx_from_addr(unsigned long addr,
long level)
{
assert(level >= s2tt_test_helpers_min_table_lvl());
assert(level <= S2TT_TEST_HELPERS_MAX_LVL);
assert((addr & ~((1UL << arch_feat_get_pa_width()) - 1UL)) == 0UL);
unsigned int levels = (unsigned int)(S2TT_TEST_HELPERS_MAX_LVL - level);
unsigned int lsb = GRANULE_SHIFT + (levels * S2TTE_STRIDE);
return (addr >> lsb) & ((1UL << S2TTE_STRIDE) - 1UL);
}
bool s2tt_test_helpers_lpa2_enabled(void)
{
return lpa2_enabled;
}
unsigned long s2tt_test_create_assigned(const struct s2tt_context *s2tt_ctx,
unsigned long pa, long level,
unsigned long ripas)
{
if (ripas == S2TTE_INVALID_RIPAS_EMPTY) {
return s2tte_create_assigned_empty(s2tt_ctx, pa, level);
} else if (ripas == S2TTE_INVALID_RIPAS_DESTROYED) {
return s2tte_create_assigned_destroyed(s2tt_ctx, pa, level);
} else if (ripas == S2TTE_INVALID_RIPAS_RAM) {
return s2tte_create_assigned_ram(s2tt_ctx, pa, level);
}
return s2tte_create_assigned_ns(s2tt_ctx, pa, level);
}
unsigned long s2tt_test_create_unassigned(const struct s2tt_context *s2tt_ctx,
unsigned long ripas)
{
if (ripas == S2TTE_INVALID_RIPAS_EMPTY) {
return s2tte_create_unassigned_empty(s2tt_ctx);
} else if (ripas == S2TTE_INVALID_RIPAS_DESTROYED) {
return s2tte_create_unassigned_destroyed(s2tt_ctx);
} else if (ripas == S2TTE_INVALID_RIPAS_RAM) {
return s2tte_create_unassigned_ram(s2tt_ctx);
}
return s2tte_create_unassigned_ns(s2tt_ctx);
}