mm: function to get the mode of a range of memory.
This supersedes mm_vm_is_mapped() since that is checking that the range
was not mapped with MM_MODE_INVALID.
Change-Id: Ic3d5018a6d207d092a7f6bf853b015eba22945bb
diff --git a/src/mm.c b/src/mm.c
index 55340a1..e46902a 100644
--- a/src/mm.c
+++ b/src/mm.c
@@ -625,56 +625,99 @@
}
/**
- * Determines if the given address is valid in the address space of the given
- * page table by recursively traversing all levels of the page table.
+ * Gets the attributes applied to the given range of stage-2 addresses at the
+ * given level.
+ *
+ * The `got_attrs` argument is initially passed as false until `attrs` contains
+ * attributes of the memory region at which point it is passed as true.
+ *
+ * The value returned in `attrs` is only valid if the function returns true.
+ *
+ * Returns true if the whole range has the same attributes and false otherwise.
*/
-static bool mm_is_mapped_recursive(struct mm_page_table *table,
- ptable_addr_t addr, uint8_t level)
+static bool mm_ptable_get_attrs_level(struct mm_page_table *table,
+ ptable_addr_t begin, ptable_addr_t end,
+ uint8_t level, bool got_attrs,
+ uint64_t *attrs)
{
- pte_t pte;
- ptable_addr_t va_level_end = mm_level_end(addr, level);
+ pte_t *pte = &table->entries[mm_index(begin, level)];
+ ptable_addr_t level_end = mm_level_end(begin, level);
+ size_t entry_size = mm_entry_size(level);
- /* It isn't mapped if it doesn't fit in the table. */
- if (addr >= va_level_end) {
- return false;
+ /* Cap end so that we don't go over the current level max. */
+ if (end > level_end) {
+ end = level_end;
}
- pte = table->entries[mm_index(addr, level)];
+ /* Check that each entry is owned. */
+ while (begin < end) {
+ if (arch_mm_pte_is_table(*pte, level)) {
+ if (!mm_ptable_get_attrs_level(
+ mm_page_table_from_pa(
+ arch_mm_table_from_pte(*pte,
+ level)),
+ begin, end, level - 1, got_attrs, attrs)) {
+ return false;
+ }
+ got_attrs = true;
+ } else {
+ if (!got_attrs) {
+ *attrs = arch_mm_pte_attrs(*pte, level);
+ got_attrs = true;
+ } else if (arch_mm_pte_attrs(*pte, level) != *attrs) {
+ return false;
+ }
+ }
- if (!arch_mm_pte_is_valid(pte, level)) {
- return false;
- }
-
- if (arch_mm_pte_is_table(pte, level)) {
- return mm_is_mapped_recursive(
- mm_page_table_from_pa(
- arch_mm_table_from_pte(pte, level)),
- addr, level - 1);
+ begin = mm_start_of_next_block(begin, entry_size);
+ pte++;
}
/* The entry is a valid block. */
- return true;
+ return got_attrs;
}
/**
- * Determines if the given address is valid in the address space of the given
- * page table.
+ * Gets the attributes applies to the given range of addresses in the stage-2
+ * table.
+ *
+ * The value returned in `attrs` is only valid if the function returns true.
+ *
+ * Returns true if the whole range has the same attributes and false otherwise.
*/
-static bool mm_ptable_is_mapped(struct mm_ptable *t, ptable_addr_t addr,
- int mode)
+static bool mm_vm_get_attrs(struct mm_ptable *t, ptable_addr_t begin,
+ ptable_addr_t end, uint64_t *attrs)
{
- struct mm_page_table *tables = mm_page_table_from_pa(t->root);
- uint8_t level = arch_mm_max_level(mode);
- size_t index;
+ int mode = 0;
+ uint8_t max_level = arch_mm_max_level(mode);
+ uint8_t root_level = max_level + 1;
+ size_t root_table_size = mm_entry_size(root_level);
+ ptable_addr_t ptable_end =
+ arch_mm_root_table_count(mode) * mm_entry_size(root_level);
+ struct mm_page_table *table;
+ bool got_attrs = false;
- addr = mm_round_down_to_page(addr);
- index = mm_index(addr, level + 1);
+ begin = mm_round_down_to_page(begin);
+ end = mm_round_up_to_page(end);
- if (index >= arch_mm_root_table_count(mode)) {
+ /* Fail if the addresses are out of range. */
+ if (end > ptable_end) {
return false;
}
- return mm_is_mapped_recursive(&tables[index], addr, level);
+ table = &mm_page_table_from_pa(t->root)[mm_index(begin, root_level)];
+ while (begin < end) {
+ if (!mm_ptable_get_attrs_level(table, begin, end, max_level,
+ got_attrs, attrs)) {
+ return false;
+ }
+
+ got_attrs = true;
+ begin = mm_start_of_next_block(begin, root_table_size);
+ table++;
+ }
+
+ return got_attrs;
}
/**
@@ -772,12 +815,23 @@
}
/**
- * Checks whether the given intermediate physical addess is mapped in the given
- * page table of a VM.
+ * Gets the mode of the give range of intermediate physical addresses if they
+ * are mapped with the same mode.
+ *
+ * Returns true if the range is mapped with the same mode and false otherwise.
*/
-bool mm_vm_is_mapped(struct mm_ptable *t, ipaddr_t ipa, int mode)
+bool mm_vm_get_mode(struct mm_ptable *t, ipaddr_t begin, ipaddr_t end,
+ int *mode)
{
- return mm_ptable_is_mapped(t, ipa_addr(ipa), mode & ~MM_MODE_STAGE1);
+ uint64_t attrs;
+ bool ret;
+
+ ret = mm_vm_get_attrs(t, ipa_addr(begin), ipa_addr(end), &attrs);
+ if (ret) {
+ *mode = arch_mm_stage2_attrs_to_mode(attrs);
+ }
+
+ return ret;
}
/**