RISC-V doesn't have hardware feature to ask MMU to translate
virtual address to physical address ( like Arm has, for example ),
so software page table walking is implemented.
Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
---
Changes in v5:
- Update the comment above _pt_walk() about returning of optional
level of the found pte.
- Rename local variable `pte_p *entry` to `ptep` in pt_walk() function.
- Add Reviewed-by: Jan Beulich <jbeulich@suse.com>.
---
Changes in v4:
- Update the comment message above _pt_walk(): add information that
`pte_level` is optional and add a note that `table` should be
unmapped by a caller.
- Unmap `table` in pt_walk().
---
Changes in v3:
- Remove circular dependency.
- Move declaration of pt_walk() to asm/page.h.
- Revert other not connected to pt_walk() changes.
- Update the commit message.
- Drop unnessary anymore local variables of pt_walk().
- Refactor pt_walk() to use for() loop instead of while() loop
as it was suggested by Jan B.
- Introduce _pt_walk() which returns pte_t * and update prototype
of pt_walk() to return pte_t by value.
---
Changes in v2:
- Update the code of pt_walk() to return pte_t instead of paddr_t.
- Fix typos and drop blankets inside parentheses in the comment.
- Update the `ret` handling; there is no need for `mfn` calculation
anymore as pt_walk() returns or pte_t of a leaf node or non-present
pte_t.
- Drop the comment before unmap_table().
- Drop local variable `pa` as pt_walk() is going to return pte_t
instead of paddr_t.
- Add the comment above pt_walk() to explain what it does and returns.
- Add additional explanation about possible return values of pt_next_level()
used inside while loop in pt_walk().
- Change `pa` to `table` in the comment before while loop in pt_walk()
as actually this loop finds a page table where paga table entry for `va`
is located.
- After including <asm/page.h> in <asm/mm.h>, the following compilation
error occurs:
./arch/riscv/include/asm/cmpxchg.h:56:9: error: implicit declaration of
function 'GENMASK'
To resolve this, <xen/bitops.h> needs to be included at the top of
<asm/cmpxchg.h>.
- To avoid an issue with the implicit declaration of map_domain_page() and
unmap_domain_page() after including <asm/page.h> in <asm/mm.h>, the
implementation of flush_page_to_ram() has been moved to mm.c. (look for
more detailed explanation in the commit message) As a result drop
inclusion of <xen/domain.h> in <asm/page.h>.
- Update the commit message.
---
xen/arch/riscv/include/asm/page.h | 2 ++
xen/arch/riscv/pt.c | 60 +++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/xen/arch/riscv/include/asm/page.h b/xen/arch/riscv/include/asm/page.h
index 7a6174a109..0439a1a9ee 100644
--- a/xen/arch/riscv/include/asm/page.h
+++ b/xen/arch/riscv/include/asm/page.h
@@ -208,6 +208,8 @@ static inline pte_t pte_from_mfn(mfn_t mfn, unsigned int flags)
return (pte_t){ .pte = pte };
}
+pte_t pt_walk(vaddr_t va, unsigned int *pte_level);
+
#endif /* __ASSEMBLY__ */
#endif /* ASM__RISCV__PAGE_H */
diff --git a/xen/arch/riscv/pt.c b/xen/arch/riscv/pt.c
index a703e0f1bd..9c1f8f6b55 100644
--- a/xen/arch/riscv/pt.c
+++ b/xen/arch/riscv/pt.c
@@ -185,6 +185,66 @@ static int pt_next_level(bool alloc_tbl, pte_t **table, unsigned int offset)
return XEN_TABLE_NORMAL;
}
+/*
+ * _pt_walk() performs software page table walking and returns the pte_t of
+ * a leaf node or the leaf-most not-present pte_t if no leaf node is found
+ * for further analysis.
+ *
+ * _pt_walk() can optionally return the level of the found pte. Pass NULL
+ * for `pte_level` if this information isn't needed.
+ *
+ * Note: unmapping of final `table` should be done by a caller.
+ */
+static pte_t *_pt_walk(vaddr_t va, unsigned int *pte_level)
+{
+ const mfn_t root = get_root_page();
+ unsigned int level;
+ pte_t *table;
+
+ DECLARE_OFFSETS(offsets, va);
+
+ table = map_table(root);
+
+ /*
+ * Find `table` of an entry which corresponds to `va` by iterating for each
+ * page level and checking if the entry points to a next page table or
+ * to a page.
+ *
+ * Two cases are possible:
+ * - ret == XEN_TABLE_SUPER_PAGE means that the entry was found;
+ * (Despite the name) XEN_TABLE_SUPER_PAGE also covers 4K mappings. If
+ * pt_next_level() is called for page table level 0, it results in the
+ * entry being a pointer to a leaf node, thereby returning
+ * XEN_TABLE_SUPER_PAGE, despite of the fact this leaf covers 4k mapping.
+ * - ret == XEN_TABLE_MAP_NONE means that requested `va` wasn't actually
+ * mapped.
+ */
+ for ( level = HYP_PT_ROOT_LEVEL; ; --level )
+ {
+ int ret = pt_next_level(false, &table, offsets[level]);
+
+ if ( ret == XEN_TABLE_MAP_NONE || ret == XEN_TABLE_SUPER_PAGE )
+ break;
+
+ ASSERT(level);
+ }
+
+ if ( pte_level )
+ *pte_level = level;
+
+ return table + offsets[level];
+}
+
+pte_t pt_walk(vaddr_t va, unsigned int *pte_level)
+{
+ pte_t *ptep = _pt_walk(va, pte_level);
+ pte_t pte = *ptep;
+
+ unmap_table(ptep);
+
+ return pte;
+}
+
/* Update an entry at the level @target. */
static int pt_update_entry(mfn_t root, vaddr_t virt,
mfn_t mfn, unsigned int target,
--
2.48.1
© 2016 - 2025 Red Hat, Inc.