diff mbox series

[v4,4/7] Convert 'info tlb' to use generic iterator.

Message ID 20240723010545.3648706-5-porter@cs.unc.edu
State New
Headers show
Series Rework x86 page table walks | expand

Commit Message

Don Porter July 23, 2024, 1:05 a.m. UTC
If the the guest is using nested page tables, change the output format
slightly, to first show guest virtual to guest physical, then guest
physical to host physical, as below:

(qemu) info tlb
Info guest TLB (guest virtual to guest physical):
0000008000800000: 000000000076a000  -------U-P
0000008000801000: 000000000076b000  -------U-P
0000008000802000: 000000000076c000  -------U-P
0000008000803000: 000000000076d000  -------U-P
[...]
0000008004ffd000: 0000000000ffd000  --------WP
0000008004ffe000: 0000000000ffe000  --------WP
0000008004fff000: 0000000000fff000  --------WP
Info host TLB, (guest physical to host physical):
0000000000001000: 0000000001b20000  ----XWR
0000000000002000: 0000000001b21000  ----XWR
0000000000003000: 0000000001b22000  ----XWR
0000000000004000: 0000000001b23000  ----XWR
0000000000005000: 0000000001b24000  ----XWR
[...]

Signed-off-by: Don Porter <porter@cs.unc.edu>
---
 include/hw/core/sysemu-cpu-ops.h |   7 +
 target/i386/cpu.c                |   1 +
 target/i386/cpu.h                |   2 +
 target/i386/monitor.c            | 233 +++++++++----------------------
 4 files changed, 75 insertions(+), 168 deletions(-)

Comments

Dr. David Alan Gilbert July 27, 2024, 8:47 p.m. UTC | #1
* Don Porter (porter@cs.unc.edu) wrote:
> If the the guest is using nested page tables, change the output format
> slightly, to first show guest virtual to guest physical, then guest
> physical to host physical, as below:

This surprises me from the title; the title made me think
this was a change purely to the implementation.

> (qemu) info tlb
> Info guest TLB (guest virtual to guest physical):

It's a shame that the code that prints this has no way to know the column widths,
since it would be nice to have:

   guest virt           guest phys    ???????????
> 0000008000800000: 000000000076a000  -------U-P
> 0000008000801000: 000000000076b000  -------U-P
> 0000008000802000: 000000000076c000  -------U-P
> 0000008000803000: 000000000076d000  -------U-P
> [...]
> 0000008004ffd000: 0000000000ffd000  --------WP
> 0000008004ffe000: 0000000000ffe000  --------WP
> 0000008004fff000: 0000000000fff000  --------WP

> Info host TLB, (guest physical to host physical):

Do you actually want this to happen in the same operation, or do you
want info tlb -h (e.g.) to get the host info?

> 0000000000001000: 0000000001b20000  ----XWR
> 0000000000002000: 0000000001b21000  ----XWR
> 0000000000003000: 0000000001b22000  ----XWR
> 0000000000004000: 0000000001b23000  ----XWR
> 0000000000005000: 0000000001b24000  ----XWR
> [...]
> 
> Signed-off-by: Don Porter <porter@cs.unc.edu>
> ---
>  include/hw/core/sysemu-cpu-ops.h |   7 +
>  target/i386/cpu.c                |   1 +
>  target/i386/cpu.h                |   2 +
>  target/i386/monitor.c            | 233 +++++++++----------------------
>  4 files changed, 75 insertions(+), 168 deletions(-)
> 
> diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h
> index d0e939def8..083df4717c 100644
> --- a/include/hw/core/sysemu-cpu-ops.h
> +++ b/include/hw/core/sysemu-cpu-ops.h
> @@ -225,6 +225,13 @@ typedef struct SysemuCPUOps {
>      bool (*mon_flush_page_print_state)(CPUState *cs,
>                                         struct mem_print_state *state);
>  
> +    /**
> +     * @mon_print_pte: Hook called by the monitor to print a page
> +     * table entry at address addr, with contents pte.
> +     */
> +    void (*mon_print_pte) (CPUState *cs, GString *buf, hwaddr addr,
> +                           hwaddr pte, uint64_t prot, int mmu_idx);
> +
>  } SysemuCPUOps;
>  
>  int compressing_iterator(CPUState *cs, void *data, DecodedPTE *pte,
> diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> index ec419e0ef0..030198497a 100644
> --- a/target/i386/cpu.c
> +++ b/target/i386/cpu.c
> @@ -8388,6 +8388,7 @@ static const struct SysemuCPUOps i386_sysemu_ops = {
>      .mon_init_page_table_iterator = &x86_mon_init_page_table_iterator,
>      .mon_info_pg_print_header = &x86_mon_info_pg_print_header,
>      .mon_flush_page_print_state = &x86_mon_flush_print_pg_state,
> +    .mon_print_pte = &x86_mon_print_pte,
>  };
>  #endif
>  
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 4e5877f41d..413c743c1a 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -2264,6 +2264,8 @@ bool x86_mon_init_page_table_iterator(CPUState *cpu, GString *buf, int mmu_idx,
>                                        struct mem_print_state *state);
>  void x86_mon_info_pg_print_header(struct mem_print_state *state);
>  bool x86_mon_flush_print_pg_state(CPUState *cs, struct mem_print_state *state);
> +void x86_mon_print_pte(CPUState *cs, GString *out_buf, hwaddr addr,
> +                       hwaddr child, uint64_t prot, int mmu_idx);
>  bool x86_ptw_translate(CPUState *cs, vaddr vaddress, hwaddr *hpa,
>                         bool debug, int mmu_idx, bool user_access,
>                         const MMUAccessType access_type, uint64_t *page_size,
> diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> index 8ef92e7c42..d88347684b 100644
> --- a/target/i386/monitor.c
> +++ b/target/i386/monitor.c
> @@ -224,201 +224,98 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
>      return addr;
>  }
>  
> -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> -                      hwaddr pte, hwaddr mask)
> +void x86_mon_print_pte(CPUState *cs, GString *out_buf, hwaddr addr,
> +                       hwaddr child, uint64_t prot, int mmu_idx)
>  {
> +    CPUX86State *env = cpu_env(cs);
> +    g_autoptr(GString) buf = g_string_new("");
> +
>      addr = addr_canonical(env, addr);
>  
> -    monitor_printf(mon, HWADDR_FMT_plx ": " HWADDR_FMT_plx
> -                   " %c%c%c%c%c%c%c%c%c\n",
> -                   addr,
> -                   pte & mask,
> -                   pte & PG_NX_MASK ? 'X' : '-',
> -                   pte & PG_GLOBAL_MASK ? 'G' : '-',
> -                   pte & PG_PSE_MASK ? 'P' : '-',
> -                   pte & PG_DIRTY_MASK ? 'D' : '-',
> -                   pte & PG_ACCESSED_MASK ? 'A' : '-',
> -                   pte & PG_PCD_MASK ? 'C' : '-',
> -                   pte & PG_PWT_MASK ? 'T' : '-',
> -                   pte & PG_USER_MASK ? 'U' : '-',
> -                   pte & PG_RW_MASK ? 'W' : '-');
> -}
> +    g_string_append_printf(buf, HWADDR_FMT_plx ": " HWADDR_FMT_plx " ",
> +                           addr, child);
>  
> -static void tlb_info_32(Monitor *mon, CPUArchState *env)
> -{
> -    unsigned int l1, l2;
> -    uint32_t pgd, pde, pte;
> +    g_string_append_printf(buf, " %s", pg_bits(cs, prot, mmu_idx));
>  
> -    pgd = env->cr[3] & ~0xfff;
> -    for(l1 = 0; l1 < 1024; l1++) {

It's nice to see all this hairy code disappearing.

> -        cpu_physical_memory_read(pgd + l1 * 4, &pde, 4);
> -        pde = le32_to_cpu(pde);
> -        if (pde & PG_PRESENT_MASK) {
> -            if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
> -                /* 4M pages */
> -                print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1));
> -            } else {
> -                for(l2 = 0; l2 < 1024; l2++) {
> -                    cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4);
> -                    pte = le32_to_cpu(pte);
> -                    if (pte & PG_PRESENT_MASK) {
> -                        print_pte(mon, env, (l1 << 22) + (l2 << 12),
> -                                  pte & ~PG_PSE_MASK,
> -                                  ~0xfff);
> -                    }
> -                }
> -            }
> -        }
> -    }
> -}
> -
> -static void tlb_info_pae32(Monitor *mon, CPUArchState *env)
> -{
> -    unsigned int l1, l2, l3;
> -    uint64_t pdpe, pde, pte;
> -    uint64_t pdp_addr, pd_addr, pt_addr;
> +    /* Trim line to fit screen */
> +    g_string_truncate(buf, 79);

Do we need that?  It's a pretty arbitrary limit these days.

> -    pdp_addr = env->cr[3] & ~0x1f;
> -    for (l1 = 0; l1 < 4; l1++) {
> -        cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8);
> -        pdpe = le64_to_cpu(pdpe);
> -        if (pdpe & PG_PRESENT_MASK) {
> -            pd_addr = pdpe & 0x3fffffffff000ULL;
> -            for (l2 = 0; l2 < 512; l2++) {
> -                cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8);
> -                pde = le64_to_cpu(pde);
> -                if (pde & PG_PRESENT_MASK) {
> -                    if (pde & PG_PSE_MASK) {
> -                        /* 2M pages with PAE, CR4.PSE is ignored */
> -                        print_pte(mon, env, (l1 << 30) + (l2 << 21), pde,
> -                                  ~((hwaddr)(1 << 20) - 1));
> -                    } else {
> -                        pt_addr = pde & 0x3fffffffff000ULL;
> -                        for (l3 = 0; l3 < 512; l3++) {
> -                            cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8);
> -                            pte = le64_to_cpu(pte);
> -                            if (pte & PG_PRESENT_MASK) {
> -                                print_pte(mon, env, (l1 << 30) + (l2 << 21)
> -                                          + (l3 << 12),
> -                                          pte & ~PG_PSE_MASK,
> -                                          ~(hwaddr)0xfff);
> -                            }
> -                        }
> -                    }
> -                }
> -            }
> -        }
> -    }
> +    g_string_append_printf(out_buf, "%s\n", buf->str);
>  }
>  
> -#ifdef TARGET_X86_64
> -static void tlb_info_la48(Monitor *mon, CPUArchState *env,
> -        uint64_t l0, uint64_t pml4_addr)
> +static
> +int mem_print_tlb(CPUState *cs, void *data, DecodedPTE *pte, int height,
> +                  int offset, int mmu_idx, const PageTableLayout *layout)
>  {
> -    uint64_t l1, l2, l3, l4;
> -    uint64_t pml4e, pdpe, pde, pte;
> -    uint64_t pdp_addr, pd_addr, pt_addr;
> -
> -    for (l1 = 0; l1 < 512; l1++) {
> -        cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8);
> -        pml4e = le64_to_cpu(pml4e);
> -        if (!(pml4e & PG_PRESENT_MASK)) {
> -            continue;
> -        }
> -
> -        pdp_addr = pml4e & 0x3fffffffff000ULL;
> -        for (l2 = 0; l2 < 512; l2++) {
> -            cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8);
> -            pdpe = le64_to_cpu(pdpe);
> -            if (!(pdpe & PG_PRESENT_MASK)) {
> -                continue;
> -            }
> +    struct mem_print_state *state = (struct mem_print_state *) data;
> +    CPUClass *cc = CPU_GET_CLASS(cs);
>  
> -            if (pdpe & PG_PSE_MASK) {
> -                /* 1G pages, CR4.PSE is ignored */
> -                print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
> -                        pdpe, 0x3ffffc0000000ULL);
> -                continue;
> -            }
> -
> -            pd_addr = pdpe & 0x3fffffffff000ULL;
> -            for (l3 = 0; l3 < 512; l3++) {
> -                cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8);
> -                pde = le64_to_cpu(pde);
> -                if (!(pde & PG_PRESENT_MASK)) {
> -                    continue;
> -                }
> +    cc->sysemu_ops->mon_print_pte(cs, state->buf, pte->bits_translated,
> +                                  pte->child, pte->prot, mmu_idx);
>  
> -                if (pde & PG_PSE_MASK) {
> -                    /* 2M pages, CR4.PSE is ignored */
> -                    print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30) +
> -                            (l3 << 21), pde, 0x3ffffffe00000ULL);
> -                    continue;
> -                }
> -
> -                pt_addr = pde & 0x3fffffffff000ULL;
> -                for (l4 = 0; l4 < 512; l4++) {
> -                    cpu_physical_memory_read(pt_addr
> -                            + l4 * 8,
> -                            &pte, 8);
> -                    pte = le64_to_cpu(pte);
> -                    if (pte & PG_PRESENT_MASK) {
> -                        print_pte(mon, env, (l0 << 48) + (l1 << 39) +
> -                                (l2 << 30) + (l3 << 21) + (l4 << 12),
> -                                pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
> -                    }
> -                }
> -            }
> -        }
> -    }
> +    return 0;
>  }
>  
> -static void tlb_info_la57(Monitor *mon, CPUArchState *env)
> +static
> +void helper_hmp_info_tlb(CPUState *cs, Monitor *mon, int mmu_idx)
>  {
> -    uint64_t l0;
> -    uint64_t pml5e;
> -    uint64_t pml5_addr;
> +    struct mem_print_state state;
> +    g_autoptr(GString) buf = g_string_new("");
> +    CPUClass *cc = CPU_GET_CLASS(cs);
>  
> -    pml5_addr = env->cr[3] & 0x3fffffffff000ULL;
> -    for (l0 = 0; l0 < 512; l0++) {
> -        cpu_physical_memory_read(pml5_addr + l0 * 8, &pml5e, 8);
> -        pml5e = le64_to_cpu(pml5e);
> -        if (pml5e & PG_PRESENT_MASK) {
> -            tlb_info_la48(mon, env, l0, pml5e & 0x3fffffffff000ULL);
> -        }
> +    if (!cc->sysemu_ops->mon_init_page_table_iterator(cs, buf, mmu_idx,
> +                                                      &state)) {
> +        monitor_printf(mon, "Unable to initialize page table iterator\n");
> +        return;
>      }
> +
> +    /**
> +     * 'info tlb' visits only leaf PTEs marked present.
> +     * It does not check other protection bits.

(What happens if the guest is using huge pages inside the guest?)

> +     */
> +    for_each_pte(cs, &mem_print_tlb, &state, false, false, false,  mmu_idx);
> +
> +    monitor_printf(mon, "%s", buf->str);
>  }
> -#endif /* TARGET_X86_64 */
>  
>  void hmp_info_tlb(Monitor *mon, const QDict *qdict)
>  {
> -    CPUArchState *env;
> +    CPUState *cs = mon_get_cpu(mon);
> +    bool nested;
>  
> -    env = mon_get_cpu_env(mon);
> -    if (!env) {
> -        monitor_printf(mon, "No CPU available\n");
> +    if (!cs) {
> +        monitor_printf(mon, "Unable to get CPUState.  Internal error\n");

I wouldn't bother saying ' Internal error'.

>          return;
>      }
>  
> -    if (!(env->cr[0] & CR0_PG_MASK)) {
> +    if (!cpu_paging_enabled(cs, 0)) {
>          monitor_printf(mon, "PG disabled\n");
>          return;
>      }
> -    if (env->cr[4] & CR4_PAE_MASK) {
> -#ifdef TARGET_X86_64
> -        if (env->hflags & HF_LMA_MASK) {
> -            if (env->cr[4] & CR4_LA57_MASK) {
> -                tlb_info_la57(mon, env);
> -            } else {
> -                tlb_info_la48(mon, env, 0, env->cr[3] & 0x3fffffffff000ULL);
> -            }
> -        } else
> -#endif
> -        {
> -            tlb_info_pae32(mon, env);
> -        }
> -    } else {
> -        tlb_info_32(mon, env);
> +
> +    CPUClass *cc = CPU_GET_CLASS(cs);
> +
> +    if (!cc->sysemu_ops->mon_print_pte
> +        || !cc->sysemu_ops->mon_init_page_table_iterator) {
> +        monitor_printf(mon, "Info tlb unsupported on this ISA\n");
> +        return;
> +    }
> +
> +    nested = cpu_paging_enabled(cs, 1);
> +
> +    if (nested) {
> +        monitor_printf(mon,
> +                       "Info guest TLB (guest virtual to guest physical):\n");
> +    }
> +
> +    helper_hmp_info_tlb(cs, mon, 0);
> +
> +    if (nested) {
> +        monitor_printf(mon,
> +                       "Info host TLB, (guest physical to host physical):\n");
> +
> +        helper_hmp_info_tlb(cs, mon, 1);
> +
>      }
>  }
>  
> -- 
> 2.34.1
>
diff mbox series

Patch

diff --git a/include/hw/core/sysemu-cpu-ops.h b/include/hw/core/sysemu-cpu-ops.h
index d0e939def8..083df4717c 100644
--- a/include/hw/core/sysemu-cpu-ops.h
+++ b/include/hw/core/sysemu-cpu-ops.h
@@ -225,6 +225,13 @@  typedef struct SysemuCPUOps {
     bool (*mon_flush_page_print_state)(CPUState *cs,
                                        struct mem_print_state *state);
 
+    /**
+     * @mon_print_pte: Hook called by the monitor to print a page
+     * table entry at address addr, with contents pte.
+     */
+    void (*mon_print_pte) (CPUState *cs, GString *buf, hwaddr addr,
+                           hwaddr pte, uint64_t prot, int mmu_idx);
+
 } SysemuCPUOps;
 
 int compressing_iterator(CPUState *cs, void *data, DecodedPTE *pte,
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index ec419e0ef0..030198497a 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -8388,6 +8388,7 @@  static const struct SysemuCPUOps i386_sysemu_ops = {
     .mon_init_page_table_iterator = &x86_mon_init_page_table_iterator,
     .mon_info_pg_print_header = &x86_mon_info_pg_print_header,
     .mon_flush_page_print_state = &x86_mon_flush_print_pg_state,
+    .mon_print_pte = &x86_mon_print_pte,
 };
 #endif
 
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 4e5877f41d..413c743c1a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2264,6 +2264,8 @@  bool x86_mon_init_page_table_iterator(CPUState *cpu, GString *buf, int mmu_idx,
                                       struct mem_print_state *state);
 void x86_mon_info_pg_print_header(struct mem_print_state *state);
 bool x86_mon_flush_print_pg_state(CPUState *cs, struct mem_print_state *state);
+void x86_mon_print_pte(CPUState *cs, GString *out_buf, hwaddr addr,
+                       hwaddr child, uint64_t prot, int mmu_idx);
 bool x86_ptw_translate(CPUState *cs, vaddr vaddress, hwaddr *hpa,
                        bool debug, int mmu_idx, bool user_access,
                        const MMUAccessType access_type, uint64_t *page_size,
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 8ef92e7c42..d88347684b 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -224,201 +224,98 @@  static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
     return addr;
 }
 
-static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
-                      hwaddr pte, hwaddr mask)
+void x86_mon_print_pte(CPUState *cs, GString *out_buf, hwaddr addr,
+                       hwaddr child, uint64_t prot, int mmu_idx)
 {
+    CPUX86State *env = cpu_env(cs);
+    g_autoptr(GString) buf = g_string_new("");
+
     addr = addr_canonical(env, addr);
 
-    monitor_printf(mon, HWADDR_FMT_plx ": " HWADDR_FMT_plx
-                   " %c%c%c%c%c%c%c%c%c\n",
-                   addr,
-                   pte & mask,
-                   pte & PG_NX_MASK ? 'X' : '-',
-                   pte & PG_GLOBAL_MASK ? 'G' : '-',
-                   pte & PG_PSE_MASK ? 'P' : '-',
-                   pte & PG_DIRTY_MASK ? 'D' : '-',
-                   pte & PG_ACCESSED_MASK ? 'A' : '-',
-                   pte & PG_PCD_MASK ? 'C' : '-',
-                   pte & PG_PWT_MASK ? 'T' : '-',
-                   pte & PG_USER_MASK ? 'U' : '-',
-                   pte & PG_RW_MASK ? 'W' : '-');
-}
+    g_string_append_printf(buf, HWADDR_FMT_plx ": " HWADDR_FMT_plx " ",
+                           addr, child);
 
-static void tlb_info_32(Monitor *mon, CPUArchState *env)
-{
-    unsigned int l1, l2;
-    uint32_t pgd, pde, pte;
+    g_string_append_printf(buf, " %s", pg_bits(cs, prot, mmu_idx));
 
-    pgd = env->cr[3] & ~0xfff;
-    for(l1 = 0; l1 < 1024; l1++) {
-        cpu_physical_memory_read(pgd + l1 * 4, &pde, 4);
-        pde = le32_to_cpu(pde);
-        if (pde & PG_PRESENT_MASK) {
-            if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
-                /* 4M pages */
-                print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1));
-            } else {
-                for(l2 = 0; l2 < 1024; l2++) {
-                    cpu_physical_memory_read((pde & ~0xfff) + l2 * 4, &pte, 4);
-                    pte = le32_to_cpu(pte);
-                    if (pte & PG_PRESENT_MASK) {
-                        print_pte(mon, env, (l1 << 22) + (l2 << 12),
-                                  pte & ~PG_PSE_MASK,
-                                  ~0xfff);
-                    }
-                }
-            }
-        }
-    }
-}
-
-static void tlb_info_pae32(Monitor *mon, CPUArchState *env)
-{
-    unsigned int l1, l2, l3;
-    uint64_t pdpe, pde, pte;
-    uint64_t pdp_addr, pd_addr, pt_addr;
+    /* Trim line to fit screen */
+    g_string_truncate(buf, 79);
 
-    pdp_addr = env->cr[3] & ~0x1f;
-    for (l1 = 0; l1 < 4; l1++) {
-        cpu_physical_memory_read(pdp_addr + l1 * 8, &pdpe, 8);
-        pdpe = le64_to_cpu(pdpe);
-        if (pdpe & PG_PRESENT_MASK) {
-            pd_addr = pdpe & 0x3fffffffff000ULL;
-            for (l2 = 0; l2 < 512; l2++) {
-                cpu_physical_memory_read(pd_addr + l2 * 8, &pde, 8);
-                pde = le64_to_cpu(pde);
-                if (pde & PG_PRESENT_MASK) {
-                    if (pde & PG_PSE_MASK) {
-                        /* 2M pages with PAE, CR4.PSE is ignored */
-                        print_pte(mon, env, (l1 << 30) + (l2 << 21), pde,
-                                  ~((hwaddr)(1 << 20) - 1));
-                    } else {
-                        pt_addr = pde & 0x3fffffffff000ULL;
-                        for (l3 = 0; l3 < 512; l3++) {
-                            cpu_physical_memory_read(pt_addr + l3 * 8, &pte, 8);
-                            pte = le64_to_cpu(pte);
-                            if (pte & PG_PRESENT_MASK) {
-                                print_pte(mon, env, (l1 << 30) + (l2 << 21)
-                                          + (l3 << 12),
-                                          pte & ~PG_PSE_MASK,
-                                          ~(hwaddr)0xfff);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
+    g_string_append_printf(out_buf, "%s\n", buf->str);
 }
 
-#ifdef TARGET_X86_64
-static void tlb_info_la48(Monitor *mon, CPUArchState *env,
-        uint64_t l0, uint64_t pml4_addr)
+static
+int mem_print_tlb(CPUState *cs, void *data, DecodedPTE *pte, int height,
+                  int offset, int mmu_idx, const PageTableLayout *layout)
 {
-    uint64_t l1, l2, l3, l4;
-    uint64_t pml4e, pdpe, pde, pte;
-    uint64_t pdp_addr, pd_addr, pt_addr;
-
-    for (l1 = 0; l1 < 512; l1++) {
-        cpu_physical_memory_read(pml4_addr + l1 * 8, &pml4e, 8);
-        pml4e = le64_to_cpu(pml4e);
-        if (!(pml4e & PG_PRESENT_MASK)) {
-            continue;
-        }
-
-        pdp_addr = pml4e & 0x3fffffffff000ULL;
-        for (l2 = 0; l2 < 512; l2++) {
-            cpu_physical_memory_read(pdp_addr + l2 * 8, &pdpe, 8);
-            pdpe = le64_to_cpu(pdpe);
-            if (!(pdpe & PG_PRESENT_MASK)) {
-                continue;
-            }
+    struct mem_print_state *state = (struct mem_print_state *) data;
+    CPUClass *cc = CPU_GET_CLASS(cs);
 
-            if (pdpe & PG_PSE_MASK) {
-                /* 1G pages, CR4.PSE is ignored */
-                print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
-                        pdpe, 0x3ffffc0000000ULL);
-                continue;
-            }
-
-            pd_addr = pdpe & 0x3fffffffff000ULL;
-            for (l3 = 0; l3 < 512; l3++) {
-                cpu_physical_memory_read(pd_addr + l3 * 8, &pde, 8);
-                pde = le64_to_cpu(pde);
-                if (!(pde & PG_PRESENT_MASK)) {
-                    continue;
-                }
+    cc->sysemu_ops->mon_print_pte(cs, state->buf, pte->bits_translated,
+                                  pte->child, pte->prot, mmu_idx);
 
-                if (pde & PG_PSE_MASK) {
-                    /* 2M pages, CR4.PSE is ignored */
-                    print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30) +
-                            (l3 << 21), pde, 0x3ffffffe00000ULL);
-                    continue;
-                }
-
-                pt_addr = pde & 0x3fffffffff000ULL;
-                for (l4 = 0; l4 < 512; l4++) {
-                    cpu_physical_memory_read(pt_addr
-                            + l4 * 8,
-                            &pte, 8);
-                    pte = le64_to_cpu(pte);
-                    if (pte & PG_PRESENT_MASK) {
-                        print_pte(mon, env, (l0 << 48) + (l1 << 39) +
-                                (l2 << 30) + (l3 << 21) + (l4 << 12),
-                                pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
-                    }
-                }
-            }
-        }
-    }
+    return 0;
 }
 
-static void tlb_info_la57(Monitor *mon, CPUArchState *env)
+static
+void helper_hmp_info_tlb(CPUState *cs, Monitor *mon, int mmu_idx)
 {
-    uint64_t l0;
-    uint64_t pml5e;
-    uint64_t pml5_addr;
+    struct mem_print_state state;
+    g_autoptr(GString) buf = g_string_new("");
+    CPUClass *cc = CPU_GET_CLASS(cs);
 
-    pml5_addr = env->cr[3] & 0x3fffffffff000ULL;
-    for (l0 = 0; l0 < 512; l0++) {
-        cpu_physical_memory_read(pml5_addr + l0 * 8, &pml5e, 8);
-        pml5e = le64_to_cpu(pml5e);
-        if (pml5e & PG_PRESENT_MASK) {
-            tlb_info_la48(mon, env, l0, pml5e & 0x3fffffffff000ULL);
-        }
+    if (!cc->sysemu_ops->mon_init_page_table_iterator(cs, buf, mmu_idx,
+                                                      &state)) {
+        monitor_printf(mon, "Unable to initialize page table iterator\n");
+        return;
     }
+
+    /**
+     * 'info tlb' visits only leaf PTEs marked present.
+     * It does not check other protection bits.
+     */
+    for_each_pte(cs, &mem_print_tlb, &state, false, false, false,  mmu_idx);
+
+    monitor_printf(mon, "%s", buf->str);
 }
-#endif /* TARGET_X86_64 */
 
 void hmp_info_tlb(Monitor *mon, const QDict *qdict)
 {
-    CPUArchState *env;
+    CPUState *cs = mon_get_cpu(mon);
+    bool nested;
 
-    env = mon_get_cpu_env(mon);
-    if (!env) {
-        monitor_printf(mon, "No CPU available\n");
+    if (!cs) {
+        monitor_printf(mon, "Unable to get CPUState.  Internal error\n");
         return;
     }
 
-    if (!(env->cr[0] & CR0_PG_MASK)) {
+    if (!cpu_paging_enabled(cs, 0)) {
         monitor_printf(mon, "PG disabled\n");
         return;
     }
-    if (env->cr[4] & CR4_PAE_MASK) {
-#ifdef TARGET_X86_64
-        if (env->hflags & HF_LMA_MASK) {
-            if (env->cr[4] & CR4_LA57_MASK) {
-                tlb_info_la57(mon, env);
-            } else {
-                tlb_info_la48(mon, env, 0, env->cr[3] & 0x3fffffffff000ULL);
-            }
-        } else
-#endif
-        {
-            tlb_info_pae32(mon, env);
-        }
-    } else {
-        tlb_info_32(mon, env);
+
+    CPUClass *cc = CPU_GET_CLASS(cs);
+
+    if (!cc->sysemu_ops->mon_print_pte
+        || !cc->sysemu_ops->mon_init_page_table_iterator) {
+        monitor_printf(mon, "Info tlb unsupported on this ISA\n");
+        return;
+    }
+
+    nested = cpu_paging_enabled(cs, 1);
+
+    if (nested) {
+        monitor_printf(mon,
+                       "Info guest TLB (guest virtual to guest physical):\n");
+    }
+
+    helper_hmp_info_tlb(cs, mon, 0);
+
+    if (nested) {
+        monitor_printf(mon,
+                       "Info host TLB, (guest physical to host physical):\n");
+
+        helper_hmp_info_tlb(cs, mon, 1);
+
     }
 }