xlat v2: Alignment tests

Change-Id: I6a689998a74f7841c668ccbb4be7b03d36b355b2
Signed-off-by: Antonio Nino Diaz <antonio.ninodiaz@arm.com>
diff --git a/tftf/tests/tests-xlat-v2.xml b/tftf/tests/tests-xlat-v2.xml
index 7eeceab..e8cf4c7 100644
--- a/tftf/tests/tests-xlat-v2.xml
+++ b/tftf/tests/tests-xlat-v2.xml
@@ -11,6 +11,7 @@
     <testsuite name="Translation tables library v2 tests"
                description="Unit tests to validate the translation tables library v2">
     <testcase name="xlat v2: Basic tests" function="xlat_lib_v2_basic_test" />
+    <testcase name="xlat v2: Alignment tests" function="xlat_lib_v2_alignment_test" />
     <testcase name="xlat v2: Stress test" function="xlat_lib_v2_stress_test" />
   </testsuite>
 
diff --git a/tftf/tests/xlat_lib_v2/xlat_lib_v2_tests.c b/tftf/tests/xlat_lib_v2/xlat_lib_v2_tests.c
index 6147abf..a62bb91 100644
--- a/tftf/tests/xlat_lib_v2/xlat_lib_v2_tests.c
+++ b/tftf/tests/xlat_lib_v2/xlat_lib_v2_tests.c
@@ -37,6 +37,23 @@
 #define MASK_L2		XLAT_BLOCK_MASK(2)
 #define MASK_L3		XLAT_BLOCK_MASK(3)
 
+static const struct {
+	size_t size;
+	size_t expected_va_mask;
+} mem_tests[] = {
+	{ SIZE_L1 + 2 * SIZE_L2 + 2 * SIZE_L3,	MASK_L3 },
+	{ SIZE_L1 + SIZE_L2 + SIZE_L3,		MASK_L3 },
+	{ SIZE_L1 + 2 * SIZE_L2,		MASK_L2 },
+	{ SIZE_L1 + SIZE_L2,			MASK_L2 },
+	{ SIZE_L1 + 2 * SIZE_L3,		MASK_L3 },
+	{ SIZE_L1 + SIZE_L3,			MASK_L3 },
+	{ SIZE_L1,				MASK_L1 },
+	{ SIZE_L2 + 2 * SIZE_L3,		MASK_L3 },
+	{ SIZE_L2 + SIZE_L3,			MASK_L3 },
+	{ SIZE_L2,				MASK_L2 },
+	{ SIZE_L3,				MASK_L3 }
+};
+
 /*
  * Translate the given virtual address into a physical address in the current
  * translation regime. Returns the resulting physical address on success,
@@ -571,6 +588,187 @@
 }
 
 /**
+ * @Test_Aim@ Perform dynamic translation tables API alignment tests
+ *
+ * This test makes sure that the alloc VA APIs return addresses aligned as
+ * expected.
+ */
+test_result_t xlat_lib_v2_alignment_test(void)
+{
+	uintptr_t memory_base_va;
+	unsigned long long memory_base_pa;
+	int rc, i;
+
+	/*
+	 * 1) Try to allocate an invalid region. It should fail, but it will
+	 * return the address of memory that can be used for the following
+	 * tests.
+	 */
+
+	rc = add_region_alloc_va(0, &memory_base_va, SIZE_MAX, MT_DEVICE);
+	if (rc == 0) {
+		tftf_testcase_printf("%d: add_region_alloc_va() didn't fail\n",
+				     __LINE__);
+
+		return TEST_RESULT_FAIL;
+	}
+
+	/*
+	 * Get address of memory region over the max used VA that is aligned to
+	 * a L1 block for the next tests.
+	 */
+	memory_base_pa = (memory_base_va + SIZE_L1 - 1ULL) & ~MASK_L1;
+
+	INFO("Using 0x%llx as base address for tests.\n", memory_base_pa);
+
+	/*
+	 * 2) Try to allocate regions that have an unaligned base VA or PA, or
+	 * a size that isn't multiple of PAGE_SIZE.
+	 */
+
+	rc = add_region(memory_base_va + 1, memory_base_va, PAGE_SIZE, MT_DEVICE);
+	if (rc != -EINVAL) {
+		tftf_testcase_printf("%d: add_region: %d\n", __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+
+	rc = add_region(memory_base_va, memory_base_va + 1, PAGE_SIZE, MT_DEVICE);
+	if (rc != -EINVAL) {
+		tftf_testcase_printf("%d: add_region: %d\n", __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+
+	rc = add_region(memory_base_va, memory_base_va, PAGE_SIZE + 1, MT_DEVICE);
+	if (rc != -EINVAL) {
+		tftf_testcase_printf("%d: add_region: %d\n", __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+
+#if AARCH64
+	/*
+	 * 3) Try to allocate at least 1 GB aligned. There is only room for this
+	 * in AArch64.
+	 */
+
+	rc = add_region_alloc_va(memory_base_pa, &memory_base_va, SIZE_L1,
+				 MT_DEVICE);
+	if (rc == -ENOMEM) {
+		tftf_testcase_printf("%d: Not enough memory\n", __LINE__);
+		return TEST_RESULT_FAIL;
+	} else if (rc != 0) {
+		tftf_testcase_printf("%d: add_region_alloc_va: %d\n",
+				     __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+
+	rc = remove_region(memory_base_va, SIZE_L1);
+	if (rc != 0) {
+		tftf_testcase_printf("%d: remove_region: %d\n", __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+#else
+	/*
+	 * 4) Try to allocate an absurdly large amount of misaligned memory,
+	 * which should fail. In AArch64 there's enough memory to map 4GB of
+	 * virtual memory so skip it.
+	 */
+	rc = add_region_alloc_va(memory_base_pa + PAGE_SIZE, &memory_base_va,
+		 PLAT_VIRT_ADDR_SPACE_SIZE - (memory_base_pa + PAGE_SIZE),
+		 MT_DEVICE);
+	if (rc != -ENOMEM) {
+		tftf_testcase_printf("%d: add_region_alloc_va: %d\n",
+				     __LINE__, rc);
+		return TEST_RESULT_FAIL;
+	}
+#endif
+
+	/*
+	 * 5) Try to allocate hand-picked regions of different sizes and make
+	 * sure that the resulting address is aligned to the correct boundary.
+	 */
+
+	for (i = 0; i < ARRAY_SIZE(mem_tests); i++) {
+		/* Allocate to a correct PA boundary. */
+
+		unsigned long long base_pa = memory_base_pa;
+
+		rc = add_region_alloc_va(base_pa, &memory_base_va,
+					 mem_tests[i].size, MT_DEVICE);
+		if ((rc == -ENOMEM) || (rc == -ERANGE)) {
+			/*
+			 * Skip regions that are too big for the address space.
+			 * This is a problem specially in AArch32, when the max
+			 * virtual address space width is 32 bit.
+			 */
+			WARN("%d: Not enough memory for case %d\n",
+			     __LINE__, i);
+			continue;
+		} else if (rc != 0) {
+			tftf_testcase_printf("%d: add_region_alloc_va: %d\n",
+					     __LINE__, rc);
+			return TEST_RESULT_FAIL;
+		}
+
+		rc = remove_region(memory_base_va, mem_tests[i].size);
+		if (rc != 0) {
+			tftf_testcase_printf("%d: remove_region: %d\n",
+					     __LINE__, rc);
+			return TEST_RESULT_FAIL;
+		}
+
+		if (memory_base_va & mem_tests[i].expected_va_mask) {
+			tftf_testcase_printf("%d: Invalid alignment for case %d\n",
+					     __LINE__, i);
+			return TEST_RESULT_FAIL;
+		}
+
+		/*
+		 * Try to allocate to an incorrect PA boundary (a smaller one).
+		 * This only makes sense for regions that are aligned to
+		 * boundaries bigger than 4 KB, as there cannot be an incorrect
+		 * alignment for 4 KB aligned regions.
+		 */
+
+		if (mem_tests[i].expected_va_mask != MASK_L3) {
+
+			base_pa = memory_base_pa;
+
+			if (mem_tests[i].expected_va_mask == MASK_L1) {
+				base_pa += SIZE_L2;
+			} else if (mem_tests[i].expected_va_mask == MASK_L2) {
+				base_pa += SIZE_L3;
+			}
+
+			rc = add_region_alloc_va(base_pa, &memory_base_va,
+						 mem_tests[i].size, MT_DEVICE);
+			if (rc == 0) {
+
+				rc = remove_region(memory_base_va,
+						   mem_tests[i].size);
+				if (rc != 0) {
+					tftf_testcase_printf("%d: remove_region: %d\n",
+							     __LINE__, rc);
+					return TEST_RESULT_FAIL;
+				}
+
+			} else if ((rc != -ENOMEM) && (rc != -ERANGE)) {
+				/*
+				 * It could happen that we run out of memory, so
+				 * it doesn't make sense to fail because of
+				 * that. However, any other error is a
+				 * legitimate error.
+				 */
+				tftf_testcase_printf("%d: add_region_alloc_va: %d\n",
+						     __LINE__, rc);
+				return TEST_RESULT_FAIL;
+			}
+		}
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/**
  * @Test_Aim@ Perform dynamic translation tables API stress tests
  *
  * This test performs a stress test in the library APIs.