refactor(smmu): secure side DMA rand and memcpy

Add operation, source and size parameters to the cactus command
initiating an operation to the DMA test engine through the SMMU.
Add RAND48 operation support to the DMA test engine.
Refactor to a common run_testengine helper exercised by MEMCPY
and RAND48 SMMU operations.
Cactus SP dispatches either one of the operation based on the
operation id passed from TFTF.

Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
Change-Id: I2ca7c303e8fef6baf99fe4b64b33102546b9ff30
diff --git a/include/runtime_services/cactus_test_cmds.h b/include/runtime_services/cactus_test_cmds.h
index 5f3d0d2..af5d066 100644
--- a/include/runtime_services/cactus_test_cmds.h
+++ b/include/runtime_services/cactus_test_cmds.h
@@ -394,10 +394,12 @@
 #define CACTUS_DMA_SMMUv3_CMD           (0x534d4d55)
 
 static inline struct ffa_value cactus_send_dma_cmd(
-	ffa_id_t source, ffa_id_t dest)
+	ffa_id_t source, ffa_id_t dest, uint32_t operation,
+	uintptr_t base, size_t range, uint32_t attributes)
 {
-	return cactus_send_cmd(source, dest, CACTUS_DMA_SMMUv3_CMD, 0, 0, 0,
-			       0);
+	return cactus_send_cmd(source, dest, CACTUS_DMA_SMMUv3_CMD,
+			       (uint64_t)operation, (uint64_t)base,
+			       (uint64_t)range, attributes);
 }
 
 /*
diff --git a/spm/cactus/cactus_tests/cactus_tests_smmuv3.c b/spm/cactus/cactus_tests/cactus_tests_smmuv3.c
index 3e6740b..5308d93 100644
--- a/spm/cactus/cactus_tests/cactus_tests_smmuv3.c
+++ b/spm/cactus/cactus_tests/cactus_tests_smmuv3.c
@@ -1,13 +1,14 @@
 /*
- * Copyright (c) 2021-2022, Arm Limited. All rights reserved.
+ * Copyright (c) 2021-2023, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <stdint.h>
 
-#include <arch_helpers.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"
@@ -19,63 +20,33 @@
 #include "sp_tests.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)
+static bool run_testengine(uint32_t operation, uintptr_t source_addr,
+			   uintptr_t target_addr, size_t transfer_size,
+			   uint32_t attributes)
 {
-	uint64_t source_addr, cpy_range, target_addr;
-	uint64_t begin_addr, end_addr, dest_addr;
+	const uint32_t streamID_list[] = { 0U, 1U };
+	uintptr_t begin_addr;
+	uintptr_t end_addr;
+	uintptr_t dest_addr;
 	uint32_t status;
-	unsigned int i, f, attempts;
+	uint32_t f;
+	uint32_t 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();
+	assert(operation == ENGINE_MEMCPY || operation == ENGINE_RAND48);
 
 	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);
+		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);
@@ -84,7 +55,12 @@
 		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_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);
 
@@ -92,8 +68,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);
+		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
@@ -101,17 +77,18 @@
 		 * invalid.
 		 */
 		if (mmio_read32_offset(USR_BASE_FRAME + F_IDX(f), CMD_OFF) == ENGINE_MIS_CFG) {
-			ERROR("SMMUv3TestEngine: Misconfigured for frame: %u\n", f);
+			ERROR("SMMUv3TestEngine: misconfigured for frame: %u\n", f);
 			return false;
 		}
 
-		/* Wait for mem copy to be complete */
+		/* 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\n");
+				ERROR("SMMUv3: test failed, engine error.\n");
 				return false;
 			}
 
@@ -123,38 +100,70 @@
 		}
 
 		if (attempts == LOOP_COUNT) {
-			ERROR("SMMUv3: Test failed\n");
+			ERROR("SMMUv3: test failed, exceeded max. wait loop.\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);
+	return true;
+}
 
-	/* 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;
+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 true;
+	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)
 {
-	struct ffa_value ffa_ret;
 	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");
+	VERBOSE("Received request through direct message for DMA service.\n");
 
 	/*
 	 * At present, the test cannot be run concurrently on multiple SPs as
@@ -165,11 +174,21 @@
 		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);
+	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 ffa_ret;
+	return cactus_error_resp(vm_id, source, 0);
 }
diff --git a/spm/cactus/cactus_tests/smmuv3_test_engine.h b/spm/cactus/cactus_tests/smmuv3_test_engine.h
index 32d86ac..d3a3dcf 100644
--- a/spm/cactus/cactus_tests/smmuv3_test_engine.h
+++ b/spm/cactus/cactus_tests/smmuv3_test_engine.h
@@ -32,6 +32,7 @@
 /* Offset of various control fields belonging to User Frame */
 #define CMD_OFF		(0x0U)
 #define UCTRL_OFF	(0x4U)
+#define ATTR_OFF	(0x20U)
 #define SEED_OFF	(0x24U)
 #define BEGIN_OFF	(0x28U)
 #define END_CTRL_OFF	(0x30U)
diff --git a/spm/cactus/plat/arm/fvp/include/sp_platform_def.h b/spm/cactus/plat/arm/fvp/include/sp_platform_def.h
index 0b23189..058209c 100644
--- a/spm/cactus/plat/arm/fvp/include/sp_platform_def.h
+++ b/spm/cactus/plat/arm/fvp/include/sp_platform_def.h
@@ -25,8 +25,9 @@
 #define CACTUS_PL011_UART_CLK_IN_HZ	PL011_UART2_CLK_IN_HZ
 
 /* 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 PLAT_CACTUS_MEMCPY_BASE			ULL(0x7400000)
+#define PLAT_CACTUS_SMMU_NS_MEMCPY_BASE		ULL(0x90000000)
+#define PLAT_CACTUS_MEMCPY_RANGE		ULL(0x8000)
 
 /* Base address of user and PRIV frames in SMMUv3TestEngine */
 #define USR_BASE_FRAME			ULL(0x2BFE0000)
diff --git a/tftf/tests/runtime_services/secure_service/test_spm_smmu.c b/tftf/tests/runtime_services/secure_service/test_spm_smmu.c
index 238feae..db76334 100644
--- a/tftf/tests/runtime_services/secure_service/test_spm_smmu.c
+++ b/tftf/tests/runtime_services/secure_service/test_spm_smmu.c
@@ -11,29 +11,73 @@
 #include <spm_test_helpers.h>
 #include <test_helpers.h>
 
+#if PLAT_fvp || PLAT_tc
+#include <sp_platform_def.h>
 static const struct ffa_uuid expected_sp_uuids[] = { {PRIMARY_UUID} };
+#endif
+
+#define TEST_DMA_ENGINE_MEMCPY	(2U)
+#define TEST_DMA_ENGINE_RAND48	(3U)
+
+#define TEST_DMA_ENGINE_ATTR_DEST_ACACHE_RAWAWB_S	(0xffU)
 
 /**************************************************************************
- * Send a command to SP1 initiate DMA service with the help of a peripheral
- * device upstream of an SMMUv3 IP
+ * test_smmu_spm
+ *
+ * Send commands to SP1 initiate DMA service with the help of a peripheral
+ * device upstream of an SMMUv3 IP.
+ * The scenario involves randomizing a secure buffer (first DMA operation),
+ * copying this buffer to another location (second DMA operation),
+ * and checking (by CPU) that both buffer contents match.
  **************************************************************************/
 test_result_t test_smmu_spm(void)
 {
+#if PLAT_fvp || PLAT_tc
 	struct ffa_value ret;
 
 	/**********************************************************************
 	 * Check SPMC has ffa_version and expected FFA endpoints are deployed.
 	 **********************************************************************/
-	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);
+	CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
 
-	VERBOSE("Sending command to SP %x for initiating DMA transfer\n",
+	VERBOSE("Sending command to SP %x for initiating DMA transfer.\n",
 			SP_ID(1));
-	ret = cactus_send_dma_cmd(HYP_ID, SP_ID(1));
 
+	/*
+	 * Randomize first half of a secure buffer from the secure world
+	 * through the SMMU test engine DMA.
+	 * Destination memory attributes are secure rawaWB.
+	 */
+	ret = cactus_send_dma_cmd(HYP_ID, SP_ID(1),
+		TEST_DMA_ENGINE_RAND48,
+		PLAT_CACTUS_MEMCPY_BASE,
+		PLAT_CACTUS_MEMCPY_RANGE / 2,
+		TEST_DMA_ENGINE_ATTR_DEST_ACACHE_RAWAWB_S << 16);
+
+	/* Expect the SMMU DMA operation to pass. */
+	if (cactus_get_response(ret) != CACTUS_SUCCESS) {
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Copy first half to second half of the buffer and
+	 * check both match.
+	 * Source and destination memory attributes are secure rawaWB.
+	 */
+	ret = cactus_send_dma_cmd(HYP_ID, SP_ID(1),
+		TEST_DMA_ENGINE_MEMCPY,
+		PLAT_CACTUS_MEMCPY_BASE,
+		PLAT_CACTUS_MEMCPY_RANGE,
+		(TEST_DMA_ENGINE_ATTR_DEST_ACACHE_RAWAWB_S << 16) |
+		TEST_DMA_ENGINE_ATTR_DEST_ACACHE_RAWAWB_S);
+
+	/* Expect the SMMU DMA operation to pass. */
 	if (cactus_get_response(ret) != CACTUS_SUCCESS) {
 		return TEST_RESULT_FAIL;
 	}
 
 	return TEST_RESULT_SUCCESS;
+#else
+	return TEST_RESULT_SKIPPED;
+#endif
 }
-