TF-RMM Release v0.1.0

This is the first external release of TF-RMM and provides a reference
implementation of Realm Management Monitor (RMM) as specified by the
RMM Beta0 specification[1].

The `docs/readme.rst` has more details about the project and
`docs/getting_started/getting-started.rst` has details on how to get
started with TF-RMM.

[1] https://developer.arm.com/documentation/den0137/1-0bet0/?lang=en

Signed-off-by: Soby Mathew <soby.mathew@arm.com>
Change-Id: I205ef14c015e4a37ae9ae1a64e4cd22eb8da746e
diff --git a/lib/xlat/src/xlat_tables_utils.c b/lib/xlat/src/xlat_tables_utils.c
new file mode 100644
index 0000000..93cda40
--- /dev/null
+++ b/lib/xlat/src/xlat_tables_utils.c
@@ -0,0 +1,517 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
+ * SPDX-FileCopyrightText: Copyright Arm Limited and Contributors.
+ */
+
+/* This file is derived from xlat_table_v2 library in TF-A project */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <utils_def.h>
+#include <xlat_contexts.h>
+#include "xlat_defs_private.h"
+#include <xlat_tables.h>
+#include "xlat_tables_private.h"
+
+#if LOG_LEVEL < LOG_LEVEL_VERBOSE
+
+void xlat_mmap_print(const struct xlat_ctx *ctx)
+{
+	(void)ctx;
+
+	/* Empty */
+}
+
+void xlat_tables_print(struct xlat_ctx *ctx)
+{
+	(void)ctx;
+
+	/* Empty */
+}
+
+#else /* if LOG_LEVEL >= LOG_LEVEL_VERBOSE */
+
+void xlat_mmap_print(const struct xlat_ctx *ctx)
+{
+	VERBOSE("mmap:\n");
+
+	for (unsigned int i = 0U; i < ctx->cfg->mmap_num; i++) {
+		uintptr_t base_va;
+
+		base_va = ((ctx->cfg->region == VA_LOW_REGION) ?
+				ctx->cfg->mmap[i].base_va :
+				(ctx->cfg->mmap[i].base_va
+					+ ctx->cfg->base_va));
+		if (MT_TYPE(ctx->cfg->mmap[i].attr) != MT_TRANSIENT) {
+			VERBOSE(" VA:0x%lx  PA:0x%lx  size:0x%zx  attr:0x%lx  granularity:0x%zx\n",
+			       base_va, ctx->cfg->mmap[i].base_pa,
+			       ctx->cfg->mmap[i].size, ctx->cfg->mmap[i].attr,
+			       ctx->cfg->mmap[i].granularity);
+		} else {
+			VERBOSE(" VA:0x%lx  PA: TRANSIENT  size:0x%zx  granularity:0x%zx\n",
+			       base_va, ctx->cfg->mmap[i].size,
+			       ctx->cfg->mmap[i].granularity);
+		}
+	};
+	VERBOSE("\n");
+}
+
+/* Print the attributes of the specified block descriptor. */
+static void xlat_desc_print(uint64_t desc)
+{
+	uint64_t mem_type_index = ATTR_INDEX_GET(desc);
+
+	if (mem_type_index == ATTR_IWBWA_OWBWA_NTR_INDEX) {
+		VERBOSE("MEM");
+	} else if (mem_type_index == ATTR_NON_CACHEABLE_INDEX) {
+		VERBOSE("NC");
+	} else {
+		if (mem_type_index != ATTR_DEVICE_INDEX) {
+			/* Unsupported memory type */
+			panic();
+		}
+		VERBOSE("DEV");
+	}
+
+	VERBOSE(((desc & LOWER_ATTRS(AP_RO)) != 0ULL) ? "-RO" : "-RW");
+	VERBOSE(((desc & UPPER_ATTRS(PXN)) != 0ULL) ? "-PXN" : "-PEXEC");
+	VERBOSE(((desc & UPPER_ATTRS(XN)) != 0ULL) ? "-XN" : "-EXEC");
+
+	if ((desc & LOWER_ATTRS(NS)) == 0ULL) {
+		VERBOSE("-RL");
+	} else {
+		VERBOSE("-N");
+	}
+
+	/* Check Guarded Page bit */
+	if ((desc & GP) != 0ULL) {
+		VERBOSE("-GP");
+	}
+}
+
+static const char * const level_spacers[] = {
+	"[LV0] ",
+	"  [LV1] ",
+	"    [LV2] ",
+	"      [LV3] "
+};
+
+static const char *invalid_descriptors_ommited =
+		"%s(%d invalid descriptors omitted)\n";
+
+/*
+ * Recursive function that reads the translation tables passed as an argument
+ * and prints their status.
+ */
+static void xlat_tables_print_internal(struct xlat_ctx *ctx, uintptr_t table_base_va,
+		const uint64_t *table_base, unsigned int table_entries,
+		unsigned int level)
+{
+	uint64_t *addr_inner;
+	unsigned int invalid_row_count;
+	unsigned int table_idx = 0U;
+	size_t level_size;
+	uintptr_t table_idx_va;
+
+	if (level > XLAT_TABLE_LEVEL_MAX) {
+		/* Level out of bounds */
+		panic();
+	}
+
+	assert((ctx != NULL) &&
+	       (ctx->cfg != NULL) &&
+	       (ctx->tbls != NULL));
+
+	level_size = XLAT_BLOCK_SIZE(level);
+	table_idx_va = (ctx->cfg->region == (VA_LOW_REGION) ?
+			(table_base_va) :
+			(table_base_va + ctx->cfg->base_va));
+
+	/*
+	 * Keep track of how many invalid descriptors are counted in a row.
+	 * Whenever multiple invalid descriptors are found, only the first one
+	 * is printed, and a line is added to inform about how many descriptors
+	 * have been omitted.
+	 */
+	invalid_row_count = 0U;
+
+	while (table_idx < table_entries) {
+		uint64_t desc;
+
+		desc = table_base[table_idx];
+
+		if ((desc & DESC_MASK) == INVALID_DESC) {
+
+			if (invalid_row_count == 0U) {
+				VERBOSE("%sVA:0x%lx size:0x%zx\n",
+					level_spacers[level],
+					table_idx_va, level_size);
+			}
+			invalid_row_count++;
+
+		} else {
+
+			if (invalid_row_count > 1U) {
+				VERBOSE(invalid_descriptors_ommited,
+				       level_spacers[level],
+				       invalid_row_count - 1U);
+			}
+			invalid_row_count = 0U;
+
+			/*
+			 * Check if this is a table or a block. Tables are only
+			 * allowed in levels other than 3, but DESC_PAGE has the
+			 * same value as DESC_TABLE, so we need to check.
+			 */
+			if (((desc & DESC_MASK) == TABLE_DESC) &&
+					(level < XLAT_TABLE_LEVEL_MAX)) {
+				/*
+				 * Do not print any PA for a table descriptor,
+				 * as it doesn't directly map physical memory
+				 * but instead points to the next translation
+				 * table in the translation table walk.
+				 */
+				VERBOSE("%sVA:0x%lx size:0x%zx\n",
+				       level_spacers[level],
+				       table_idx_va, level_size);
+
+				addr_inner = (uint64_t *)(void *)(desc & TABLE_ADDR_MASK);
+
+				/* FIXME: Recursion. */
+				xlat_tables_print_internal(ctx, table_idx_va,
+					addr_inner, XLAT_TABLE_ENTRIES,
+					level + 1U);
+			} else {
+				VERBOSE("%sVA:0x%lx PA:0x%lx size:0x%zx ",
+				       level_spacers[level], table_idx_va,
+				       (uint64_t)(desc & TABLE_ADDR_MASK),
+				       level_size);
+				xlat_desc_print(desc);
+				VERBOSE("\n");
+			}
+		}
+
+		table_idx++;
+		table_idx_va += level_size;
+	}
+
+	if (invalid_row_count > 1U) {
+		VERBOSE(invalid_descriptors_ommited,
+			level_spacers[level], invalid_row_count - 1U);
+	}
+}
+
+void xlat_tables_print(struct xlat_ctx *ctx)
+{
+	unsigned int used_page_tables;
+	struct xlat_ctx_cfg *ctx_cfg = ctx->cfg;
+
+	assert(ctx_cfg != NULL);
+
+	uintptr_t max_mapped_va_offset = (ctx_cfg->region == (VA_LOW_REGION) ?
+			(ctx_cfg->max_mapped_va_offset) :
+			(ctx_cfg->max_mapped_va_offset + ctx_cfg->base_va));
+	uintptr_t max_allowed_va = (ctx_cfg->region == (VA_LOW_REGION) ?
+			(ctx_cfg->max_va_size) :
+			(ctx_cfg->max_va_size + ctx_cfg->base_va));
+
+	VERBOSE("Translation tables state:\n");
+	VERBOSE("  Max allowed PA:  0x%lx\n", xlat_arch_get_max_supported_pa());
+	VERBOSE("  Max allowed VA:  0x%lx\n", max_allowed_va);
+	VERBOSE("  Max mapped PA:   0x%lx", ctx_cfg->max_mapped_pa);
+	for (unsigned int i = 0U; i < ctx_cfg->mmap_num; i++) {
+		if (ctx_cfg->mmap[i].attr == MT_TRANSIENT) {
+			/*
+			 * If there is a transient region on this context, we
+			 * do not know what will be the highest PA, so print a
+			 * note on the log.
+			 */
+			VERBOSE(" - Estimated (transient region)");
+			break;
+		}
+	}
+	VERBOSE("\n");
+	VERBOSE("  Max mapped VA:   0x%lx\n", max_mapped_va_offset);
+
+	VERBOSE("  Initial lookup level: %u\n", ctx_cfg->base_level);
+	VERBOSE("  Entries @initial lookup level: %u\n",
+					ctx->tbls->max_base_table_entries);
+
+	used_page_tables = ctx->tbls->next_table;
+	VERBOSE("  Used %d tables out of %d (spare: %d)\n",
+				used_page_tables, ctx->tbls->tables_num,
+				ctx->tbls->tables_num - used_page_tables);
+
+	xlat_tables_print_internal(ctx, 0U, ctx->tbls->base_table,
+				   ctx->tbls->max_base_table_entries,
+				   ctx_cfg->base_level);
+}
+
+#endif /* LOG_LEVEL >= LOG_LEVEL_VERBOSE */
+
+/*
+ * Do a translation table walk to find the last level table that maps
+ * virtual_addr.
+ *
+ * On success, return the address of the last level table within the
+ * translation table. Its lookup level is stored in '*out_level'.
+ * On error, return NULL.
+ */
+static uint64_t *find_xlat_last_table(uintptr_t virtual_addr,
+				      const struct xlat_ctx * const ctx,
+				      unsigned int * const out_level)
+{
+	unsigned int start_level;
+	uint64_t *ret_table;
+	unsigned int entries;
+	struct xlat_ctx_tbls *ctx_tbls;
+	struct xlat_ctx_cfg *ctx_cfg;
+
+
+	assert(ctx != NULL);
+	assert(ctx->cfg != NULL);
+	assert(ctx->tbls != NULL);
+	assert(out_level != NULL);
+
+	ctx_tbls = ctx->tbls;
+	ctx_cfg = ctx->cfg;
+	start_level = ctx_cfg->base_level;
+	ret_table = ctx_tbls->base_table;
+	entries = ctx_tbls->max_base_table_entries;
+
+	for (unsigned int level = start_level;
+	     level <= XLAT_TABLE_LEVEL_MAX;
+	     level++) {
+		unsigned int idx;
+		uint64_t desc;
+		uint64_t desc_type;
+
+		idx = XLAT_TABLE_IDX(virtual_addr, level);
+		if (idx >= entries) {
+			WARN("Missing xlat table entry at address 0x%lx\n",
+			      virtual_addr);
+			return NULL;
+		}
+
+		desc = ret_table[idx];
+		desc_type = desc & DESC_MASK;
+
+		if (desc_type != TABLE_DESC) {
+			if (((desc_type == BLOCK_DESC) ||
+			    (((desc_type == PAGE_DESC) || (desc_type == INVALID_DESC))
+			      && (level == XLAT_TABLE_LEVEL_MAX)))) {
+				*out_level = level;
+				return ret_table;
+			}
+			return NULL;
+		}
+
+		ret_table = (uint64_t *)(void *)(desc & TABLE_ADDR_MASK);
+		entries = XLAT_TABLE_ENTRIES;
+	}
+
+	/*
+	 * This shouldn't be reached, the translation table walk should end at
+	 * most at level XLAT_TABLE_LEVEL_MAX and return from inside the loop
+	 * but we need this to avoid MISRA problems.
+	 */
+	return NULL;
+}
+
+/*****************************************************************************
+ * Public part of the utility library for translation tables.
+ ****************************************************************************/
+
+/*
+ * Function to unmap a physical memory page from the descriptor entry and
+ * VA given.
+ * This function implements the "Break" part of the Break-Before-Make semantics
+ * needed by the Armv8.x architecture in order to update the page descriptors.
+ *
+ * This function returns 0 on success or an error code otherwise.
+ *
+ * For simplicity, this function will not take into consideration holes on the
+ * table pointed by entry, as long as va belongs to the VA space owned by the
+ * context.
+ */
+int xlat_unmap_memory_page(struct xlat_table_entry * const table,
+			   const uintptr_t va)
+{
+	uint64_t *entry;
+
+	assert(table != NULL);
+
+	entry = xlat_get_pte_from_table(table, va);
+
+	if (entry == NULL) {
+		return -EFAULT;
+	}
+
+	/*
+	 * No need to perform any checks on this page descriptor as it is going
+	 * to be made invalid anyway.
+	 */
+	xlat_write_descriptor(entry, INVALID_DESC);
+
+	/* Invalidate any cached copy of this mapping in the TLBs. */
+	xlat_arch_tlbi_va(va);
+
+	/* Ensure completion of the invalidation. */
+	xlat_arch_tlbi_va_sync();
+
+	return 0;
+}
+
+/*
+ * Function to map a physical memory page from the descriptor table entry
+ * and VA given. This function implements the "Make" part of the
+ * Break-Before-Make semantics needed by the armv8.x architecture in order
+ * to update the page descriptors.
+ *
+ * This function eturns 0 on success or an error code otherwise.
+ *
+ * For simplicity, this function will not take into consideration holes on the
+ * table pointed by entry, as long as va belongs to the VA space owned by the
+ * context.
+ */
+int xlat_map_memory_page_with_attrs(const struct xlat_table_entry * const table,
+				    const uintptr_t va,
+				    const uintptr_t pa,
+				    const uint64_t attrs)
+{
+	uint64_t desc;
+	uint64_t *desc_ptr;
+
+	assert(table != NULL);
+
+	desc_ptr = xlat_get_pte_from_table(table, va);
+
+	if (desc_ptr == NULL) {
+		return -EFAULT;
+	}
+
+	/* This function must only be called on invalid descriptors */
+	if (xlat_read_descriptor(desc_ptr) != INVALID_DESC) {
+		return -EFAULT;
+	}
+
+	/* Check that pa is within boundaries */
+	if (pa > xlat_arch_get_max_supported_pa()) {
+		return -EFAULT;
+	}
+
+	/* Generate the new descriptor */
+	desc = xlat_desc(attrs, pa, table->level);
+
+	xlat_write_descriptor(desc_ptr, desc);
+
+	/* Ensure the translation table write has drained into memory */
+	dsb(ishst);
+	isb();
+
+	return 0;
+}
+
+/*
+ * Return a table entry structure given a context and a VA.
+ * The return structure is populated on the retval field.
+ *
+ * This function returns 0 on success or a Linux error code otherwise.
+ */
+int xlat_get_table_from_va(struct xlat_table_entry * const retval,
+			   const struct xlat_ctx * const ctx,
+			   const uintptr_t va)
+{
+	uintptr_t page_va;
+	uint64_t *table;
+	unsigned int level;
+	struct xlat_ctx_cfg *ctx_cfg;
+
+	assert((ctx != NULL) &&
+	       (ctx->cfg != NULL) &&
+	       (ctx->tbls != NULL) &&
+	       (retval != NULL) &&
+	       (ctx->tbls->initialized == true));
+
+	ctx_cfg = ctx->cfg;
+
+	/* Check if the VA is within the mapped range */
+	if (((va > (ctx_cfg->max_mapped_va_offset + ctx_cfg->base_va))
+		|| (va < ctx_cfg->base_va))) {
+		return -EFAULT;
+	}
+
+	/*
+	 * From the translation tables point of view, the VA is actually an
+	 * offset with regards to the base address of the VA space, so before
+	 * using a VA, we need to extract the base VA from it.
+	 */
+	page_va = va - ctx_cfg->base_va;
+	page_va &= ~PAGE_SIZE_MASK; /* Page address of the VA address passed. */
+
+	table = find_xlat_last_table(page_va, ctx, &level);
+
+	if (table == NULL) {
+		WARN("Address 0x%lx is not mapped.\n", va);
+		return -EFAULT;
+	}
+
+	/* Maximum number of entries used by this table. */
+	if (level == ctx_cfg->base_level) {
+		retval->entries = ctx->tbls->max_base_table_entries;
+	} else {
+		retval->entries = XLAT_TABLE_ENTRIES;
+	}
+
+	retval->table = table;
+	retval->level = level;
+	retval->base_va = ctx_cfg->base_va;
+
+	return 0;
+}
+
+/*
+ * This function finds the descriptor entry on a table given the corresponding
+ * table entry structure and the VA for that descriptor.
+ *
+ * If va is not mapped by the table pointed by entry, it returns NULL.
+ *
+ * For simplicity and as long as va belongs to the VA space owned by the
+ * translation context, this function will not take into consideration holes
+ * on the table pointed by entry either because the address is not mapped by
+ * the caller or left as INVALID_DESC for future dynamic mapping.
+ */
+uint64_t *xlat_get_pte_from_table(const struct xlat_table_entry * const entry,
+				    const uintptr_t va)
+{
+	unsigned int index;
+	uint64_t *table;
+	uintptr_t va_offset;
+
+	assert(entry != NULL);
+
+	if (va < entry->base_va) {
+		return NULL;
+	}
+
+	/*
+	 * From the translation tables point of view, the VA is actually an
+	 * offset with regards to the base address of the VA space, so before
+	 * using a VA, we need to extract the base VA from it.
+	 */
+
+	va_offset = va - entry->base_va;
+	table = entry->table;
+	index = XLAT_TABLE_IDX(va_offset, entry->level);
+
+	if (index >= entry->entries) {
+		return NULL;
+	}
+
+	return &table[index];
+}