mm: treat level 0 page entries as blocks.
This simplifies the abstraction so all page table levels have entries
that are:
1. absent
2. tables -- references a next level table
3. block -- references a memory block, including a basic page
The arch implementation is left to handle the differences in
representation at the different levels.
Change-Id: If0d65d6ee727a699eb60d2375e1fda282e8ee3a8
diff --git a/src/arch/aarch64/inc/hf/arch/mm.h b/src/arch/aarch64/inc/hf/arch/mm.h
index eaf7c4c..980f386 100644
--- a/src/arch/aarch64/inc/hf/arch/mm.h
+++ b/src/arch/aarch64/inc/hf/arch/mm.h
@@ -10,112 +10,121 @@
#define PAGE_LEVEL_BITS 9
+#define HF_ARCH_AARCH64_MM_PTE_ATTR_MASK \
+ (((UINT64_C(1) << PAGE_BITS) - 1) | ~((UINT64_C(1) << 48) - 1))
+
+#define HF_ARCH_AARCH64_MM_GET_PTE_ATTRS(v) \
+ ((v)&HF_ARCH_AARCH64_MM_PTE_ATTR_MASK)
+
+#define HF_ARCH_AARCH64_MM_CLEAR_PTE_ATTRS(v) \
+ ((v) & ~HF_ARCH_AARCH64_MM_PTE_ATTR_MASK)
+
+/**
+ * Returns the encoding of a page table entry that isn't present.
+ */
+static inline pte_t arch_mm_absent_pte(int level)
+{
+ return 0;
+}
+
/**
* Converts a physical address to a table PTE.
*
* The spec says that 'Table descriptors for stage 2 translations do not
* include any attribute field', so we don't take any attributes as arguments.
*/
-static inline pte_t arch_mm_pa_to_table_pte(paddr_t pa)
+static inline pte_t arch_mm_table_pte(int level, paddr_t pa)
{
+ /* This is the same for all levels on aarch64. */
+ (void)level;
return pa_addr(pa) | 0x3;
}
/**
* Converts a physical address to a block PTE.
+ *
+ * The level must allow block entries.
*/
-static inline pte_t arch_mm_pa_to_block_pte(paddr_t pa, uint64_t attrs)
+static inline pte_t arch_mm_block_pte(int level, paddr_t pa, uint64_t attrs)
{
- return pa_addr(pa) | attrs;
-}
-
-/**
- * Converts a physical address to a page PTE.
- */
-static inline pte_t arch_mm_pa_to_page_pte(paddr_t pa, uint64_t attrs)
-{
- return pa_addr(pa) | attrs | ((attrs & 1) << 1);
-}
-
-/**
- * Converts a block PTE to a page PTE.
- */
-static inline pte_t arch_mm_block_to_page_pte(pte_t pte)
-{
- return pte | 2;
+ pte_t pte = pa_addr(pa) | attrs;
+ if (level == 0) {
+ pte |= 0x2;
+ }
+ return pte;
}
/**
* Specifies whether block mappings are acceptable at the given level.
+ *
+ * Level 0 must allow block entries.
*/
static inline bool arch_mm_is_block_allowed(int level)
{
- return level == 1 || level == 2;
-}
-
-/**
- * Returns the encoding of a page table entry that isn't present.
- */
-static inline pte_t arch_mm_absent_pte(void)
-{
- return 0;
+ return level <= 2;
}
/**
* Determines if the given pte is present, i.e., if it points to another table,
* to a page, or a block of pages.
*/
-static inline bool arch_mm_pte_is_present(pte_t pte)
+static inline bool arch_mm_pte_is_present(pte_t pte, int level)
{
- return (pte & 1) != 0;
+ return (pte & 0x1) != 0;
}
/**
* Determines if the given pte references another table.
*/
-static inline bool arch_mm_pte_is_table(pte_t pte)
+static inline bool arch_mm_pte_is_table(pte_t pte, int level)
{
- return (pte & 3) == 3;
+ return level != 0 && (pte & 0x3) == 0x3;
}
/**
* Determines if the given pte references a block of pages.
*/
-static inline bool arch_mm_pte_is_block(pte_t pte)
+static inline bool arch_mm_pte_is_block(pte_t pte, int level)
{
- return (pte & 3) == 1;
+ return arch_mm_is_block_allowed(level) &&
+ (pte & 0x3) == (level == 0 ? 0x3 : 0x1);
}
-#define CLEAR_PTE_ATTRS(v) \
- ((v) & ~((1ull << PAGE_BITS) - 1) & ((1ull << 48) - 1))
-
/**
* Clears the given physical address, i.e., sets the ignored bits (from a page
* table perspective) to zero.
*/
static inline paddr_t arch_mm_clear_pa(paddr_t pa)
{
- return pa_init(CLEAR_PTE_ATTRS(pa_addr(pa)));
+ return pa_init(HF_ARCH_AARCH64_MM_CLEAR_PTE_ATTRS(pa_addr(pa)));
}
/**
- * Extracts the physical address from a page table entry.
+ * Extracts the physical address of the block referred to by the given page
+ * table entry.
*/
-static inline paddr_t arch_mm_pte_to_paddr(pte_t pte)
+static inline paddr_t arch_mm_block_from_pte(pte_t pte)
{
- return pa_init(CLEAR_PTE_ATTRS(pte));
+ return pa_init(HF_ARCH_AARCH64_MM_CLEAR_PTE_ATTRS(pte));
}
/**
* Extracts the physical address of the page table referred to by the given page
* table entry.
*/
-static inline paddr_t arch_mm_pte_to_table(pte_t pte)
+static inline paddr_t arch_mm_table_from_pte(pte_t pte)
{
- return pa_init(CLEAR_PTE_ATTRS(pte));
+ return pa_init(HF_ARCH_AARCH64_MM_CLEAR_PTE_ATTRS(pte));
}
-#undef CLEAR_PTE_ATTRS
+/**
+ * Extracts the architecture specific attributes applies to the given page table
+ * entry.
+ */
+static inline uint64_t arch_mm_pte_attrs(pte_t pte)
+{
+ return HF_ARCH_AARCH64_MM_GET_PTE_ATTRS(pte);
+}
/**
* Invalidates stage-1 TLB entries referring to the given virtual address range.
@@ -132,7 +141,7 @@
__asm__ volatile("dsb ishst");
- for (it = begin; it < end; it += (1ull << (PAGE_BITS - 12))) {
+ for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) {
__asm__("tlbi vae2is, %0" : : "r"(it));
}
@@ -157,7 +166,7 @@
__asm__ volatile("dsb ishst");
- for (it = begin; it < end; it += (1ull << (PAGE_BITS - 12))) {
+ for (it = begin; it < end; it += (UINT64_C(1) << (PAGE_BITS - 12))) {
__asm__("tlbi ipas2e1, %0" : : "r"(it));
}
diff --git a/src/arch/aarch64/mm.c b/src/arch/aarch64/mm.c
index 212a56e..df4ed2d 100644
--- a/src/arch/aarch64/mm.c
+++ b/src/arch/aarch64/mm.c
@@ -9,54 +9,54 @@
/* Keep macro alignment */
/* clang-format off */
-#define NON_SHAREABLE 0ull
-#define OUTER_SHAREABLE 2ull
-#define INNER_SHAREABLE 3ull
+#define NON_SHAREABLE UINT64_C(0)
+#define OUTER_SHAREABLE UINT64_C(2)
+#define INNER_SHAREABLE UINT64_C(3)
-#define STAGE1_XN (1ull << 54)
-#define STAGE1_CONTIGUOUS (1ull << 52)
-#define STAGE1_DBM (1ull << 51)
-#define STAGE1_NG (1ull << 11)
-#define STAGE1_AF (1ull << 10)
+#define STAGE1_XN (UINT64_C(1) << 54)
+#define STAGE1_CONTIGUOUS (UINT64_C(1) << 52)
+#define STAGE1_DBM (UINT64_C(1) << 51)
+#define STAGE1_NG (UINT64_C(1) << 11)
+#define STAGE1_AF (UINT64_C(1) << 10)
#define STAGE1_SH(x) ((x) << 8)
#define STAGE1_AP(x) ((x) << 6)
-#define STAGE1_NS (1ull << 5)
+#define STAGE1_NS (UINT64_C(1) << 5)
#define STAGE1_ATTRINDX(x) ((x) << 2)
-#define STAGE1_READONLY 2ull
-#define STAGE1_READWRITE 0ull
+#define STAGE1_READONLY UINT64_C(2)
+#define STAGE1_READWRITE UINT64_C(0)
-#define STAGE1_DEVICEINDX 0ull
-#define STAGE1_NORMALINDX 1ull
+#define STAGE1_DEVICEINDX UINT64_C(0)
+#define STAGE1_NORMALINDX UINT64_C(1)
#define STAGE2_XN(x) ((x) << 53)
-#define STAGE2_CONTIGUOUS (1ull << 52)
-#define STAGE2_DBM (1ull << 51)
-#define STAGE2_AF (1ull << 10)
+#define STAGE2_CONTIGUOUS (UINT64_C(1) << 52)
+#define STAGE2_DBM (UINT64_C(1) << 51)
+#define STAGE2_AF (UINT64_C(1) << 10)
#define STAGE2_SH(x) ((x) << 8)
#define STAGE2_S2AP(x) ((x) << 6)
#define STAGE2_MEMATTR(x) ((x) << 2)
-#define STAGE2_EXECUTE_ALL 0ull
-#define STAGE2_EXECUTE_EL0 1ull
-#define STAGE2_EXECUTE_NONE 2ull
-#define STAGE2_EXECUTE_EL1 3ull
+#define STAGE2_EXECUTE_ALL UINT64_C(0)
+#define STAGE2_EXECUTE_EL0 UINT64_C(1)
+#define STAGE2_EXECUTE_NONE UINT64_C(2)
+#define STAGE2_EXECUTE_EL1 UINT64_C(3)
/* The following are stage-2 memory attributes for normal memory. */
-#define STAGE2_NONCACHEABLE 1ull
-#define STAGE2_WRITETHROUGH 2ull
-#define STAGE2_WRITEBACK 3ull
+#define STAGE2_NONCACHEABLE UINT64_C(1)
+#define STAGE2_WRITETHROUGH UINT64_C(2)
+#define STAGE2_WRITEBACK UINT64_C(3)
#define STAGE2_MEMATTR_NORMAL(outer, inner) ((((outer) << 2) | (inner)) << 2)
/* The following stage-2 memory attributes for device memory. */
-#define STAGE2_MEMATTR_DEVICE_nGnRnE (0ull << 2)
-#define STAGE2_MEMATTR_DEVICE_nGnRE (1ull << 2)
-#define STAGE2_MEMATTR_DEVICE_nGRE (2ull << 2)
-#define STAGE2_MEMATTR_DEVICE_GRE (3ull << 2)
+#define STAGE2_MEMATTR_DEVICE_nGnRnE (UINT64_C(0) << 2)
+#define STAGE2_MEMATTR_DEVICE_nGnRE (UINT64_C(1) << 2)
+#define STAGE2_MEMATTR_DEVICE_nGRE (UINT64_C(2) << 2)
+#define STAGE2_MEMATTR_DEVICE_GRE (UINT64_C(3) << 2)
-#define STAGE2_ACCESS_READ 1ull
-#define STAGE2_ACCESS_WRITE 2ull
+#define STAGE2_ACCESS_READ UINT64_C(1)
+#define STAGE2_ACCESS_WRITE UINT64_C(2)
/* clang-format on */
diff --git a/src/load.c b/src/load.c
index b55455a..cd81237 100644
--- a/src/load.c
+++ b/src/load.c
@@ -123,11 +123,12 @@
/* Map the 1TB of memory. */
/* TODO: We should do a whitelist rather than a blacklist. */
- if (!mm_vm_identity_map(&primary_vm.ptable, pa_init(0),
- pa_init(1024ull * 1024 * 1024 * 1024),
- MM_MODE_R | MM_MODE_W | MM_MODE_X |
- MM_MODE_NOINVALIDATE,
- NULL)) {
+ if (!mm_vm_identity_map(
+ &primary_vm.ptable, pa_init(0),
+ pa_init(UINT64_C(1024) * 1024 * 1024 * 1024),
+ MM_MODE_R | MM_MODE_W | MM_MODE_X |
+ MM_MODE_NOINVALIDATE,
+ NULL)) {
dlog("Unable to initialise memory for primary vm\n");
return false;
}
diff --git a/src/mm.c b/src/mm.c
index d3b6a46..776f7a6 100644
--- a/src/mm.c
+++ b/src/mm.c
@@ -58,7 +58,7 @@
*/
static inline size_t mm_entry_size(int level)
{
- return 1ull << (PAGE_BITS + level * PAGE_LEVEL_BITS);
+ return UINT64_C(1) << (PAGE_BITS + level * PAGE_LEVEL_BITS);
}
/**
@@ -78,7 +78,7 @@
static inline size_t mm_index(ptable_addr_t addr, int level)
{
ptable_addr_t v = addr >> (PAGE_BITS + level * PAGE_LEVEL_BITS);
- return v & ((1ull << PAGE_LEVEL_BITS) - 1);
+ return v & ((UINT64_C(1) << PAGE_LEVEL_BITS) - 1);
}
/**
@@ -96,8 +96,8 @@
size_t inc;
/* Just return pointer to table if it's already populated. */
- if (arch_mm_pte_is_table(v)) {
- return ptr_from_va(va_from_pa(arch_mm_pte_to_table(v)));
+ if (arch_mm_pte_is_table(v, level)) {
+ return ptr_from_va(va_from_pa(arch_mm_table_from_pte(v)));
}
/* Allocate a new table. */
@@ -109,16 +109,15 @@
}
/* Determine template for new pte and its increment. */
- if (!arch_mm_pte_is_block(v)) {
- inc = 0;
- new_pte = arch_mm_absent_pte();
+ if (arch_mm_pte_is_block(v, level)) {
+ int level_below = level - 1;
+ inc = mm_entry_size(level_below);
+ new_pte = arch_mm_block_pte(level_below,
+ arch_mm_block_from_pte(v),
+ arch_mm_pte_attrs(v));
} else {
- inc = mm_entry_size(level - 1);
- if (level == 1) {
- new_pte = arch_mm_block_to_page_pte(v);
- } else {
- new_pte = v;
- }
+ inc = 0;
+ new_pte = arch_mm_absent_pte(level);
}
/* Initialise entries in the new table. */
@@ -132,7 +131,7 @@
* update it.
*/
atomic_thread_fence(memory_order_release);
- *pte = arch_mm_pa_to_table_pte(pa_init((uintpaddr_t)ntable));
+ *pte = arch_mm_table_pte(level, pa_init((uintpaddr_t)ntable));
return ntable;
}
@@ -147,7 +146,7 @@
(void)level;
(void)sync;
/* TODO: Implement.
- if (!arch_mm_pte_is_present(pte) || level < 1)
+ if (!arch_mm_pte_is_present(pte, level) || level < 1)
return;
*/
}
@@ -176,16 +175,12 @@
/* Fill each entry in the table. */
while (begin < end) {
- if (level == 0) {
- if (commit) {
- *pte = arch_mm_pa_to_page_pte(pa, attrs);
- }
- } else if ((end - begin) >= entry_size &&
- arch_mm_is_block_allowed(level) &&
- (begin & (entry_size - 1)) == 0) {
+ if ((end - begin) >= entry_size &&
+ arch_mm_is_block_allowed(level) &&
+ (begin & (entry_size - 1)) == 0) {
if (commit) {
pte_t v = *pte;
- *pte = arch_mm_pa_to_block_pte(pa, attrs);
+ *pte = arch_mm_block_pte(level, pa, attrs);
/* TODO: Add barrier. How do we ensure this
* isn't in use by another CPU? Send IPI? */
mm_free_page_pte(v, level, sync);
@@ -321,7 +316,7 @@
}
i = mm_index(addr, 0);
- table[i] = arch_mm_pa_to_page_pte(pa, attrs);
+ table[i] = arch_mm_block_pte(0, pa, attrs);
return true;
}
@@ -333,19 +328,16 @@
{
uint64_t i;
for (i = 0; i < PAGE_SIZE / sizeof(pte_t); i++) {
- if (!arch_mm_pte_is_present(table[i])) {
+ if (!arch_mm_pte_is_present(table[i], level)) {
continue;
}
dlog("%*s%x: %x\n", 4 * (max_level - level), "", i, table[i]);
- if (!level) {
- continue;
- }
- if (arch_mm_pte_is_table(table[i])) {
+ if (arch_mm_pte_is_table(table[i], level)) {
mm_dump_table_recursive(
ptr_from_va(va_from_pa(
- arch_mm_pte_to_table(table[i]))),
+ arch_mm_table_from_pte(table[i]))),
level - 1, max_level);
}
}
@@ -403,20 +395,17 @@
pte = table[mm_index(addr, level)];
- if (level == 0) {
- return arch_mm_pte_is_present(pte);
- }
-
- if (arch_mm_is_block_allowed(level) && arch_mm_pte_is_block(pte)) {
+ if (arch_mm_pte_is_block(pte, level)) {
return true;
}
- if (arch_mm_pte_is_table(pte)) {
+ if (arch_mm_pte_is_table(pte, level)) {
return mm_is_mapped_recursive(
- ptr_from_va(va_from_pa(arch_mm_pte_to_table(pte))),
+ ptr_from_va(va_from_pa(arch_mm_table_from_pte(pte))),
addr, level - 1);
}
+ /* The entry is not present. */
return false;
}
@@ -453,7 +442,7 @@
}
for (i = 0; i < PAGE_SIZE / sizeof(pte_t); i++) {
- table[i] = arch_mm_absent_pte();
+ table[i] = arch_mm_absent_pte(arch_mm_max_level(mode));
}
/* TODO: halloc could return a virtual or physical address if mm not