Cactus test for exercising SMMUv3 driver to perform stage2 translation
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
Change-Id: I498cb63aed497ab469a38e486a7943dd634e5b36
diff --git a/spm/cactus/cactus.mk b/spm/cactus/cactus.mk
index a52120f..ae66c1d 100644
--- a/spm/cactus/cactus.mk
+++ b/spm/cactus/cactus.mk
@@ -49,6 +49,7 @@
cactus_test_ffa.c \
cactus_test_interrupts.c \
cactus_test_memory_sharing.c \
+ cactus_tests_smmuv3.c \
)
# TODO: Remove dependency on TFTF files.
diff --git a/spm/cactus/cactus_main.c b/spm/cactus/cactus_main.c
index 45d2db0..ff3f618 100644
--- a/spm/cactus/cactus_main.c
+++ b/spm/cactus/cactus_main.c
@@ -93,6 +93,9 @@
/* PLAT_ARM_DEVICE0 area includes UART2 necessary to console */
MAP_REGION_FLAT(PLAT_ARM_DEVICE0_BASE, PLAT_ARM_DEVICE0_SIZE,
MT_DEVICE | MT_RW),
+ /* scratch memory allocated to be used for running SMMU tests */
+ MAP_REGION_FLAT(PLAT_CACTUS_MEMCPY_BASE, PLAT_CACTUS_MEMCPY_RANGE,
+ MT_MEMORY | MT_RW),
{0}
};
diff --git a/spm/cactus/cactus_tests/cactus_tests_smmuv3.c b/spm/cactus/cactus_tests/cactus_tests_smmuv3.c
new file mode 100644
index 0000000..ce53dc6
--- /dev/null
+++ b/spm/cactus/cactus_tests/cactus_tests_smmuv3.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdint.h>
+
+#include <arch_helpers.h>
+#include "cactus.h"
+#include "cactus_message_loop.h"
+#include <cactus_platform_def.h>
+#include "cactus_test_cmds.h"
+#include "cactus_tests.h"
+#include <debug.h>
+#include <ffa_helpers.h>
+#include <mmio.h>
+#include "smmuv3_test_engine.h"
+#include <sp_helpers.h>
+#include <spm_common.h>
+
+/* Source and target address for memcopy operation */
+#define MEMCPY_SOURCE_BASE PLAT_CACTUS_MEMCPY_BASE
+#define MEMPCY_TOTAL_SIZE (PLAT_CACTUS_MEMCPY_RANGE / 2)
+#define MEMCPY_TARGET_BASE (MEMCPY_SOURCE_BASE + MEMPCY_TOTAL_SIZE)
+
+/* Miscellaneous */
+#define NO_SUBSTREAMID (0xFFFFFFFFU)
+#define TRANSFER_SIZE (MEMPCY_TOTAL_SIZE / FRAME_COUNT)
+#define LOOP_COUNT (5000U)
+
+static bool run_smmuv3_test(void)
+{
+ uint64_t source_addr, cpy_range, target_addr;
+ uint64_t begin_addr, end_addr, dest_addr;
+ uint32_t status;
+ unsigned int i, f, attempts;
+
+ /*
+ * 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
+ */
+
+ VERBOSE("CACTUS: Running SMMUv3 test\n");
+
+ source_addr = MEMCPY_SOURCE_BASE;
+ cpy_range = MEMPCY_TOTAL_SIZE;
+ target_addr = MEMCPY_TARGET_BASE;
+ uint32_t streamID_list[] = { 0U, 1U };
+
+ uint64_t data[] = {
+ ULL(0xBAADFEEDCEEBDAAF),
+ ULL(0x0123456776543210)
+ };
+
+ /* Write pre-determined content to source pages */
+ for (i = 0U; i < (cpy_range / 8U); i++) {
+ mmio_write64_offset(source_addr, i * 8, data[i%2]);
+ }
+
+ /* Clean the data caches */
+ clean_dcache_range(source_addr, cpy_range);
+
+ /*
+ * Make sure above load, store and cache maintenance instructions
+ * complete before we start writing to TestEngine frame configuration
+ * fields
+ */
+ dsbsy();
+
+ for (f = 0U; f < FRAME_COUNT; f++) {
+ attempts = 0U;
+ begin_addr = source_addr + (TRANSFER_SIZE * f);
+ end_addr = begin_addr + TRANSFER_SIZE - 1U;
+ dest_addr = target_addr + (TRANSFER_SIZE * f);
+
+ /* 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), SEED_OFF, 0);
+ 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, ENGINE_MEMCPY);
+ VERBOSE("SMMUv3TestEngine: Waiting for MEMCPY 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 mem copy to be complete */
+ 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\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\n");
+ return false;
+ }
+
+ dsbsy();
+ }
+
+ /*
+ * Invalidate cached entries to force the CPU to fetch the data from
+ * Main memory
+ */
+ inv_dcache_range(source_addr, cpy_range);
+ inv_dcache_range(target_addr, cpy_range);
+
+ /* Compare source and destination memory locations for data */
+ for (i = 0U; i < (cpy_range / 8U); i++) {
+ if (mmio_read_64(source_addr + 8 * i) != mmio_read_64(target_addr + 8 * i)) {
+ ERROR("SMMUv3: Mem copy failed: %llx\n", target_addr + 8 * i);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CACTUS_CMD_HANDLER(smmuv3_cmd, CACTUS_DMA_SMMUv3_CMD)
+{
+ smc_ret_values ffa_ret;
+ ffa_vm_id_t vm_id = ffa_dir_msg_dest(*args);
+ ffa_vm_id_t source = ffa_dir_msg_source(*args);
+
+ 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);
+ }
+
+ if (run_smmuv3_test()) {
+ ffa_ret = cactus_success_resp(vm_id, source, 0);
+ } else {
+ ffa_ret = cactus_error_resp(vm_id, source, 0);
+ }
+
+ return ffa_ret;
+}
diff --git a/spm/cactus/cactus_tests/smmuv3_test_engine.h b/spm/cactus/cactus_tests/smmuv3_test_engine.h
new file mode 100644
index 0000000..32d86ac
--- /dev/null
+++ b/spm/cactus/cactus_tests/smmuv3_test_engine.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/* The test engine supports numerous frames but we only use a few */
+#define FRAME_COUNT (2U)
+#define FRAME_SIZE (0x80U) /* 128 bytes */
+#define F_IDX(n) (n * FRAME_SIZE)
+
+/* Commands supported by SMMUv3TestEngine built into the AEM */
+#define ENGINE_NO_FRAME (0U)
+#define ENGINE_HALTED (1U)
+
+/*
+ * ENGINE_MEMCPY: Read and Write transactions
+ * ENGINE_RAND48: Only Write transactions: Source address not required
+ * ENGINE_SUM64: Only read transactions: Target address not required
+ */
+#define ENGINE_MEMCPY (2U)
+#define ENGINE_RAND48 (3U)
+#define ENGINE_SUM64 (4U)
+#define ENGINE_ERROR (0xFFFFFFFFU)
+#define ENGINE_MIS_CFG (ENGINE_ERROR - 1)
+
+/*
+ * Refer to:
+ * https://developer.arm.com/documentation/100964/1111-00/Trace-components/SMMUv3TestEngine---trace
+ */
+
+/* Offset of various control fields belonging to User Frame */
+#define CMD_OFF (0x0U)
+#define UCTRL_OFF (0x4U)
+#define SEED_OFF (0x24U)
+#define BEGIN_OFF (0x28U)
+#define END_CTRL_OFF (0x30U)
+#define STRIDE_OFF (0x38U)
+#define UDATA_OFF (0x40U)
+
+/* Offset of various control fields belonging to PRIV Frame */
+#define PCTRL_OFF (0x0U)
+#define DOWNSTREAM_PORT_OFF (0x4U)
+#define STREAM_ID_OFF (0x8U)
+#define SUBSTREAM_ID_OFF (0xCU)
diff --git a/spm/cactus/plat/arm/fvp/fdts/cactus.dts b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
index eb569f7..1c28fde 100644
--- a/spm/cactus/plat/arm/fvp/fdts/cactus.dts
+++ b/spm/cactus/plat/arm/fvp/fdts/cactus.dts
@@ -62,6 +62,24 @@
pages-count = <4>;
attributes = <0x7>; /* read-write-execute */
};
+
+ /*
+ * Scratch memory used for the purpose of testing SMMUv3 driver
+ * through Cactus SP
+ */
+ smmuv3-memcpy-src {
+ description = "smmuv3-memcpy-source";
+ pages-count = <4>;
+ base-address = <0x00000000 0x7400000>;
+ attributes = <0x3>; /* read-write */
+ };
+
+ smmuv3-memcpy-dst {
+ description = "smmuv3-memcpy-destination";
+ pages-count = <4>;
+ base-address = <0x00000000 0x7404000>;
+ attributes = <0x3>; /* read-write */
+ };
};
device-regions {
@@ -73,6 +91,20 @@
attributes = <0x3>; /* read-write */
};
+ smmuv3-testengine {
+ /*
+ * SMMUv3TestEngine is a DMA IP modeled in the
+ * Base-RevC FVP Model.
+ * User Frame: 0x2bfe0000
+ * Privileged Frame: 0x2bff0000
+ */
+ base-address = <0x00000000 0x2bfe0000>;
+ pages-count = <32>; /* Two 64KB pages */
+ attributes = <0x3>; /* read-write */
+ smmu-id = <0>;
+ stream-ids = <0x0 0x1>;
+ };
+
test-reg {
/* Dummy Values */
base-address = <0x00000000 0x22000000>;
diff --git a/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h b/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
index b4c57ef..8940c83 100644
--- a/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
+++ b/spm/cactus/plat/arm/fvp/include/cactus_platform_def.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020, Arm Limited. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -18,8 +18,16 @@
#define PLAT_CACTUS_RX_BASE ULL(0x7300000)
#define PLAT_CACTUS_CORE_COUNT (8U)
+/* Scratch memory used for SMMUv3 driver testing purposes in Cactus SP */
+#define PLAT_CACTUS_MEMCPY_BASE ULL(0x7400000)
+#define PLAT_CACTUS_MEMCPY_RANGE ULL(0x8000)
+
#define CACTUS_PRIMARY_EC_COUNT (8U)
#define CACTUS_SECONDARY_EC_COUNT (8U)
#define CACTUS_TERTIARY_EC_COUNT (1U)
+/* Base address of user and PRIV frames in SMMUv3TestEngine */
+#define USR_BASE_FRAME ULL(0x2BFE0000)
+#define PRIV_BASE_FRAME ULL(0x2BFF0000)
+
#endif /* CACTUS_PLATFORM_DEF_H */