gpt: added unit tests for GPT PAS

New tests are added for the Granule Protection Table. The test
added initializes the L0 table structures, walks the L0 table
and creates the PAS (Physical Address Space) regions and uses
them to initialize the L1 tables.

Signed-off-by: Juan Pablo Conde <juanpablo.conde@arm.com>
Co-developed-by: Kathleen Capella <kathleen.capella@arm.com>
Change-Id: I778d141eddeb1b788ef4a79dcbe102fa0d8fc23b
diff --git a/tests/lib/gpt/test_gpt_utils.c b/tests/lib/gpt/test_gpt_utils.c
index 4d076b1..f4c452c 100644
--- a/tests/lib/gpt/test_gpt_utils.c
+++ b/tests/lib/gpt/test_gpt_utils.c
@@ -11,6 +11,7 @@
 #include "stdlib.h"
 #include "stdio.h"
 #include "test_gpt_utils.h"
+#include <time.h>
 
 #define BLOCK_DESC_GPI (ULL(0xF0))
 #define BLOCK_DESC_GPI_SHIFT (4)
@@ -261,3 +262,118 @@
 	return table;
 }
 
+uintptr_t allocate_l1_table_mem(uintptr_t table, size_t l1_mem_size)
+{
+	size_t alignment;
+	int num_entries;
+	uint64_t * entry;
+
+	/* Allocate table space */
+	table = (uintptr_t)aligned_alloc(l1_mem_size,l1_mem_size);
+
+	if(table) {
+		/* Initialize entries as invalid */
+		num_entries = l1_mem_size / L1_ENTRY_SIZE;
+		entry = (uint64_t *)table;
+
+		for (int i = 0; i < num_entries; i++) {
+			entry[i] = INVALID_L1_ENTRY;
+		}
+	}
+
+	return table;
+}
+
+/*
+ * Creates a set of pass regions and returns the number of
+ * regions created and the number of l1 tables that need
+ * to be allocated.
+ */
+pas_regions_info_t create_pas_regions(pas_region_t *pas_regions,
+	unsigned long int pps, unsigned long int pgs, 
+	unsigned long int l0gptsz)
+{	
+	size_t size;
+	unsigned int attrs;
+	int num_l0_entries;
+	int entry_type;
+	bool choose_entry_type = true;
+	int i = 0;
+	unsigned long int remaining_space = pps;
+
+	srand(time(NULL));
+
+	pas_regions_info_t counts = { 
+		.pas_count = 0,
+		.l1_table_count = 0
+	};
+	uintptr_t base_pa = 0x0;
+
+	num_l0_entries = pps/l0gptsz;
+
+	for (i = 0; i < num_l0_entries; ++i) {
+		bool entry_added = false;
+		pas_region_t new_pas_region;
+
+		if (choose_entry_type) {
+			entry_type = i % 2;
+		}
+		choose_entry_type = true;
+
+		/* Choose a GPI encoding for the entry */
+		attrs = GPI_OPTIONS[(int)(i % NUM_GPI_OPTIONS)];
+
+		if (entry_type == 1){ /* BLOCK DESC, no L1 */
+			/*
+			 * If base not on L0 edge, fill in the necessary space
+			 * with a table descriptor so the subsequent block
+			 * descriptor is aligned.
+			 */
+			if (base_pa % l0gptsz != 0) {
+				size = l0gptsz - base_pa % l0gptsz;
+				new_pas_region =
+					(pas_region_t)GPT_MAP_REGION_GRANULE((base_pa),
+						size, attrs);
+				counts.l1_table_count++;
+				entry_added = true;
+				/*
+				 * Prevent the entry type in the next iteration from
+				 * being selected randomly, as it has to be a block
+				 * descriptor.
+				 */
+				choose_entry_type = false;
+			}
+			/*
+			 * If there is space in the protected physical
+			 * memory space, add block descriptor.
+			 */
+			else if (base_pa + l0gptsz <= pps) {
+				/* Create block PAS region on next L0 boundary */
+				size = l0gptsz;
+				new_pas_region =
+					(pas_region_t)GPT_MAP_REGION_BLOCK((base_pa),
+						l0gptsz, attrs);
+				entry_added = true;
+			}
+		}
+		else{ /* TABLE DESC, yes L1 */
+			unsigned long int available_space =
+				remaining_space > l0gptsz ? l0gptsz : remaining_space;
+			size = pgs * 2;
+			new_pas_region =
+				(pas_region_t)GPT_MAP_REGION_GRANULE((base_pa), size,
+					attrs);
+			counts.l1_table_count++;
+			entry_added = true;
+		}
+
+		if (entry_added) {
+			pas_regions[counts.pas_count] = new_pas_region;
+			base_pa += size;
+			counts.pas_count++;
+			remaining_space = pps - base_pa;
+		}
+	}
+
+	return counts;
+}
diff --git a/tests/lib/gpt/test_gpt_utils.h b/tests/lib/gpt/test_gpt_utils.h
index 1c8772e..b310f28 100644
--- a/tests/lib/gpt/test_gpt_utils.h
+++ b/tests/lib/gpt/test_gpt_utils.h
@@ -52,6 +52,11 @@
 bool is_valid_contiguous_desc(uint64_t contiguous_desc);
 bool check_gpi_type(uint64_t gpi, unsigned int expected_type);
 uintptr_t allocate_l0_table_mem(size_t l0_mem_size);
+uintptr_t allocate_l1_table_mem(uintptr_t table, size_t l1_mem_size);
+pas_regions_info_t create_pas_regions(pas_region_t *pas_regions,
+                                      unsigned long int pps,
+                                      unsigned long int pgs,
+                                      unsigned long int l0gptsz);
 
 #ifdef __cplusplus
 }
diff --git a/tests/lib/gpt/test_tables.cpp b/tests/lib/gpt/test_tables.cpp
index 662cab5..57e5700 100644
--- a/tests/lib/gpt/test_tables.cpp
+++ b/tests/lib/gpt/test_tables.cpp
@@ -76,9 +76,7 @@
 	unsigned long l0gptsz;
 	
 	TEST_SETUP() {
-		int i;
-		int j;
-		int k;
+		int i, j, k;
 
 		/* Enable MMU and data caches */
 		enable_mmu();
@@ -278,3 +276,109 @@
 	CHECK(gpt_init_l0_tables(pps_encoding, l0_table,
 		wrong_l0_mem_size) == -ENOMEM);
 }
+
+TEST_GROUP(gpt_init_pas_l1_tables) {
+	TEST_SETUP() {
+		int i, j, k;
+
+		unsigned int num_l0_entries;
+		unsigned int num_l1_tables;
+		unsigned int init_pas_count;
+
+		/* Enable MMU and data caches */
+		enable_mmu();
+
+		uintptr_t base_pa = 0x0;
+
+		/* Get PPS */
+		j = 0;
+		pps = pps_values[j];
+		pps_encoding = pps_encodings[j];
+
+		/* Set the mock PPS */
+		set_pps(pps_encoding);
+
+		/* Get L0GPTSZ */
+		i = 0;
+		l0gptsz = l0gptsz_values[i];
+
+		/* Set the mock L0 page size */
+		set_l0gptsz(l0gptsz_encodings[i]);
+
+		/* Get PGS */
+		k = 0;
+		pgs_encoding = pgs_encodings[k];
+
+		/* Set the mock PGS */
+		set_pgs((gpccr_pgs_e)pgs_encoding);
+
+		/* Allocate memory for L0 table */
+		l0_mem_size = pps >= l0gptsz ? (pps/l0gptsz) * L0_ENTRY_SIZE : L0_ENTRY_SIZE;
+
+		l0_table = allocate_l0_table_mem(l0_mem_size);
+
+		if (!l0_table) {
+			FAIL("Unable to allocate L0 table\n");
+		}
+
+		pgs = SIZE_4KB;
+		pgs_encoding = GPCCR_PGS_4K;
+
+		num_l0_entries = l0_mem_size / L0_ENTRY_SIZE;
+
+		pas_regions = (pas_region_t *)calloc(PAS_COUNT_MAX, sizeof(pas_region_t));
+		if(!pas_regions) {
+			FAIL("Could not allocate pas_regions array\n");
+		}
+
+		counts = create_pas_regions(pas_regions, pps, pgs, l0gptsz);
+
+		/* Calculate size for L1 tables */
+		l1_mem_size = ((l0gptsz / pgs) / 2) * counts.l1_table_count;
+
+		/* Allocate space for L1 tables */
+		l1_table = allocate_l1_table_mem(l1_table, l1_mem_size);
+		if (!l1_table) {
+			FAIL("Unable to allocate L1 table\n");
+		}
+	}
+
+	TEST_TEARDOWN() {
+		if (l0_table) {
+			free((void *)l0_table);
+		}
+		if (l1_table) {
+			free((void *)l1_table);
+		}
+	}
+
+	unsigned long int pps;
+	gpccr_pgs_e pgs_encoding;
+	size_t pgs;
+	gpccr_pps_e pps_encoding;
+	unsigned long l0gptsz;
+	gpccr_l0gptsz_e l0gptsz_encoding;
+	size_t l0_mem_size;
+	size_t l1_mem_size;
+	uintptr_t l0_table;
+	uintptr_t l1_table;
+	pas_regions_info_t counts;
+	pas_region_t *pas_regions;
+};
+
+TEST(gpt_init_pas_l1_tables, init) {
+
+	if (gpt_init_l0_tables(pps_encoding, l0_table,
+		l0_mem_size) != 0) {
+		printf("Failed to init l0 table for pps %lu, l0gptsz %lu",
+			pps, l0gptsz);
+		FAIL("\n");
+	}
+	walk_l0_table(l0_table, l0_mem_size, false);
+	if (gpt_init_pas_l1_tables(pgs_encoding, l1_table, l1_mem_size,
+		pas_regions, counts.pas_count) != 0) {
+		printf("Failed to init l1 table for pps %lu, l0gptsz %lu",
+			pps, l0gptsz);
+		FAIL("\n");
+	}
+}