blob: 5308d933c1e592b5f8281fd5cea0a8a851df06d6 [file] [log] [blame]
/*
* Copyright (c) 2021-2023, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <assert.h>
#include "cactus.h"
#include <arch_helpers.h>
#include "cactus_message_loop.h"
#include <sp_platform_def.h>
#include "cactus_test_cmds.h"
#include <debug.h>
#include <ffa_helpers.h>
#include <mmio.h>
#include "smmuv3_test_engine.h"
#include <sp_helpers.h>
#include "sp_tests.h"
#include <spm_common.h>
/* Miscellaneous */
#define NO_SUBSTREAMID (0xFFFFFFFFU)
#define LOOP_COUNT (5000U)
static bool run_testengine(uint32_t operation, uintptr_t source_addr,
uintptr_t target_addr, size_t transfer_size,
uint32_t attributes)
{
const uint32_t streamID_list[] = { 0U, 1U };
uintptr_t begin_addr;
uintptr_t end_addr;
uintptr_t dest_addr;
uint32_t status;
uint32_t f;
uint32_t attempts;
assert(operation == ENGINE_MEMCPY || operation == ENGINE_RAND48);
for (f = 0U; f < FRAME_COUNT; f++) {
begin_addr = source_addr + (transfer_size * f);
end_addr = begin_addr + transfer_size - 1U;
if (operation == ENGINE_MEMCPY) {
dest_addr = target_addr + (transfer_size * f);
} else {
dest_addr = 0;
}
/* Initiate DMA sequence */
mmio_write32_offset(PRIV_BASE_FRAME + F_IDX(f), PCTRL_OFF, 0);
mmio_write32_offset(PRIV_BASE_FRAME + F_IDX(f), DOWNSTREAM_PORT_OFF, 0);
mmio_write32_offset(PRIV_BASE_FRAME + F_IDX(f), STREAM_ID_OFF, streamID_list[f%2]);
mmio_write32_offset(PRIV_BASE_FRAME + F_IDX(f), SUBSTREAM_ID_OFF, NO_SUBSTREAMID);
mmio_write32_offset(USR_BASE_FRAME + F_IDX(f), UCTRL_OFF, 0);
mmio_write32_offset(USR_BASE_FRAME + F_IDX(f), ATTR_OFF, attributes);
if (operation == ENGINE_RAND48) {
mmio_write32_offset(USR_BASE_FRAME + F_IDX(f), SEED_OFF, (f + 1) * 42);
}
mmio_write64_offset(USR_BASE_FRAME + F_IDX(f), BEGIN_OFF, begin_addr);
mmio_write64_offset(USR_BASE_FRAME + F_IDX(f), END_CTRL_OFF, end_addr);
/* Legal values for stride: 1 and any multiples of 8 */
mmio_write64_offset(USR_BASE_FRAME + F_IDX(f), STRIDE_OFF, 1);
mmio_write64_offset(USR_BASE_FRAME + F_IDX(f), UDATA_OFF, dest_addr);
mmio_write32_offset(USR_BASE_FRAME + F_IDX(f), CMD_OFF, operation);
VERBOSE("SMMUv3TestEngine: waiting completion for frame: %u\n", f);
/*
* It is guaranteed that a read of "cmd" fields after writing to it will
* immediately return ENGINE_FRAME_MISCONFIGURED if the command was
* invalid.
*/
if (mmio_read32_offset(USR_BASE_FRAME + F_IDX(f), CMD_OFF) == ENGINE_MIS_CFG) {
ERROR("SMMUv3TestEngine: misconfigured for frame: %u\n", f);
return false;
}
/* Wait for operation to be complete */
attempts = 0U;
while (attempts++ < LOOP_COUNT) {
status = mmio_read32_offset(USR_BASE_FRAME + F_IDX(f), CMD_OFF);
if (status == ENGINE_HALTED) {
break;
} else if (status == ENGINE_ERROR) {
ERROR("SMMUv3: test failed, engine error.\n");
return false;
}
/*
* TODO: Introduce a small delay here to make sure the
* CPU memory accesses do not starve the interconnect
* due to continuous polling.
*/
}
if (attempts == LOOP_COUNT) {
ERROR("SMMUv3: test failed, exceeded max. wait loop.\n");
return false;
}
dsbsy();
}
return true;
}
static bool run_smmuv3_memcpy(uintptr_t start_address, size_t size, uint32_t attributes)
{
uintptr_t target_address;
size_t cpy_range = size >> 1;
bool ret;
/*
* The test engine's MEMCPY command copies data from the region in
* range [begin, end_incl] to the region with base address as udata.
* In this test, we configure the test engine to initiate memcpy from
* scratch page located at MEMCPY_SOURCE_BASE to the page located at
* address MEMCPY_TARGET_BASE
*/
target_address = start_address + cpy_range;
ret = run_testengine(ENGINE_MEMCPY, start_address, target_address,
cpy_range / FRAME_COUNT, attributes);
if (ret) {
/*
* Invalidate cached entries to force the CPU to fetch the data from
* Main memory
*/
inv_dcache_range(start_address, cpy_range);
inv_dcache_range(target_address, cpy_range);
/* Compare source and destination memory locations for data */
for (size_t i = 0U; i < (cpy_range / 8U); i++) {
if (mmio_read_64(start_address + 8 * i) !=
mmio_read_64(target_address + 8 * i)) {
ERROR("SMMUv3: Mem copy failed: %lx\n", target_address + 8 * i);
return false;
}
}
}
return ret;
}
static bool run_smmuv3_rand48(uintptr_t start_address, size_t size, uint32_t attributes)
{
return run_testengine(ENGINE_RAND48, start_address, 0, size / FRAME_COUNT, attributes);
}
CACTUS_CMD_HANDLER(smmuv3_cmd, CACTUS_DMA_SMMUv3_CMD)
{
ffa_id_t vm_id = ffa_dir_msg_dest(*args);
ffa_id_t source = ffa_dir_msg_source(*args);
uint32_t operation = args->arg4;
uintptr_t start_address = args->arg5;
size_t size = args->arg6;
uint32_t attributes = args->arg7;
VERBOSE("Received request through direct message for DMA service.\n");
/*
* At present, the test cannot be run concurrently on multiple SPs as
* there is only one SMMUv3TestEngine IP in the FVP model. Hence, run
* the test only on the first SP.
*/
if (vm_id != SPM_VM_ID_FIRST) {
return cactus_error_resp(vm_id, source, 0);
}
switch (operation) {
case ENGINE_MEMCPY:
if (run_smmuv3_memcpy(start_address, size, attributes)) {
return cactus_success_resp(vm_id, source, 0);
}
break;
case ENGINE_RAND48:
if (run_smmuv3_rand48(start_address, size, attributes)) {
return cactus_success_resp(vm_id, source, 0);
}
break;
default:
ERROR("SMMUv3TestEngine: unsupported operation (%u).\n", operation);
break;
}
return cactus_error_resp(vm_id, source, 0);
}