blob: f55a1c98f4a13843ac77dc6b079c17c2491cbd03 [file] [log] [blame]
/*
* Copyright (c) 2025, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdlib.h>
#include <arch_features.h>
#include <cassert.h>
#include <host_da_helper.h>
#include <host_realm_helper.h>
#include <host_realm_mem_layout.h>
#include <host_shared_data.h>
#include <pcie_doe.h>
#include <plat_topology.h>
#include <platform.h>
#include <test_helpers.h>
CASSERT(RTT_MIN_DEV_BLOCK_LEVEL == 2L, min_dev_block_level_mismatch);
/* Maximum number of dev memory regions to test */
#define MAX_DEV_REGIONS 32U
#define NUM_L3_REGIONS MAX_DEV_REGIONS
#define NUM_L2_REGIONS 8U
#define NUM_INFO_TESTS 3U
struct dev_mem_info {
uintptr_t base_pa; /* Dev memory region PA */
size_t min_size; /* Dev memory region size required */
long map_level; /* RTT level */
size_t map_size; /* RTT level mapping size */
unsigned int num_regions; /* Number of granule/level 2 block regions */
};
struct dev_mem_region {
uintptr_t base; /* Dev memory region base */
size_t size; /* Dev memory region size */
};
/*
* @Test_Aim@ Test device memory map and unmap commands
*
* Delegate device granules for 2 memory regions.
* Dev Mem Map/Unmap 2 device memory regions with page and block mapping levels.
* Check returned 'pa' and 'top' output values after Dev Mem Unmap.
* Check RTT entries states and RIPAS values after Dev Mem Map/Unmap.
* Undelegate device granules.
*/
test_result_t host_realm_dev_mem_map_unmap(void)
{
u_register_t rec_flag[] = {RMI_RUNNABLE};
u_register_t map_addr[NUM_INFO_TESTS][MAX_DEV_REGIONS];
u_register_t feature_flag = 0UL;
u_register_t res, rmi_features;
__unused size_t dev_size;
test_result_t ret = TEST_RESULT_FAIL;
long sl = RTT_MIN_LEVEL;
struct realm realm;
struct rtt_entry rtt;
struct dev_mem_region mem_region[2];
struct dev_mem_info mem_info[NUM_INFO_TESTS] = {
/* Test region 1 */
{0UL, 2 * RTT_L2_BLOCK_SIZE, RTT_MAX_LEVEL,
RTT_MAP_SIZE(RTT_MAX_LEVEL), NUM_L3_REGIONS},
/* Test region 2 */
{0UL, 2 * RTT_L2_BLOCK_SIZE, RTT_MAX_LEVEL,
RTT_MAP_SIZE(RTT_MAX_LEVEL), NUM_L3_REGIONS},
/* Test region 2 */
{0UL, 2 * RTT_L1_BLOCK_SIZE, RTT_MIN_DEV_BLOCK_LEVEL,
RTT_MAP_SIZE(RTT_MIN_DEV_BLOCK_LEVEL), NUM_L2_REGIONS}
};
unsigned int num[NUM_INFO_TESTS];
unsigned int num_reg, offset, i, j;
CHECK_DA_SUPPORT_IN_RMI(rmi_features);
/* Initialise memory test structures */
/* Retrieve platform PCIe memory regions */
for (num_reg = 0U; num_reg < 2U; num_reg++) {
if (plat_get_dev_region((uint64_t *)&mem_region[num_reg].base,
&mem_region[num_reg].size,
DEV_MEM_NON_COHERENT, num_reg) != 0) {
break;
}
INFO("PCIe memory region %u 0x%lx-0x%lx\n",
num_reg, mem_region[num_reg].base,
mem_region[num_reg].base +
mem_region[num_reg].size - 1UL);
}
if (num_reg == 0U) {
INFO("Cannot retrieve PCIe memory regions\n");
return TEST_RESULT_SKIPPED;
}
if (num_reg == 1U) {
/* Found 1 PCIe memory region */
if (mem_region[0].size < mem_info[0].min_size) {
INFO("PCIe memory region 0 too small\n");
return TEST_RESULT_SKIPPED;
}
if (mem_region[0].size <
mem_info[1].min_size + mem_info[2].min_size) {
/* Setup test region 1 */
mem_info[0].base_pa = mem_region[0].base;
} else {
/* Setup test region 2 */
mem_info[1].base_pa = mem_region[0].base;
mem_info[2].base_pa = mem_region[0].base;
}
} else {
/* Found 2 PCIe memory regions */
unsigned int reg_s, reg_b;
/* Find smaller and bigger region */
reg_s = (mem_region[0].size < mem_region[1].size) ? 0U : 1U;
reg_b = reg_s ^ 1U;
if (mem_region[reg_b].size < mem_info[0].min_size) {
INFO("PCIe memory regions too small\n");
return TEST_RESULT_SKIPPED;
}
if (mem_region[reg_s].size >= mem_info[0].min_size) {
/* Setup test region 1 to the smaller region */
mem_info[0].base_pa = mem_region[reg_s].base;
if (mem_region[reg_b].size >=
mem_info[1].min_size + mem_info[2].min_size) {
/* Setup test region 2 to the bigger region */
mem_info[1].base_pa = mem_region[reg_b].base;
mem_info[2].base_pa = mem_region[reg_b].base;
}
} else {
if (mem_region[reg_b].size <
mem_info[1].min_size + mem_info[2].min_size) {
/* Setup test region 1 to the bigger region */
mem_info[0].base_pa = mem_region[reg_b].base;
} else {
/* Setup test region 2 to the bigger region */
mem_info[1].base_pa = mem_region[reg_b].base;
mem_info[2].base_pa = mem_region[reg_b].base;
}
}
}
host_rmi_init_cmp_result();
if (is_feat_52b_on_4k_2_supported()) {
feature_flag = RMI_FEATURE_REGISTER_0_LPA2;
sl = RTT_MIN_LEVEL_LPA2;
}
if (!host_create_activate_realm_payload(&realm,
(u_register_t)REALM_IMAGE_BASE,
feature_flag, 0U, sl, rec_flag, 1U, 0U)) {
ERROR("Realm creation failed\n");
return TEST_RESULT_FAIL;
}
/* Seed the random number generator */
assert(is_feat_rng_present());
srand((unsigned int)read_rndr());
/*
* To use two 2MB blocks with level 3 mapping calculate
* random offset from the start of the 1st 2MB block.
*/
if (mem_info[0].base_pa != 0UL) {
offset = (RTT_L2_BLOCK_SIZE / GRANULE_SIZE) -
((unsigned int)rand() % (NUM_L3_REGIONS - 1U));
mem_info[0].base_pa += offset * GRANULE_SIZE;
}
if (mem_info[1].base_pa != 0UL) {
offset = (RTT_L2_BLOCK_SIZE / GRANULE_SIZE) -
((unsigned int)rand() % (NUM_L3_REGIONS - 1U));
mem_info[1].base_pa += offset * GRANULE_SIZE;
}
/*
* To use two 1GB blocks with level 2 mapping calculate
* random offset from the start of the 1st 1GB block.
*/
if (mem_info[2].base_pa != 0UL) {
offset = (RTT_L1_BLOCK_SIZE / RTT_L2_BLOCK_SIZE) -
((unsigned int)rand() % (NUM_L2_REGIONS - 1U));
mem_info[2].base_pa += offset * RTT_L2_BLOCK_SIZE;
}
/* Delegate device granules */
for (i = 0U; i < NUM_INFO_TESTS; i++) {
unsigned int num_granules; /* number of granules */
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
num_granules = (mem_info[i].map_size / GRANULE_SIZE) *
mem_info[i].num_regions;
for (num[i] = 0U; num[i] < num_granules; num[i]++) {
res = host_rmi_granule_delegate(mem_info[i].base_pa +
num[i] * GRANULE_SIZE);
if (res != RMI_SUCCESS) {
ERROR("%s() for 0x%lx failed, 0x%lx\n",
"host_rmi_granule_delegate",
(mem_info[i].base_pa + num[i] * GRANULE_SIZE),
res);
goto undelegate_granules;
}
}
}
/* Map device memory */
for (i = 0U; i < NUM_INFO_TESTS; i++) {
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
for (j = 0U; j < mem_info[i].num_regions; j++) {
res = host_dev_mem_map(&realm,
mem_info[i].base_pa + j * mem_info[i].map_size,
mem_info[i].map_level, &map_addr[i][j]);
if (res != REALM_SUCCESS) {
ERROR("%s() for 0x%lx failed, 0x%lx\n",
"host_realm_dev_mem_map",
mem_info[i].base_pa + j * mem_info[i].map_size,
res);
goto undelegate_granules;
}
}
}
/* Check RTT entries */
for (i = 0U; i < NUM_INFO_TESTS; i++) {
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
for (j = 0U; j < mem_info[i].num_regions; j++) {
res = host_rmi_rtt_readentry(realm.rd, map_addr[i][j],
mem_info[i].map_level, &rtt);
if (res != RMI_SUCCESS) {
ERROR("%s() for 0x%lx failed, 0x%lx\n",
"host_rmi_rtt_readentry",
map_addr[i][j], res);
goto undelegate_granules;
}
if ((rtt.state != RMI_ASSIGNED_DEV) ||
(rtt.ripas != RMI_EMPTY) ||
(rtt.walk_level != mem_info[i].map_level) ||
(rtt.out_addr != map_addr[i][j])) {
ERROR("RTT entry for 0x%lx:\n", map_addr[i][j]);
ERROR("%s level:%ld addr:0x%lx state:%lu ripas:%lu\n",
"Expected", mem_info[i].map_level, map_addr[i][j],
RMI_ASSIGNED_DEV, RMI_EMPTY);
ERROR("%s level:%ld addr:0x%lx state:%lu ripas:%lu\n",
"Read ", rtt.walk_level,
(unsigned long)rtt.out_addr, rtt.state, rtt.ripas);
goto undelegate_granules;
}
}
}
/* Unmap device memory */
for (i = 0U; i < NUM_INFO_TESTS; i++) {
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
for (j = 0U; j < mem_info[i].num_regions; j++) {
u_register_t pa, pa_exp, top, top_exp;
res = host_rmi_dev_mem_unmap(realm.rd, map_addr[i][j],
mem_info[i].map_level,
&pa, &top);
if (res != RMI_SUCCESS) {
ERROR("%s() for 0x%lx failed, 0x%lx\n",
"host_rmi_dev_mem_unmap",
map_addr[i][j], res);
goto undelegate_granules;
}
INFO("DEV_MEM_UNMAP 0x%lx: pa 0x%lx top 0x%lx\n",
map_addr[i][j], pa, top);
pa_exp = mem_info[i].base_pa + j * mem_info[i].map_size;
/* Check PA of the device memory region which was unmapped. */
if (pa != pa_exp) {
ERROR("%s() for 0x%lx failed, "
"expected pa 0x%lx, returned 0x%lx\n",
"host_rmi_dev_mem_unmap",
map_addr[i][j], pa_exp, pa);
goto undelegate_granules;
}
/*
* Check top IPA of non-live RTT entries, from entry
* at which the RTT walk terminated.
*/
if (j == (mem_info[i].num_regions - 1U)) {
/* IPA aligned to the previous mapping level */
top_exp = ALIGN_DOWN(map_addr[i][j],
RTT_MAP_SIZE(mem_info[i].map_level - 1L)) +
RTT_MAP_SIZE(mem_info[i].map_level - 1L);
} else {
/* Next IPA of the current mapping level */
top_exp = map_addr[i][j] + mem_info[i].map_size;
}
if (top != top_exp) {
ERROR("%s() for 0x%lx failed, "
"expected top 0x%lx, returned 0x%lx\n",
"host_rmi_dev_mem_unmap",
map_addr[i][j], top_exp, top);
goto undelegate_granules;
}
}
}
/* Check RTT entries */
for (i = 0U; i < NUM_INFO_TESTS; i++) {
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
for (j = 0U; j < mem_info[i].num_regions; j++) {
res = host_rmi_rtt_readentry(realm.rd, map_addr[i][j],
mem_info[i].map_level, &rtt);
if (res != RMI_SUCCESS) {
ERROR("%s() for 0x%lx failed, 0x%lx\n",
"host_rmi_rtt_readentry",
map_addr[i][j], res);
goto undelegate_granules;
}
if ((rtt.state != RMI_UNASSIGNED) ||
(rtt.ripas != RMI_EMPTY)) {
ERROR("%s() for 0x%lx failed, "
"expected state %lu ripas %lu, read %lu %lu",
"host_rmi_rtt_readentry",
map_addr[i][j], RMI_UNASSIGNED, RMI_EMPTY,
rtt.state, rtt.ripas);
goto undelegate_granules;
}
}
}
ret = TEST_RESULT_SUCCESS;
undelegate_granules:
for (i = 0U; i < NUM_INFO_TESTS; i++) {
/* Skip non-initialised test region */
if (mem_info[i].base_pa == 0UL) {
continue;
}
for (j = 0U; j < num[i]; j++) {
res = host_rmi_granule_undelegate(mem_info[i].base_pa + j * GRANULE_SIZE);
if (res != RMI_SUCCESS) {
ERROR("%s for 0x%lx failed, 0x%lx\n",
"host_rmi_granule_undelegate",
(mem_info[i].base_pa + j * GRANULE_SIZE),
res);
ret = TEST_RESULT_FAIL;
break;
}
}
}
if (!host_destroy_realm(&realm)) {
ERROR("host_destroy_realm() failed\n");
return TEST_RESULT_FAIL;
}
if (ret == TEST_RESULT_FAIL) {
return ret;
}
return host_cmp_result();
}