@@ -34,6 +34,155 @@
#include "qapi/qapi-commands-misc-target.h"
#include "qapi/qapi-commands-misc.h"
+/**
+ * PTLayout describes the layout of an x86 page table in enough detail
+ * to fully decode up to a 4-level 64-bit page table tree.
+ */
+typedef struct PTLayout {
+ int levels, entsize;
+ int entries[4]; /* Entries in each table level */
+ int shift[4]; /* VA bit shift each each level */
+ bool pse[4]; /* Whether PSE bit is valid */
+ const char *names[4];
+ int vaw, paw; /* VA and PA width in characters */
+} PTLayout;
+
+/**
+ * PTIter provides a generic way to traverse and decode an x86 page
+ * table tree.
+ */
+typedef struct PTIter {
+ const PTLayout *layout;
+ bool pse; /* PSE enabled */
+ int level; /* Current level */
+ int i[4]; /* Index at each level */
+ hwaddr base[4]; /* Physical base pointer */
+
+ uint64_t ent; /* Current entry */
+ bool present, leaf;
+ target_ulong va;
+ hwaddr pa;
+ target_ulong size;
+} PTIter;
+
+static bool ptiter_succ(PTIter *it);
+
+/**
+ * Initialize a PTIter to point to the first entry of the page table's
+ * top level. On failure, prints a message to mon and returns false.
+ */
+static bool
+__attribute__ ((unused))
+ptiter_init(Monitor *mon, PTIter *it)
+{
+ static const PTLayout l32 = {
+ 2, 4, {1024, 1024}, {22, 12}, {1, 0}, {"PDE", "PTE"}, 8, 8
+ };
+ static const PTLayout lpae = {
+ 3, 8, {4, 512, 512}, {30, 21, 12}, {0, 1, 0},
+ {"PDP", "PDE", "PTE"}, 8, 13
+ };
+#ifdef TARGET_X86_64
+ static const PTLayout l64 = {
+ 4, 8, {512, 512, 512, 512}, {39, 30, 21, 12}, {0, 1, 1, 0},
+ {"PML4", "PDP", "PDE", "PTE"}, 12, 13
+ };
+#endif
+ CPUArchState *env;
+
+ env = mon_get_cpu_env(mon);
+
+ if (!(env->cr[0] & CR0_PG_MASK)) {
+ monitor_printf(mon, "PG disabled\n");
+ return false;
+ }
+
+ memset(it, 0, sizeof(*it));
+ if (env->cr[4] & CR4_PAE_MASK) {
+#ifdef TARGET_X86_64
+ if (env->hflags & HF_LMA_MASK) {
+ it->layout = &l64;
+ it->base[0] = env->cr[3] & 0x3fffffffff000ULL;
+ } else
+#endif
+ {
+ it->layout = &lpae;
+ it->base[0] = env->cr[3] & ~0x1f;
+ }
+ it->pse = true;
+ } else {
+ it->layout = &l32;
+ it->base[0] = env->cr[3] & ~0xfff;
+ it->pse = (env->cr[4] & CR4_PSE_MASK);
+ }
+
+ /* Trick ptiter_succ into doing the hard initialization. */
+ it->i[0] = -1;
+ it->leaf = true;
+ ptiter_succ(it);
+ return true;
+}
+
+/**
+ * Move a PTIter to the successor of the current entry. Specifically:
+ * if the iterator points to a leaf, move to its next sibling, or to
+ * the next sibling of a parent if it has no more siblings. If the
+ * iterator points to a non-leaf, move to its first child. If there
+ * is no successor, return false.
+ *
+ * Note that the resulting entry may not be marked present, though
+ * non-present entries are always leafs (within a page
+ * table/directory/etc, this will always visit all entries).
+ */
+static bool ptiter_succ(PTIter *it)
+{
+ int i, l, entsize;
+ uint64_t ent64;
+ uint32_t ent32;
+ bool large;
+
+ if (it->level < 0) {
+ return false;
+ } else if (!it->leaf) {
+ /* Move to this entry's first child */
+ it->level++;
+ it->base[it->level] = it->pa;
+ it->i[it->level] = 0;
+ } else {
+ /* Move forward and, if we hit the end of this level, up */
+ while (++it->i[it->level] == it->layout->entries[it->level]) {
+ if (it->level-- == 0) {
+ /* We're out of page table */
+ return false;
+ }
+ }
+ }
+
+ /* Read this entry */
+ l = it->level;
+ entsize = it->layout->entsize;
+ cpu_physical_memory_read(it->base[l] + it->i[l] * entsize,
+ entsize == 4 ? (void *)&ent32 : (void *)&ent64,
+ entsize);
+ if (entsize == 4) {
+ it->ent = le32_to_cpu(ent32);
+ } else {
+ it->ent = le64_to_cpu(ent64);
+ }
+
+ /* Decode the entry */
+ large = (it->pse && it->layout->pse[l] && (it->ent & PG_PSE_MASK));
+ it->present = it->ent & PG_PRESENT_MASK;
+ it->leaf = (large || !it->present || ((l + 1) == it->layout->levels));
+ it->va = 0;
+ for (i = 0; i <= l; i++) {
+ it->va |= (uint64_t)it->i[i] << it->layout->shift[i];
+ }
+ it->pa = it->ent & (large ? 0x3ffffffffc000ULL : 0x3fffffffff000ULL);
+ it->size = 1 << it->layout->shift[l];
+ return true;
+}
+
/* Perform linear address sign extension */
static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
{