@@ -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 */
@@ -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,