* [PATCH v3] monitor: Refine 'info tlb' command
@ 2026-08-18 13:25 AlanoSong
2026-08-18 13:36 ` Philippe Mathieu-Daudé
2026-08-18 15:12 ` Marc-André Lureau
0 siblings, 2 replies; 4+ messages in thread
From: AlanoSong @ 2026-08-18 13:25 UTC (permalink / raw)
To: qemu-devel; +Cc: dave, laurent, zhao1.liu, atar4qemu, Alano Song
When booting an i386 target, the 'info tlb' command
may walk the entire page table hierarchy and emit
an enormous amount of output. It will take dozens of
minutes to print all the info, and
The same situation also occurred on sparc32 and
m68k targets.
So this change do the following:
1) Add a address range argument to help user control
the number of output items.
2) Add warning note in help message that such address
range argument only supported on target i386,
sparc32 and m68k.
3) Print ignore warning when user add such address
range argument on other targets (sh4, sparc64,
ppc and xtensa). Those targets only print limited
tlb info.
Signed-off-by: Alano Song <AlanoSong@163.com>
---
hmp-commands-info.hx | 17 ++++++--
target/i386/monitor.c | 92 ++++++++++++++++++++++++++++-----------
target/m68k/cpu.h | 2 +-
target/m68k/helper.c | 55 +++++++++++++++--------
target/m68k/monitor.c | 15 ++++++-
target/ppc/monitor.c | 6 +++
target/sh4/monitor.c | 5 +++
target/sparc/cpu.h | 3 ++
target/sparc/mmu_helper.c | 40 +++++++++++++----
target/sparc/monitor.c | 23 ++++++++++
target/xtensa/monitor.c | 6 +++
11 files changed, 206 insertions(+), 58 deletions(-)
diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
index 82134eb6c2..f907edf273 100644
--- a/hmp-commands-info.hx
+++ b/hmp-commands-info.hx
@@ -188,17 +188,26 @@ ERST
{
.name = "tlb",
- .args_type = "",
- .params = "",
- .help = "show virtual to physical memory mappings",
+ .args_type = "start:l?,end:l?",
+ .params = "[start [end]]",
+ .help = "show virtual to physical memory mappings. "
+ "output can be extremely large for i386, sparc32 "
+ "and m68k targets. use 'info tlb [start [end]]' "
+ "to show a range of entries. Note that the range "
+ "argument is only supported on i386, sparc32 and "
+ "m68k targets.",
.cmd = hmp_info_tlb,
.arch_bitmask = QEMU_ARCH_I386 | QEMU_ARCH_SH4 | QEMU_ARCH_SPARC \
| QEMU_ARCH_PPC | QEMU_ARCH_XTENSA | QEMU_ARCH_M68K,
},
SRST
- ``info tlb``
+ ``info tlb`` [*start* [*end*]]
Show virtual to physical memory mappings.
+ The output can be extremely large for i386, sparc32 and m68k targets.
+ Use *start* and *end* to print entries located in virtual address
+ range [start, end] (end is optional). Note that the range argument is
+ only supported on i386, sparc32 and m68k targets.
ERST
{
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index a536712c75..8db9ab0576 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -48,11 +48,9 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
return addr;
}
-static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
- hwaddr pte, hwaddr mask)
+static void do_print_pte(Monitor *mon, hwaddr addr,
+ hwaddr pte, hwaddr mask)
{
- 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,
@@ -68,7 +66,25 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
pte & PG_RW_MASK ? 'W' : '-');
}
-static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
+static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
+ hwaddr pte, hwaddr mask, hwaddr size,
+ hwaddr start, hwaddr end)
+{
+ hwaddr addr_start = addr_canonical(env, addr);
+ hwaddr addr_end = addr_canonical(env, addr + size - 1);
+ /*
+ * Print current page [addr_start, addr_end] only if it overlaps the
+ * requested virtual address range [start, end].
+ */
+ if (addr_start > end || addr_end < start) {
+ return;
+ }
+
+ do_print_pte(mon, addr_start, pte, mask);
+}
+
+static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as,
+ hwaddr start, hwaddr end)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
unsigned int l1, l2;
@@ -80,15 +96,16 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
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));
+ print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1),
+ 0x400000, start, end);
} else {
for(l2 = 0; l2 < 1024; l2++) {
pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4,
attrs, NULL);
if (pte & PG_PRESENT_MASK) {
print_pte(mon, env, (l1 << 22) + (l2 << 12),
- pte & ~PG_PSE_MASK,
- ~0xfff);
+ pte & ~PG_PSE_MASK, ~0xfff, 0x1000,
+ start, end);
}
}
}
@@ -96,7 +113,8 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
}
}
-static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
+static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as,
+ hwaddr start, hwaddr end)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
unsigned int l1, l2, l3;
@@ -114,17 +132,19 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
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));
+ ~((hwaddr)(1 << 20) - 1), 0x200000,
+ start, end);
} else {
pt_addr = pde & 0x3fffffffff000ULL;
for (l3 = 0; l3 < 512; l3++) {
pte = address_space_ldq_le(as, pt_addr + l3 * 8,
attrs, NULL);
if (pte & PG_PRESENT_MASK) {
- print_pte(mon, env, (l1 << 30) + (l2 << 21)
- + (l3 << 12),
+ print_pte(mon, env,
+ (l1 << 30) + (l2 << 21) + (l3 << 12),
pte & ~PG_PSE_MASK,
- ~(hwaddr)0xfff);
+ ~(hwaddr)0xfff, 0x1000,
+ start, end);
}
}
}
@@ -136,7 +156,8 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
#ifdef TARGET_X86_64
static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
- uint64_t l0, uint64_t pml4_addr)
+ uint64_t l0, uint64_t pml4_addr, hwaddr start,
+ hwaddr end)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
uint64_t l1, l2, l3, l4;
@@ -159,7 +180,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
if (pdpe & PG_PSE_MASK) {
/* 1G pages, CR4.PSE is ignored */
print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
- pdpe, 0x3ffffc0000000ULL);
+ pdpe, 0x3ffffc0000000ULL, 0x40000000, start, end);
continue;
}
@@ -172,8 +193,9 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
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);
+ print_pte(mon, env,
+ (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21),
+ pde, 0x3ffffffe00000ULL, 0x200000, start, end);
continue;
}
@@ -182,9 +204,11 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
pte = address_space_ldq_le(as, pt_addr + l4 * 8,
attrs, NULL);
if (pte & PG_PRESENT_MASK) {
- print_pte(mon, env, (l0 << 48) + (l1 << 39) +
- (l2 << 30) + (l3 << 21) + (l4 << 12),
- pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
+ print_pte(mon, env,
+ (l0 << 48) + (l1 << 39) + (l2 << 30) +
+ (l3 << 21) + (l4 << 12),
+ pte & ~PG_PSE_MASK,
+ 0x3fffffffff000ULL, 0x1000, start, end);
}
}
}
@@ -192,7 +216,8 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
}
}
-static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
+static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as,
+ hwaddr start, hwaddr end)
{
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
uint64_t l0;
@@ -203,7 +228,8 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
for (l0 = 0; l0 < 512; l0++) {
pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL);
if (pml5e & PG_PRESENT_MASK) {
- tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL);
+ tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL,
+ start, end);
}
}
}
@@ -213,6 +239,18 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
{
CPUArchState *env;
AddressSpace *as;
+ hwaddr start = 0, end = HWADDR_MAX;
+
+ if (qdict_haskey(qdict, "start")) {
+ start = (hwaddr)qdict_get_int(qdict, "start");
+ }
+ if (qdict_haskey(qdict, "end")) {
+ end = (hwaddr)qdict_get_int(qdict, "end");
+ }
+ if (start > end) {
+ monitor_printf(mon, "Invalid address range: start > end.\n");
+ return;
+ }
env = mon_get_cpu_env(mon);
if (!env) {
@@ -229,17 +267,19 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
#ifdef TARGET_X86_64
if (env->hflags & HF_LMA_MASK) {
if (env->cr[4] & CR4_LA57_MASK) {
- tlb_info_la57(mon, env, as);
+ tlb_info_la57(mon, env, as, start, end);
} else {
- tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff000ULL);
+ tlb_info_la48(mon, env, as, 0,
+ env->cr[3] & 0x3fffffffff000ULL,
+ start, end);
}
} else
#endif
{
- tlb_info_pae32(mon, env, as);
+ tlb_info_pae32(mon, env, as, start, end);
}
} else {
- tlb_info_32(mon, env, as);
+ tlb_info_32(mon, env, as, start, end);
}
}
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
index 7cf3791108..b129cb150f 100644
--- a/target/m68k/cpu.h
+++ b/target/m68k/cpu.h
@@ -606,6 +606,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
#define TB_FLAGS_TRACE 16
#define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
-void dump_mmu(CPUM68KState *env);
+void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end);
#endif
diff --git a/target/m68k/helper.c b/target/m68k/helper.c
index 5f91d206f5..1b854c912b 100644
--- a/target/m68k/helper.c
+++ b/target/m68k/helper.c
@@ -461,27 +461,46 @@ void m68k_switch_sp(CPUM68KState *env)
/* MMU: 68040 only */
static void print_address_zone(uint32_t logical, uint32_t physical,
- uint32_t size, int attr)
+ uint32_t size, int attr,
+ hwaddr start, hwaddr end)
{
+ uint64_t zone_start = logical;
+ uint64_t zone_end = zone_start + size - 1;
+ uint64_t zone_len;
+
+ /*
+ * Print current zone [zone_start, zone_end] only if it overlaps the
+ * requested virtual address range [start, end].
+ */
+ if (zone_end < start || zone_start > end) {
+ return;
+ }
+
+ physical += (uint32_t)(start > zone_start ? start - zone_start : 0);
+ zone_start = zone_start > start ? zone_start : start;
+ zone_end = zone_end < end ? zone_end : end;
+ zone_len = zone_end - zone_start + 1;
+
qemu_printf("%08x - %08x -> %08x - %08x %c ",
- logical, logical + size - 1,
- physical, physical + size - 1,
+ (uint32_t)zone_start, (uint32_t)zone_end,
+ physical, physical + (uint32_t)(zone_len - 1),
attr & 4 ? 'W' : '-');
- size >>= 10;
- if (size < 1024) {
- qemu_printf("(%d KiB)\n", size);
+ zone_len >>= 10;
+ if (zone_len < 1024) {
+ qemu_printf("(%d KiB)\n", (int)zone_len);
} else {
- size >>= 10;
- if (size < 1024) {
- qemu_printf("(%d MiB)\n", size);
+ zone_len >>= 10;
+ if (zone_len < 1024) {
+ qemu_printf("(%d MiB)\n", (int)zone_len);
} else {
- size >>= 10;
- qemu_printf("(%d GiB)\n", size);
+ zone_len >>= 10;
+ qemu_printf("(%d GiB)\n", (int)zone_len);
}
}
}
-static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
+static void dump_address_map(CPUM68KState *env, uint32_t root_pointer,
+ hwaddr start, hwaddr end)
{
int tic_size, tic_shift;
uint32_t tib_mask;
@@ -550,7 +569,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
size = last_logical + (1 << tic_shift) -
first_logical;
print_address_zone(first_logical,
- first_physical, size, last_attr);
+ first_physical, size, last_attr,
+ start, end);
}
first_logical = logical;
first_physical = physical;
@@ -560,7 +580,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
}
if (first_logical != logical || (attr & 4) != (last_attr & 4)) {
size = logical + (1 << tic_shift) - first_logical;
- print_address_zone(first_logical, first_physical, size, last_attr);
+ print_address_zone(first_logical, first_physical, size, last_attr,
+ start, end);
}
}
@@ -610,7 +631,7 @@ static void dump_ttr(uint32_t ttr)
M68K_DESC_USERATTR_SHIFT);
}
-void dump_mmu(CPUM68KState *env)
+void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end)
{
if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
qemu_printf("Translation disabled\n");
@@ -675,10 +696,10 @@ void dump_mmu(CPUM68KState *env)
dump_ttr(env->mmu.ttr[M68K_DTTR1]);
qemu_printf("SRP: 0x%08x\n", env->mmu.srp);
- dump_address_map(env, env->mmu.srp);
+ dump_address_map(env, env->mmu.srp, start, end);
qemu_printf("URP: 0x%08x\n", env->mmu.urp);
- dump_address_map(env, env->mmu.urp);
+ dump_address_map(env, env->mmu.urp, start, end);
}
static int check_TTR(uint32_t ttr, int *prot, target_ulong addr,
diff --git a/target/m68k/monitor.c b/target/m68k/monitor.c
index 3e0df40a6b..bcb528d8e3 100644
--- a/target/m68k/monitor.c
+++ b/target/m68k/monitor.c
@@ -9,15 +9,28 @@
#include "cpu.h"
#include "monitor/hmp.h"
#include "monitor/monitor.h"
+#include "qobject/qdict.h"
void hmp_info_tlb(Monitor *mon, const QDict *qdict)
{
CPUArchState *env1 = mon_get_cpu_env(mon);
+ hwaddr start = 0, end = HWADDR_MAX;
if (!env1) {
monitor_printf(mon, "No CPU available\n");
return;
}
- dump_mmu(env1);
+ if (qdict_haskey(qdict, "start")) {
+ start = (hwaddr)qdict_get_int(qdict, "start");
+ }
+ if (qdict_haskey(qdict, "end")) {
+ end = (hwaddr)qdict_get_int(qdict, "end");
+ }
+ if (start > end) {
+ monitor_printf(mon, "Invalid address range: start > end.\n");
+ return;
+ }
+
+ dump_mmu(env1, start, end);
}
diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c
index 7c88e0e2bd..753ed6b93d 100644
--- a/target/ppc/monitor.c
+++ b/target/ppc/monitor.c
@@ -10,6 +10,7 @@
#include "monitor/monitor.h"
#include "monitor/hmp.h"
#include "cpu.h"
+#include "qobject/qdict.h"
void hmp_info_tlb(Monitor *mon, const QDict *qdict)
{
@@ -19,5 +20,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "No CPU available\n");
return;
}
+
+ if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
+ monitor_printf(mon, "The range arguments will be ignored.\n");
+ }
+
dump_mmu(env1);
}
diff --git a/target/sh4/monitor.c b/target/sh4/monitor.c
index 50324d3600..c804824bab 100644
--- a/target/sh4/monitor.c
+++ b/target/sh4/monitor.c
@@ -25,6 +25,7 @@
#include "cpu.h"
#include "monitor/monitor.h"
#include "monitor/hmp.h"
+#include "qobject/qdict.h"
static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
{
@@ -48,6 +49,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
return;
}
+ if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
+ monitor_printf(mon, "The range arguments will be ignored.\n");
+ }
+
monitor_printf (mon, "ITLB:\n");
for (i = 0 ; i < ITLB_SIZE ; i++)
print_tlb (mon, i, &env->itlb[i]);
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index 5f583ed9de..11f94b6bd8 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -601,6 +601,9 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
bool probe, uintptr_t retaddr);
target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
void dump_mmu(CPUSPARCState *env);
+#ifndef TARGET_SPARC64
+void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end);
+#endif
#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 07ba25dfce..65a30b438b 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -354,7 +354,32 @@ target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev)
return 0;
}
+static void print_tlb_entry(target_ulong va, hwaddr pa, uint32_t pde,
+ const char *indent, const char *kind,
+ hwaddr size, hwaddr start, hwaddr end)
+{
+ hwaddr va_hw_start = (hwaddr)va;
+ hwaddr va_hw_end = (hwaddr)(va + size - 1);
+
+ /*
+ * Print current region [va_hw_start, va_hw_end] only if it overlaps the
+ * requested virtual address range [start, end].
+ */
+ if (va_hw_start > end || va_hw_end < start) {
+ return;
+ }
+
+ qemu_printf("%sVA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
+ " %s: " TARGET_FMT_lx "\n",
+ indent, va, pa, kind, pde);
+}
+
void dump_mmu(CPUSPARCState *env)
+{
+ dump_mmu_range(env, 0, HWADDR_MAX);
+}
+
+void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end)
{
CPUState *cs = env_cpu(env);
target_ulong va, va1, va2;
@@ -373,8 +398,8 @@ void dump_mmu(CPUSPARCState *env)
} else {
pa = tres.physaddr;
}
- qemu_printf("VA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
- " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
+ print_tlb_entry(va, pa, pde, "", "PDE", 16 * 1024 * 1024,
+ start, end);
for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
pde = mmu_probe(env, va1, 1);
if (pde) {
@@ -383,9 +408,8 @@ void dump_mmu(CPUSPARCState *env)
} else {
pa = tres.physaddr;
}
- qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
- HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n",
- va1, pa, pde);
+ print_tlb_entry(va1, pa, pde, " ", "PDE", 256 * 1024,
+ start, end);
for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
pde = mmu_probe(env, va2, 0);
if (pde) {
@@ -394,10 +418,8 @@ void dump_mmu(CPUSPARCState *env)
} else {
pa = tres.physaddr;
}
- qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
- HWADDR_FMT_plx " PTE: "
- TARGET_FMT_lx "\n",
- va2, pa, pde);
+ print_tlb_entry(va2, pa, pde, " ", "PTE",
+ 4 * 1024, start, end);
}
}
}
diff --git a/target/sparc/monitor.c b/target/sparc/monitor.c
index 36f3d8d58e..993e411a37 100644
--- a/target/sparc/monitor.c
+++ b/target/sparc/monitor.c
@@ -25,6 +25,7 @@
#include "cpu.h"
#include "monitor/monitor.h"
#include "monitor/hmp.h"
+#include "qobject/qdict.h"
void hmp_info_tlb(Monitor *mon, const QDict *qdict)
@@ -35,5 +36,27 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "No CPU available\n");
return;
}
+
+#ifdef TARGET_SPARC64
+ if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
+ monitor_printf(mon, "The range arguments will be ignored.\n");
+ }
+
dump_mmu(env1);
+#else
+ hwaddr start = 0, end = HWADDR_MAX;
+
+ if (qdict_haskey(qdict, "start")) {
+ start = (hwaddr)qdict_get_int(qdict, "start");
+ }
+ if (qdict_haskey(qdict, "end")) {
+ end = (hwaddr)qdict_get_int(qdict, "end");
+ }
+ if (start > end) {
+ monitor_printf(mon, "Invalid address range: start > end.\n");
+ return;
+ }
+
+ dump_mmu_range(env1, start, end);
+#endif
}
diff --git a/target/xtensa/monitor.c b/target/xtensa/monitor.c
index 2af84934f8..df572cba42 100644
--- a/target/xtensa/monitor.c
+++ b/target/xtensa/monitor.c
@@ -25,6 +25,7 @@
#include "cpu.h"
#include "monitor/monitor.h"
#include "monitor/hmp.h"
+#include "qobject/qdict.h"
void hmp_info_tlb(Monitor *mon, const QDict *qdict)
{
@@ -34,5 +35,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "No CPU available\n");
return;
}
+
+ if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
+ monitor_printf(mon, "The range arguments will be ignored.\n");
+ }
+
dump_mmu(env1);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v3] monitor: Refine 'info tlb' command
2026-08-18 13:25 [PATCH v3] monitor: Refine 'info tlb' command AlanoSong
@ 2026-08-18 13:36 ` Philippe Mathieu-Daudé
2026-08-18 15:12 ` Marc-André Lureau
1 sibling, 0 replies; 4+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-08-18 13:36 UTC (permalink / raw)
To: AlanoSong, qemu-devel, Marc-André Lureau
Cc: dave, laurent, zhao1.liu, atar4qemu
Cc'ing Marc-André
On 18/8/26 15:25, AlanoSong@163.com wrote:
> When booting an i386 target, the 'info tlb' command
> may walk the entire page table hierarchy and emit
> an enormous amount of output. It will take dozens of
> minutes to print all the info, and
> The same situation also occurred on sparc32 and
> m68k targets.
>
> So this change do the following:
> 1) Add a address range argument to help user control
> the number of output items.
> 2) Add warning note in help message that such address
> range argument only supported on target i386,
> sparc32 and m68k.
> 3) Print ignore warning when user add such address
> range argument on other targets (sh4, sparc64,
> ppc and xtensa). Those targets only print limited
> tlb info.
>
> Signed-off-by: Alano Song <AlanoSong@163.com>
> ---
> hmp-commands-info.hx | 17 ++++++--
> target/i386/monitor.c | 92 ++++++++++++++++++++++++++++-----------
> target/m68k/cpu.h | 2 +-
> target/m68k/helper.c | 55 +++++++++++++++--------
> target/m68k/monitor.c | 15 ++++++-
> target/ppc/monitor.c | 6 +++
> target/sh4/monitor.c | 5 +++
> target/sparc/cpu.h | 3 ++
> target/sparc/mmu_helper.c | 40 +++++++++++++----
> target/sparc/monitor.c | 23 ++++++++++
> target/xtensa/monitor.c | 6 +++
> 11 files changed, 206 insertions(+), 58 deletions(-)
>
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 82134eb6c2..f907edf273 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -188,17 +188,26 @@ ERST
>
> {
> .name = "tlb",
> - .args_type = "",
> - .params = "",
> - .help = "show virtual to physical memory mappings",
> + .args_type = "start:l?,end:l?",
> + .params = "[start [end]]",
> + .help = "show virtual to physical memory mappings. "
> + "output can be extremely large for i386, sparc32 "
> + "and m68k targets. use 'info tlb [start [end]]' "
> + "to show a range of entries. Note that the range "
> + "argument is only supported on i386, sparc32 and "
> + "m68k targets.",
> .cmd = hmp_info_tlb,
> .arch_bitmask = QEMU_ARCH_I386 | QEMU_ARCH_SH4 | QEMU_ARCH_SPARC \
> | QEMU_ARCH_PPC | QEMU_ARCH_XTENSA | QEMU_ARCH_M68K,
> },
>
> SRST
> - ``info tlb``
> + ``info tlb`` [*start* [*end*]]
> Show virtual to physical memory mappings.
> + The output can be extremely large for i386, sparc32 and m68k targets.
> + Use *start* and *end* to print entries located in virtual address
> + range [start, end] (end is optional). Note that the range argument is
> + only supported on i386, sparc32 and m68k targets.
> ERST
>
> {
> diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> index a536712c75..8db9ab0576 100644
> --- a/target/i386/monitor.c
> +++ b/target/i386/monitor.c
> @@ -48,11 +48,9 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
> return addr;
> }
>
> -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> - hwaddr pte, hwaddr mask)
> +static void do_print_pte(Monitor *mon, hwaddr addr,
> + hwaddr pte, hwaddr mask)
> {
> - 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,
> @@ -68,7 +66,25 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> pte & PG_RW_MASK ? 'W' : '-');
> }
>
> -static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> + hwaddr pte, hwaddr mask, hwaddr size,
> + hwaddr start, hwaddr end)
> +{
> + hwaddr addr_start = addr_canonical(env, addr);
> + hwaddr addr_end = addr_canonical(env, addr + size - 1);
> + /*
> + * Print current page [addr_start, addr_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (addr_start > end || addr_end < start) {
> + return;
> + }
> +
> + do_print_pte(mon, addr_start, pte, mask);
> +}
> +
> +static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> unsigned int l1, l2;
> @@ -80,15 +96,16 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> 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));
> + print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1),
> + 0x400000, start, end);
> } else {
> for(l2 = 0; l2 < 1024; l2++) {
> pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> print_pte(mon, env, (l1 << 22) + (l2 << 12),
> - pte & ~PG_PSE_MASK,
> - ~0xfff);
> + pte & ~PG_PSE_MASK, ~0xfff, 0x1000,
> + start, end);
> }
> }
> }
> @@ -96,7 +113,8 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> }
> }
>
> -static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> unsigned int l1, l2, l3;
> @@ -114,17 +132,19 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> 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));
> + ~((hwaddr)(1 << 20) - 1), 0x200000,
> + start, end);
> } else {
> pt_addr = pde & 0x3fffffffff000ULL;
> for (l3 = 0; l3 < 512; l3++) {
> pte = address_space_ldq_le(as, pt_addr + l3 * 8,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> - print_pte(mon, env, (l1 << 30) + (l2 << 21)
> - + (l3 << 12),
> + print_pte(mon, env,
> + (l1 << 30) + (l2 << 21) + (l3 << 12),
> pte & ~PG_PSE_MASK,
> - ~(hwaddr)0xfff);
> + ~(hwaddr)0xfff, 0x1000,
> + start, end);
> }
> }
> }
> @@ -136,7 +156,8 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
>
> #ifdef TARGET_X86_64
> static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> - uint64_t l0, uint64_t pml4_addr)
> + uint64_t l0, uint64_t pml4_addr, hwaddr start,
> + hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> uint64_t l1, l2, l3, l4;
> @@ -159,7 +180,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> if (pdpe & PG_PSE_MASK) {
> /* 1G pages, CR4.PSE is ignored */
> print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
> - pdpe, 0x3ffffc0000000ULL);
> + pdpe, 0x3ffffc0000000ULL, 0x40000000, start, end);
> continue;
> }
>
> @@ -172,8 +193,9 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
>
> 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);
> + print_pte(mon, env,
> + (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21),
> + pde, 0x3ffffffe00000ULL, 0x200000, start, end);
> continue;
> }
>
> @@ -182,9 +204,11 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> pte = address_space_ldq_le(as, pt_addr + l4 * 8,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> - print_pte(mon, env, (l0 << 48) + (l1 << 39) +
> - (l2 << 30) + (l3 << 21) + (l4 << 12),
> - pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
> + print_pte(mon, env,
> + (l0 << 48) + (l1 << 39) + (l2 << 30) +
> + (l3 << 21) + (l4 << 12),
> + pte & ~PG_PSE_MASK,
> + 0x3fffffffff000ULL, 0x1000, start, end);
> }
> }
> }
> @@ -192,7 +216,8 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> }
> }
>
> -static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> uint64_t l0;
> @@ -203,7 +228,8 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> for (l0 = 0; l0 < 512; l0++) {
> pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL);
> if (pml5e & PG_PRESENT_MASK) {
> - tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL);
> + tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL,
> + start, end);
> }
> }
> }
> @@ -213,6 +239,18 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> CPUArchState *env;
> AddressSpace *as;
> + hwaddr start = 0, end = HWADDR_MAX;
> +
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
>
> env = mon_get_cpu_env(mon);
> if (!env) {
> @@ -229,17 +267,19 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> #ifdef TARGET_X86_64
> if (env->hflags & HF_LMA_MASK) {
> if (env->cr[4] & CR4_LA57_MASK) {
> - tlb_info_la57(mon, env, as);
> + tlb_info_la57(mon, env, as, start, end);
> } else {
> - tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff000ULL);
> + tlb_info_la48(mon, env, as, 0,
> + env->cr[3] & 0x3fffffffff000ULL,
> + start, end);
> }
> } else
> #endif
> {
> - tlb_info_pae32(mon, env, as);
> + tlb_info_pae32(mon, env, as, start, end);
> }
> } else {
> - tlb_info_32(mon, env, as);
> + tlb_info_32(mon, env, as, start, end);
> }
> }
>
> diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
> index 7cf3791108..b129cb150f 100644
> --- a/target/m68k/cpu.h
> +++ b/target/m68k/cpu.h
> @@ -606,6 +606,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
> #define TB_FLAGS_TRACE 16
> #define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
>
> -void dump_mmu(CPUM68KState *env);
> +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end);
>
> #endif
> diff --git a/target/m68k/helper.c b/target/m68k/helper.c
> index 5f91d206f5..1b854c912b 100644
> --- a/target/m68k/helper.c
> +++ b/target/m68k/helper.c
> @@ -461,27 +461,46 @@ void m68k_switch_sp(CPUM68KState *env)
> /* MMU: 68040 only */
>
> static void print_address_zone(uint32_t logical, uint32_t physical,
> - uint32_t size, int attr)
> + uint32_t size, int attr,
> + hwaddr start, hwaddr end)
> {
> + uint64_t zone_start = logical;
> + uint64_t zone_end = zone_start + size - 1;
> + uint64_t zone_len;
> +
> + /*
> + * Print current zone [zone_start, zone_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (zone_end < start || zone_start > end) {
> + return;
> + }
> +
> + physical += (uint32_t)(start > zone_start ? start - zone_start : 0);
> + zone_start = zone_start > start ? zone_start : start;
> + zone_end = zone_end < end ? zone_end : end;
> + zone_len = zone_end - zone_start + 1;
> +
> qemu_printf("%08x - %08x -> %08x - %08x %c ",
> - logical, logical + size - 1,
> - physical, physical + size - 1,
> + (uint32_t)zone_start, (uint32_t)zone_end,
> + physical, physical + (uint32_t)(zone_len - 1),
> attr & 4 ? 'W' : '-');
> - size >>= 10;
> - if (size < 1024) {
> - qemu_printf("(%d KiB)\n", size);
> + zone_len >>= 10;
> + if (zone_len < 1024) {
> + qemu_printf("(%d KiB)\n", (int)zone_len);
> } else {
> - size >>= 10;
> - if (size < 1024) {
> - qemu_printf("(%d MiB)\n", size);
> + zone_len >>= 10;
> + if (zone_len < 1024) {
> + qemu_printf("(%d MiB)\n", (int)zone_len);
> } else {
> - size >>= 10;
> - qemu_printf("(%d GiB)\n", size);
> + zone_len >>= 10;
> + qemu_printf("(%d GiB)\n", (int)zone_len);
> }
> }
> }
>
> -static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> +static void dump_address_map(CPUM68KState *env, uint32_t root_pointer,
> + hwaddr start, hwaddr end)
> {
> int tic_size, tic_shift;
> uint32_t tib_mask;
> @@ -550,7 +569,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> size = last_logical + (1 << tic_shift) -
> first_logical;
> print_address_zone(first_logical,
> - first_physical, size, last_attr);
> + first_physical, size, last_attr,
> + start, end);
> }
> first_logical = logical;
> first_physical = physical;
> @@ -560,7 +580,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> }
> if (first_logical != logical || (attr & 4) != (last_attr & 4)) {
> size = logical + (1 << tic_shift) - first_logical;
> - print_address_zone(first_logical, first_physical, size, last_attr);
> + print_address_zone(first_logical, first_physical, size, last_attr,
> + start, end);
> }
> }
>
> @@ -610,7 +631,7 @@ static void dump_ttr(uint32_t ttr)
> M68K_DESC_USERATTR_SHIFT);
> }
>
> -void dump_mmu(CPUM68KState *env)
> +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end)
> {
> if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
> qemu_printf("Translation disabled\n");
> @@ -675,10 +696,10 @@ void dump_mmu(CPUM68KState *env)
> dump_ttr(env->mmu.ttr[M68K_DTTR1]);
>
> qemu_printf("SRP: 0x%08x\n", env->mmu.srp);
> - dump_address_map(env, env->mmu.srp);
> + dump_address_map(env, env->mmu.srp, start, end);
>
> qemu_printf("URP: 0x%08x\n", env->mmu.urp);
> - dump_address_map(env, env->mmu.urp);
> + dump_address_map(env, env->mmu.urp, start, end);
> }
>
> static int check_TTR(uint32_t ttr, int *prot, target_ulong addr,
> diff --git a/target/m68k/monitor.c b/target/m68k/monitor.c
> index 3e0df40a6b..bcb528d8e3 100644
> --- a/target/m68k/monitor.c
> +++ b/target/m68k/monitor.c
> @@ -9,15 +9,28 @@
> #include "cpu.h"
> #include "monitor/hmp.h"
> #include "monitor/monitor.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> CPUArchState *env1 = mon_get_cpu_env(mon);
> + hwaddr start = 0, end = HWADDR_MAX;
>
> if (!env1) {
> monitor_printf(mon, "No CPU available\n");
> return;
> }
>
> - dump_mmu(env1);
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
> +
> + dump_mmu(env1, start, end);
> }
> diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c
> index 7c88e0e2bd..753ed6b93d 100644
> --- a/target/ppc/monitor.c
> +++ b/target/ppc/monitor.c
> @@ -10,6 +10,7 @@
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> #include "cpu.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> @@ -19,5 +20,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> }
> diff --git a/target/sh4/monitor.c b/target/sh4/monitor.c
> index 50324d3600..c804824bab 100644
> --- a/target/sh4/monitor.c
> +++ b/target/sh4/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
> static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
> {
> @@ -48,6 +49,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> return;
> }
>
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> monitor_printf (mon, "ITLB:\n");
> for (i = 0 ; i < ITLB_SIZE ; i++)
> print_tlb (mon, i, &env->itlb[i]);
> diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
> index 5f583ed9de..11f94b6bd8 100644
> --- a/target/sparc/cpu.h
> +++ b/target/sparc/cpu.h
> @@ -601,6 +601,9 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> bool probe, uintptr_t retaddr);
> target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
> void dump_mmu(CPUSPARCState *env);
> +#ifndef TARGET_SPARC64
> +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end);
> +#endif
>
> #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
> int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
> diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
> index 07ba25dfce..65a30b438b 100644
> --- a/target/sparc/mmu_helper.c
> +++ b/target/sparc/mmu_helper.c
> @@ -354,7 +354,32 @@ target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev)
> return 0;
> }
>
> +static void print_tlb_entry(target_ulong va, hwaddr pa, uint32_t pde,
> + const char *indent, const char *kind,
> + hwaddr size, hwaddr start, hwaddr end)
> +{
> + hwaddr va_hw_start = (hwaddr)va;
> + hwaddr va_hw_end = (hwaddr)(va + size - 1);
> +
> + /*
> + * Print current region [va_hw_start, va_hw_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (va_hw_start > end || va_hw_end < start) {
> + return;
> + }
> +
> + qemu_printf("%sVA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> + " %s: " TARGET_FMT_lx "\n",
> + indent, va, pa, kind, pde);
> +}
> +
> void dump_mmu(CPUSPARCState *env)
> +{
> + dump_mmu_range(env, 0, HWADDR_MAX);
> +}
> +
> +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end)
> {
> CPUState *cs = env_cpu(env);
> target_ulong va, va1, va2;
> @@ -373,8 +398,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf("VA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> - " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
> + print_tlb_entry(va, pa, pde, "", "PDE", 16 * 1024 * 1024,
> + start, end);
> for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
> pde = mmu_probe(env, va1, 1);
> if (pde) {
> @@ -383,9 +408,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> - HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n",
> - va1, pa, pde);
> + print_tlb_entry(va1, pa, pde, " ", "PDE", 256 * 1024,
> + start, end);
> for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
> pde = mmu_probe(env, va2, 0);
> if (pde) {
> @@ -394,10 +418,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> - HWADDR_FMT_plx " PTE: "
> - TARGET_FMT_lx "\n",
> - va2, pa, pde);
> + print_tlb_entry(va2, pa, pde, " ", "PTE",
> + 4 * 1024, start, end);
> }
> }
> }
> diff --git a/target/sparc/monitor.c b/target/sparc/monitor.c
> index 36f3d8d58e..993e411a37 100644
> --- a/target/sparc/monitor.c
> +++ b/target/sparc/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> @@ -35,5 +36,27 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> +#ifdef TARGET_SPARC64
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> +#else
> + hwaddr start = 0, end = HWADDR_MAX;
> +
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
> +
> + dump_mmu_range(env1, start, end);
> +#endif
> }
> diff --git a/target/xtensa/monitor.c b/target/xtensa/monitor.c
> index 2af84934f8..df572cba42 100644
> --- a/target/xtensa/monitor.c
> +++ b/target/xtensa/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> @@ -34,5 +35,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> }
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v3] monitor: Refine 'info tlb' command
2026-08-18 13:25 [PATCH v3] monitor: Refine 'info tlb' command AlanoSong
2026-08-18 13:36 ` Philippe Mathieu-Daudé
@ 2026-08-18 15:12 ` Marc-André Lureau
2026-08-18 23:08 ` Dr. David Alan Gilbert
1 sibling, 1 reply; 4+ messages in thread
From: Marc-André Lureau @ 2026-08-18 15:12 UTC (permalink / raw)
To: AlanoSong; +Cc: qemu-devel, dave, laurent, zhao1.liu, atar4qemu
Hi
On Tue, Aug 18, 2026 at 5:30 PM <AlanoSong@163.com> wrote:
>
> When booting an i386 target, the 'info tlb' command
> may walk the entire page table hierarchy and emit
> an enormous amount of output. It will take dozens of
> minutes to print all the info, and
> The same situation also occurred on sparc32 and
> m68k targets.
>
> So this change do the following:
> 1) Add a address range argument to help user control
> the number of output items.
> 2) Add warning note in help message that such address
> range argument only supported on target i386,
> sparc32 and m68k.
> 3) Print ignore warning when user add such address
> range argument on other targets (sh4, sparc64,
> ppc and xtensa). Those targets only print limited
> tlb info.
>
> Signed-off-by: Alano Song <AlanoSong@163.com>
lgtm overall
- m68k print_address_zone() used to be called with page-aligned zones,
but now it is clipped to arbitrary user ranges, which may not be
aligned. 1 byte range will be reported as 0kb. We may want to handle
the case for small ranges. Btw that code could use the macros KiB for
readability
- A later patch could prune non-overlapping ranges during traversal,
not just during printing
> ---
> hmp-commands-info.hx | 17 ++++++--
> target/i386/monitor.c | 92 ++++++++++++++++++++++++++++-----------
> target/m68k/cpu.h | 2 +-
> target/m68k/helper.c | 55 +++++++++++++++--------
> target/m68k/monitor.c | 15 ++++++-
> target/ppc/monitor.c | 6 +++
> target/sh4/monitor.c | 5 +++
> target/sparc/cpu.h | 3 ++
> target/sparc/mmu_helper.c | 40 +++++++++++++----
> target/sparc/monitor.c | 23 ++++++++++
> target/xtensa/monitor.c | 6 +++
> 11 files changed, 206 insertions(+), 58 deletions(-)
>
> diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> index 82134eb6c2..f907edf273 100644
> --- a/hmp-commands-info.hx
> +++ b/hmp-commands-info.hx
> @@ -188,17 +188,26 @@ ERST
>
> {
> .name = "tlb",
> - .args_type = "",
> - .params = "",
> - .help = "show virtual to physical memory mappings",
> + .args_type = "start:l?,end:l?",
> + .params = "[start [end]]",
> + .help = "show virtual to physical memory mappings. "
> + "output can be extremely large for i386, sparc32 "
> + "and m68k targets. use 'info tlb [start [end]]' "
> + "to show a range of entries. Note that the range "
> + "argument is only supported on i386, sparc32 and "
> + "m68k targets.",
> .cmd = hmp_info_tlb,
> .arch_bitmask = QEMU_ARCH_I386 | QEMU_ARCH_SH4 | QEMU_ARCH_SPARC \
> | QEMU_ARCH_PPC | QEMU_ARCH_XTENSA | QEMU_ARCH_M68K,
> },
>
> SRST
> - ``info tlb``
> + ``info tlb`` [*start* [*end*]]
> Show virtual to physical memory mappings.
> + The output can be extremely large for i386, sparc32 and m68k targets.
> + Use *start* and *end* to print entries located in virtual address
> + range [start, end] (end is optional). Note that the range argument is
> + only supported on i386, sparc32 and m68k targets.
> ERST
>
> {
> diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> index a536712c75..8db9ab0576 100644
> --- a/target/i386/monitor.c
> +++ b/target/i386/monitor.c
> @@ -48,11 +48,9 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
> return addr;
> }
>
> -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> - hwaddr pte, hwaddr mask)
> +static void do_print_pte(Monitor *mon, hwaddr addr,
> + hwaddr pte, hwaddr mask)
> {
> - 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,
> @@ -68,7 +66,25 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> pte & PG_RW_MASK ? 'W' : '-');
> }
>
> -static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> + hwaddr pte, hwaddr mask, hwaddr size,
> + hwaddr start, hwaddr end)
> +{
> + hwaddr addr_start = addr_canonical(env, addr);
> + hwaddr addr_end = addr_canonical(env, addr + size - 1);
> + /*
> + * Print current page [addr_start, addr_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (addr_start > end || addr_end < start) {
> + return;
> + }
> +
> + do_print_pte(mon, addr_start, pte, mask);
> +}
> +
> +static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> unsigned int l1, l2;
> @@ -80,15 +96,16 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> 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));
> + print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1),
> + 0x400000, start, end);
> } else {
> for(l2 = 0; l2 < 1024; l2++) {
> pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> print_pte(mon, env, (l1 << 22) + (l2 << 12),
> - pte & ~PG_PSE_MASK,
> - ~0xfff);
> + pte & ~PG_PSE_MASK, ~0xfff, 0x1000,
> + start, end);
> }
> }
> }
> @@ -96,7 +113,8 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> }
> }
>
> -static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> unsigned int l1, l2, l3;
> @@ -114,17 +132,19 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> 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));
> + ~((hwaddr)(1 << 20) - 1), 0x200000,
> + start, end);
> } else {
> pt_addr = pde & 0x3fffffffff000ULL;
> for (l3 = 0; l3 < 512; l3++) {
> pte = address_space_ldq_le(as, pt_addr + l3 * 8,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> - print_pte(mon, env, (l1 << 30) + (l2 << 21)
> - + (l3 << 12),
> + print_pte(mon, env,
> + (l1 << 30) + (l2 << 21) + (l3 << 12),
> pte & ~PG_PSE_MASK,
> - ~(hwaddr)0xfff);
> + ~(hwaddr)0xfff, 0x1000,
> + start, end);
> }
> }
> }
> @@ -136,7 +156,8 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
>
> #ifdef TARGET_X86_64
> static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> - uint64_t l0, uint64_t pml4_addr)
> + uint64_t l0, uint64_t pml4_addr, hwaddr start,
> + hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> uint64_t l1, l2, l3, l4;
> @@ -159,7 +180,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> if (pdpe & PG_PSE_MASK) {
> /* 1G pages, CR4.PSE is ignored */
> print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
> - pdpe, 0x3ffffc0000000ULL);
> + pdpe, 0x3ffffc0000000ULL, 0x40000000, start, end);
> continue;
> }
>
> @@ -172,8 +193,9 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
>
> 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);
> + print_pte(mon, env,
> + (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21),
> + pde, 0x3ffffffe00000ULL, 0x200000, start, end);
> continue;
> }
>
> @@ -182,9 +204,11 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> pte = address_space_ldq_le(as, pt_addr + l4 * 8,
> attrs, NULL);
> if (pte & PG_PRESENT_MASK) {
> - print_pte(mon, env, (l0 << 48) + (l1 << 39) +
> - (l2 << 30) + (l3 << 21) + (l4 << 12),
> - pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
> + print_pte(mon, env,
> + (l0 << 48) + (l1 << 39) + (l2 << 30) +
> + (l3 << 21) + (l4 << 12),
> + pte & ~PG_PSE_MASK,
> + 0x3fffffffff000ULL, 0x1000, start, end);
> }
> }
> }
> @@ -192,7 +216,8 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> }
> }
>
> -static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> +static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as,
> + hwaddr start, hwaddr end)
> {
> const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> uint64_t l0;
> @@ -203,7 +228,8 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> for (l0 = 0; l0 < 512; l0++) {
> pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL);
> if (pml5e & PG_PRESENT_MASK) {
> - tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL);
> + tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL,
> + start, end);
> }
> }
> }
> @@ -213,6 +239,18 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> CPUArchState *env;
> AddressSpace *as;
> + hwaddr start = 0, end = HWADDR_MAX;
> +
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
>
> env = mon_get_cpu_env(mon);
> if (!env) {
> @@ -229,17 +267,19 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> #ifdef TARGET_X86_64
> if (env->hflags & HF_LMA_MASK) {
> if (env->cr[4] & CR4_LA57_MASK) {
> - tlb_info_la57(mon, env, as);
> + tlb_info_la57(mon, env, as, start, end);
> } else {
> - tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff000ULL);
> + tlb_info_la48(mon, env, as, 0,
> + env->cr[3] & 0x3fffffffff000ULL,
> + start, end);
> }
> } else
> #endif
> {
> - tlb_info_pae32(mon, env, as);
> + tlb_info_pae32(mon, env, as, start, end);
> }
> } else {
> - tlb_info_32(mon, env, as);
> + tlb_info_32(mon, env, as, start, end);
> }
> }
>
> diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
> index 7cf3791108..b129cb150f 100644
> --- a/target/m68k/cpu.h
> +++ b/target/m68k/cpu.h
> @@ -606,6 +606,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
> #define TB_FLAGS_TRACE 16
> #define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
>
> -void dump_mmu(CPUM68KState *env);
> +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end);
>
> #endif
> diff --git a/target/m68k/helper.c b/target/m68k/helper.c
> index 5f91d206f5..1b854c912b 100644
> --- a/target/m68k/helper.c
> +++ b/target/m68k/helper.c
> @@ -461,27 +461,46 @@ void m68k_switch_sp(CPUM68KState *env)
> /* MMU: 68040 only */
>
> static void print_address_zone(uint32_t logical, uint32_t physical,
> - uint32_t size, int attr)
> + uint32_t size, int attr,
> + hwaddr start, hwaddr end)
> {
> + uint64_t zone_start = logical;
> + uint64_t zone_end = zone_start + size - 1;
> + uint64_t zone_len;
> +
> + /*
> + * Print current zone [zone_start, zone_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (zone_end < start || zone_start > end) {
> + return;
> + }
> +
> + physical += (uint32_t)(start > zone_start ? start - zone_start : 0);
> + zone_start = zone_start > start ? zone_start : start;
> + zone_end = zone_end < end ? zone_end : end;
> + zone_len = zone_end - zone_start + 1;
> +
> qemu_printf("%08x - %08x -> %08x - %08x %c ",
> - logical, logical + size - 1,
> - physical, physical + size - 1,
> + (uint32_t)zone_start, (uint32_t)zone_end,
> + physical, physical + (uint32_t)(zone_len - 1),
> attr & 4 ? 'W' : '-');
> - size >>= 10;
> - if (size < 1024) {
> - qemu_printf("(%d KiB)\n", size);
> + zone_len >>= 10;
> + if (zone_len < 1024) {
> + qemu_printf("(%d KiB)\n", (int)zone_len);
> } else {
> - size >>= 10;
> - if (size < 1024) {
> - qemu_printf("(%d MiB)\n", size);
> + zone_len >>= 10;
> + if (zone_len < 1024) {
> + qemu_printf("(%d MiB)\n", (int)zone_len);
> } else {
> - size >>= 10;
> - qemu_printf("(%d GiB)\n", size);
> + zone_len >>= 10;
> + qemu_printf("(%d GiB)\n", (int)zone_len);
> }
> }
> }
>
> -static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> +static void dump_address_map(CPUM68KState *env, uint32_t root_pointer,
> + hwaddr start, hwaddr end)
> {
> int tic_size, tic_shift;
> uint32_t tib_mask;
> @@ -550,7 +569,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> size = last_logical + (1 << tic_shift) -
> first_logical;
> print_address_zone(first_logical,
> - first_physical, size, last_attr);
> + first_physical, size, last_attr,
> + start, end);
> }
> first_logical = logical;
> first_physical = physical;
> @@ -560,7 +580,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> }
> if (first_logical != logical || (attr & 4) != (last_attr & 4)) {
> size = logical + (1 << tic_shift) - first_logical;
> - print_address_zone(first_logical, first_physical, size, last_attr);
> + print_address_zone(first_logical, first_physical, size, last_attr,
> + start, end);
> }
> }
>
> @@ -610,7 +631,7 @@ static void dump_ttr(uint32_t ttr)
> M68K_DESC_USERATTR_SHIFT);
> }
>
> -void dump_mmu(CPUM68KState *env)
> +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end)
> {
> if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
> qemu_printf("Translation disabled\n");
> @@ -675,10 +696,10 @@ void dump_mmu(CPUM68KState *env)
> dump_ttr(env->mmu.ttr[M68K_DTTR1]);
>
> qemu_printf("SRP: 0x%08x\n", env->mmu.srp);
> - dump_address_map(env, env->mmu.srp);
> + dump_address_map(env, env->mmu.srp, start, end);
>
> qemu_printf("URP: 0x%08x\n", env->mmu.urp);
> - dump_address_map(env, env->mmu.urp);
> + dump_address_map(env, env->mmu.urp, start, end);
> }
>
> static int check_TTR(uint32_t ttr, int *prot, target_ulong addr,
> diff --git a/target/m68k/monitor.c b/target/m68k/monitor.c
> index 3e0df40a6b..bcb528d8e3 100644
> --- a/target/m68k/monitor.c
> +++ b/target/m68k/monitor.c
> @@ -9,15 +9,28 @@
> #include "cpu.h"
> #include "monitor/hmp.h"
> #include "monitor/monitor.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> CPUArchState *env1 = mon_get_cpu_env(mon);
> + hwaddr start = 0, end = HWADDR_MAX;
>
> if (!env1) {
> monitor_printf(mon, "No CPU available\n");
> return;
> }
>
> - dump_mmu(env1);
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
> +
> + dump_mmu(env1, start, end);
> }
> diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c
> index 7c88e0e2bd..753ed6b93d 100644
> --- a/target/ppc/monitor.c
> +++ b/target/ppc/monitor.c
> @@ -10,6 +10,7 @@
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> #include "cpu.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> @@ -19,5 +20,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> }
> diff --git a/target/sh4/monitor.c b/target/sh4/monitor.c
> index 50324d3600..c804824bab 100644
> --- a/target/sh4/monitor.c
> +++ b/target/sh4/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
> static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
> {
> @@ -48,6 +49,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> return;
> }
>
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> monitor_printf (mon, "ITLB:\n");
> for (i = 0 ; i < ITLB_SIZE ; i++)
> print_tlb (mon, i, &env->itlb[i]);
> diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
> index 5f583ed9de..11f94b6bd8 100644
> --- a/target/sparc/cpu.h
> +++ b/target/sparc/cpu.h
> @@ -601,6 +601,9 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> bool probe, uintptr_t retaddr);
> target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
> void dump_mmu(CPUSPARCState *env);
> +#ifndef TARGET_SPARC64
> +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end);
> +#endif
>
> #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
> int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
> diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
> index 07ba25dfce..65a30b438b 100644
> --- a/target/sparc/mmu_helper.c
> +++ b/target/sparc/mmu_helper.c
> @@ -354,7 +354,32 @@ target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev)
> return 0;
> }
>
> +static void print_tlb_entry(target_ulong va, hwaddr pa, uint32_t pde,
> + const char *indent, const char *kind,
> + hwaddr size, hwaddr start, hwaddr end)
> +{
> + hwaddr va_hw_start = (hwaddr)va;
> + hwaddr va_hw_end = (hwaddr)(va + size - 1);
> +
> + /*
> + * Print current region [va_hw_start, va_hw_end] only if it overlaps the
> + * requested virtual address range [start, end].
> + */
> + if (va_hw_start > end || va_hw_end < start) {
> + return;
> + }
> +
> + qemu_printf("%sVA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> + " %s: " TARGET_FMT_lx "\n",
> + indent, va, pa, kind, pde);
> +}
> +
> void dump_mmu(CPUSPARCState *env)
> +{
> + dump_mmu_range(env, 0, HWADDR_MAX);
> +}
> +
> +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end)
> {
> CPUState *cs = env_cpu(env);
> target_ulong va, va1, va2;
> @@ -373,8 +398,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf("VA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> - " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
> + print_tlb_entry(va, pa, pde, "", "PDE", 16 * 1024 * 1024,
> + start, end);
> for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
> pde = mmu_probe(env, va1, 1);
> if (pde) {
> @@ -383,9 +408,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> - HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n",
> - va1, pa, pde);
> + print_tlb_entry(va1, pa, pde, " ", "PDE", 256 * 1024,
> + start, end);
> for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
> pde = mmu_probe(env, va2, 0);
> if (pde) {
> @@ -394,10 +418,8 @@ void dump_mmu(CPUSPARCState *env)
> } else {
> pa = tres.physaddr;
> }
> - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> - HWADDR_FMT_plx " PTE: "
> - TARGET_FMT_lx "\n",
> - va2, pa, pde);
> + print_tlb_entry(va2, pa, pde, " ", "PTE",
> + 4 * 1024, start, end);
> }
> }
> }
> diff --git a/target/sparc/monitor.c b/target/sparc/monitor.c
> index 36f3d8d58e..993e411a37 100644
> --- a/target/sparc/monitor.c
> +++ b/target/sparc/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> @@ -35,5 +36,27 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> +#ifdef TARGET_SPARC64
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> +#else
> + hwaddr start = 0, end = HWADDR_MAX;
> +
> + if (qdict_haskey(qdict, "start")) {
> + start = (hwaddr)qdict_get_int(qdict, "start");
> + }
> + if (qdict_haskey(qdict, "end")) {
> + end = (hwaddr)qdict_get_int(qdict, "end");
> + }
> + if (start > end) {
> + monitor_printf(mon, "Invalid address range: start > end.\n");
> + return;
> + }
> +
> + dump_mmu_range(env1, start, end);
> +#endif
> }
> diff --git a/target/xtensa/monitor.c b/target/xtensa/monitor.c
> index 2af84934f8..df572cba42 100644
> --- a/target/xtensa/monitor.c
> +++ b/target/xtensa/monitor.c
> @@ -25,6 +25,7 @@
> #include "cpu.h"
> #include "monitor/monitor.h"
> #include "monitor/hmp.h"
> +#include "qobject/qdict.h"
>
> void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> {
> @@ -34,5 +35,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> monitor_printf(mon, "No CPU available\n");
> return;
> }
> +
> + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> + monitor_printf(mon, "The range arguments will be ignored.\n");
> + }
> +
> dump_mmu(env1);
> }
> --
> 2.43.0
>
>
--
Marc-André Lureau
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v3] monitor: Refine 'info tlb' command
2026-08-18 15:12 ` Marc-André Lureau
@ 2026-08-18 23:08 ` Dr. David Alan Gilbert
0 siblings, 0 replies; 4+ messages in thread
From: Dr. David Alan Gilbert @ 2026-08-18 23:08 UTC (permalink / raw)
To: Marc-André Lureau
Cc: AlanoSong, qemu-devel, laurent, zhao1.liu, atar4qemu
* Marc-André Lureau (marcandre.lureau@gmail.com) wrote:
> Hi
>
> On Tue, Aug 18, 2026 at 5:30 PM <AlanoSong@163.com> wrote:
> >
> > When booting an i386 target, the 'info tlb' command
> > may walk the entire page table hierarchy and emit
> > an enormous amount of output. It will take dozens of
> > minutes to print all the info, and
> > The same situation also occurred on sparc32 and
> > m68k targets.
> >
> > So this change do the following:
> > 1) Add a address range argument to help user control
> > the number of output items.
> > 2) Add warning note in help message that such address
> > range argument only supported on target i386,
> > sparc32 and m68k.
> > 3) Print ignore warning when user add such address
> > range argument on other targets (sh4, sparc64,
> > ppc and xtensa). Those targets only print limited
> > tlb info.
> >
> > Signed-off-by: Alano Song <AlanoSong@163.com>
>
> lgtm overall
>
> - m68k print_address_zone() used to be called with page-aligned zones,
> but now it is clipped to arbitrary user ranges, which may not be
> aligned. 1 byte range will be reported as 0kb. We may want to handle
> the case for small ranges. Btw that code could use the macros KiB for
> readability
>
> - A later patch could prune non-overlapping ranges during traversal,
> not just during printing
Yeh I'd say it's OK, two thoughts:
a) I'd have split the patch into a series with more managable chunks
b) There's quite a bit of repetition which feels like a common helper
might have removed.
But:
Reviewed-by: Dr. David Alan Gilbert <dave@treblig.org>
Dave
> > ---
> > hmp-commands-info.hx | 17 ++++++--
> > target/i386/monitor.c | 92 ++++++++++++++++++++++++++++-----------
> > target/m68k/cpu.h | 2 +-
> > target/m68k/helper.c | 55 +++++++++++++++--------
> > target/m68k/monitor.c | 15 ++++++-
> > target/ppc/monitor.c | 6 +++
> > target/sh4/monitor.c | 5 +++
> > target/sparc/cpu.h | 3 ++
> > target/sparc/mmu_helper.c | 40 +++++++++++++----
> > target/sparc/monitor.c | 23 ++++++++++
> > target/xtensa/monitor.c | 6 +++
> > 11 files changed, 206 insertions(+), 58 deletions(-)
> >
> > diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx
> > index 82134eb6c2..f907edf273 100644
> > --- a/hmp-commands-info.hx
> > +++ b/hmp-commands-info.hx
> > @@ -188,17 +188,26 @@ ERST
> >
> > {
> > .name = "tlb",
> > - .args_type = "",
> > - .params = "",
> > - .help = "show virtual to physical memory mappings",
> > + .args_type = "start:l?,end:l?",
> > + .params = "[start [end]]",
> > + .help = "show virtual to physical memory mappings. "
> > + "output can be extremely large for i386, sparc32 "
> > + "and m68k targets. use 'info tlb [start [end]]' "
> > + "to show a range of entries. Note that the range "
> > + "argument is only supported on i386, sparc32 and "
> > + "m68k targets.",
> > .cmd = hmp_info_tlb,
> > .arch_bitmask = QEMU_ARCH_I386 | QEMU_ARCH_SH4 | QEMU_ARCH_SPARC \
> > | QEMU_ARCH_PPC | QEMU_ARCH_XTENSA | QEMU_ARCH_M68K,
> > },
> >
> > SRST
> > - ``info tlb``
> > + ``info tlb`` [*start* [*end*]]
> > Show virtual to physical memory mappings.
> > + The output can be extremely large for i386, sparc32 and m68k targets.
> > + Use *start* and *end* to print entries located in virtual address
> > + range [start, end] (end is optional). Note that the range argument is
> > + only supported on i386, sparc32 and m68k targets.
> > ERST
> >
> > {
> > diff --git a/target/i386/monitor.c b/target/i386/monitor.c
> > index a536712c75..8db9ab0576 100644
> > --- a/target/i386/monitor.c
> > +++ b/target/i386/monitor.c
> > @@ -48,11 +48,9 @@ static hwaddr addr_canonical(CPUArchState *env, hwaddr addr)
> > return addr;
> > }
> >
> > -static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> > - hwaddr pte, hwaddr mask)
> > +static void do_print_pte(Monitor *mon, hwaddr addr,
> > + hwaddr pte, hwaddr mask)
> > {
> > - 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,
> > @@ -68,7 +66,25 @@ static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> > pte & PG_RW_MASK ? 'W' : '-');
> > }
> >
> > -static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > +static void print_pte(Monitor *mon, CPUArchState *env, hwaddr addr,
> > + hwaddr pte, hwaddr mask, hwaddr size,
> > + hwaddr start, hwaddr end)
> > +{
> > + hwaddr addr_start = addr_canonical(env, addr);
> > + hwaddr addr_end = addr_canonical(env, addr + size - 1);
> > + /*
> > + * Print current page [addr_start, addr_end] only if it overlaps the
> > + * requested virtual address range [start, end].
> > + */
> > + if (addr_start > end || addr_end < start) {
> > + return;
> > + }
> > +
> > + do_print_pte(mon, addr_start, pte, mask);
> > +}
> > +
> > +static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > + hwaddr start, hwaddr end)
> > {
> > const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> > unsigned int l1, l2;
> > @@ -80,15 +96,16 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > 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));
> > + print_pte(mon, env, (l1 << 22), pde, ~((1 << 21) - 1),
> > + 0x400000, start, end);
> > } else {
> > for(l2 = 0; l2 < 1024; l2++) {
> > pte = address_space_ldl_le(as, (pde & ~0xfff) + l2 * 4,
> > attrs, NULL);
> > if (pte & PG_PRESENT_MASK) {
> > print_pte(mon, env, (l1 << 22) + (l2 << 12),
> > - pte & ~PG_PSE_MASK,
> > - ~0xfff);
> > + pte & ~PG_PSE_MASK, ~0xfff, 0x1000,
> > + start, end);
> > }
> > }
> > }
> > @@ -96,7 +113,8 @@ static void tlb_info_32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > }
> > }
> >
> > -static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > +static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > + hwaddr start, hwaddr end)
> > {
> > const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> > unsigned int l1, l2, l3;
> > @@ -114,17 +132,19 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > 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));
> > + ~((hwaddr)(1 << 20) - 1), 0x200000,
> > + start, end);
> > } else {
> > pt_addr = pde & 0x3fffffffff000ULL;
> > for (l3 = 0; l3 < 512; l3++) {
> > pte = address_space_ldq_le(as, pt_addr + l3 * 8,
> > attrs, NULL);
> > if (pte & PG_PRESENT_MASK) {
> > - print_pte(mon, env, (l1 << 30) + (l2 << 21)
> > - + (l3 << 12),
> > + print_pte(mon, env,
> > + (l1 << 30) + (l2 << 21) + (l3 << 12),
> > pte & ~PG_PSE_MASK,
> > - ~(hwaddr)0xfff);
> > + ~(hwaddr)0xfff, 0x1000,
> > + start, end);
> > }
> > }
> > }
> > @@ -136,7 +156,8 @@ static void tlb_info_pae32(Monitor *mon, CPUArchState *env, AddressSpace *as)
> >
> > #ifdef TARGET_X86_64
> > static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > - uint64_t l0, uint64_t pml4_addr)
> > + uint64_t l0, uint64_t pml4_addr, hwaddr start,
> > + hwaddr end)
> > {
> > const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> > uint64_t l1, l2, l3, l4;
> > @@ -159,7 +180,7 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > if (pdpe & PG_PSE_MASK) {
> > /* 1G pages, CR4.PSE is ignored */
> > print_pte(mon, env, (l0 << 48) + (l1 << 39) + (l2 << 30),
> > - pdpe, 0x3ffffc0000000ULL);
> > + pdpe, 0x3ffffc0000000ULL, 0x40000000, start, end);
> > continue;
> > }
> >
> > @@ -172,8 +193,9 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> >
> > 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);
> > + print_pte(mon, env,
> > + (l0 << 48) + (l1 << 39) + (l2 << 30) + (l3 << 21),
> > + pde, 0x3ffffffe00000ULL, 0x200000, start, end);
> > continue;
> > }
> >
> > @@ -182,9 +204,11 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > pte = address_space_ldq_le(as, pt_addr + l4 * 8,
> > attrs, NULL);
> > if (pte & PG_PRESENT_MASK) {
> > - print_pte(mon, env, (l0 << 48) + (l1 << 39) +
> > - (l2 << 30) + (l3 << 21) + (l4 << 12),
> > - pte & ~PG_PSE_MASK, 0x3fffffffff000ULL);
> > + print_pte(mon, env,
> > + (l0 << 48) + (l1 << 39) + (l2 << 30) +
> > + (l3 << 21) + (l4 << 12),
> > + pte & ~PG_PSE_MASK,
> > + 0x3fffffffff000ULL, 0x1000, start, end);
> > }
> > }
> > }
> > @@ -192,7 +216,8 @@ static void tlb_info_la48(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > }
> > }
> >
> > -static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > +static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as,
> > + hwaddr start, hwaddr end)
> > {
> > const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
> > uint64_t l0;
> > @@ -203,7 +228,8 @@ static void tlb_info_la57(Monitor *mon, CPUArchState *env, AddressSpace *as)
> > for (l0 = 0; l0 < 512; l0++) {
> > pml5e = address_space_ldq_le(as, pml5_addr + l0 * 8, attrs, NULL);
> > if (pml5e & PG_PRESENT_MASK) {
> > - tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL);
> > + tlb_info_la48(mon, env, as, l0, pml5e & 0x3fffffffff000ULL,
> > + start, end);
> > }
> > }
> > }
> > @@ -213,6 +239,18 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > {
> > CPUArchState *env;
> > AddressSpace *as;
> > + hwaddr start = 0, end = HWADDR_MAX;
> > +
> > + if (qdict_haskey(qdict, "start")) {
> > + start = (hwaddr)qdict_get_int(qdict, "start");
> > + }
> > + if (qdict_haskey(qdict, "end")) {
> > + end = (hwaddr)qdict_get_int(qdict, "end");
> > + }
> > + if (start > end) {
> > + monitor_printf(mon, "Invalid address range: start > end.\n");
> > + return;
> > + }
> >
> > env = mon_get_cpu_env(mon);
> > if (!env) {
> > @@ -229,17 +267,19 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > #ifdef TARGET_X86_64
> > if (env->hflags & HF_LMA_MASK) {
> > if (env->cr[4] & CR4_LA57_MASK) {
> > - tlb_info_la57(mon, env, as);
> > + tlb_info_la57(mon, env, as, start, end);
> > } else {
> > - tlb_info_la48(mon, env, as, 0, env->cr[3] & 0x3fffffffff000ULL);
> > + tlb_info_la48(mon, env, as, 0,
> > + env->cr[3] & 0x3fffffffff000ULL,
> > + start, end);
> > }
> > } else
> > #endif
> > {
> > - tlb_info_pae32(mon, env, as);
> > + tlb_info_pae32(mon, env, as, start, end);
> > }
> > } else {
> > - tlb_info_32(mon, env, as);
> > + tlb_info_32(mon, env, as, start, end);
> > }
> > }
> >
> > diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h
> > index 7cf3791108..b129cb150f 100644
> > --- a/target/m68k/cpu.h
> > +++ b/target/m68k/cpu.h
> > @@ -606,6 +606,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr,
> > #define TB_FLAGS_TRACE 16
> > #define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE)
> >
> > -void dump_mmu(CPUM68KState *env);
> > +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end);
> >
> > #endif
> > diff --git a/target/m68k/helper.c b/target/m68k/helper.c
> > index 5f91d206f5..1b854c912b 100644
> > --- a/target/m68k/helper.c
> > +++ b/target/m68k/helper.c
> > @@ -461,27 +461,46 @@ void m68k_switch_sp(CPUM68KState *env)
> > /* MMU: 68040 only */
> >
> > static void print_address_zone(uint32_t logical, uint32_t physical,
> > - uint32_t size, int attr)
> > + uint32_t size, int attr,
> > + hwaddr start, hwaddr end)
> > {
> > + uint64_t zone_start = logical;
> > + uint64_t zone_end = zone_start + size - 1;
> > + uint64_t zone_len;
> > +
> > + /*
> > + * Print current zone [zone_start, zone_end] only if it overlaps the
> > + * requested virtual address range [start, end].
> > + */
> > + if (zone_end < start || zone_start > end) {
> > + return;
> > + }
> > +
> > + physical += (uint32_t)(start > zone_start ? start - zone_start : 0);
> > + zone_start = zone_start > start ? zone_start : start;
> > + zone_end = zone_end < end ? zone_end : end;
> > + zone_len = zone_end - zone_start + 1;
> > +
> > qemu_printf("%08x - %08x -> %08x - %08x %c ",
> > - logical, logical + size - 1,
> > - physical, physical + size - 1,
> > + (uint32_t)zone_start, (uint32_t)zone_end,
> > + physical, physical + (uint32_t)(zone_len - 1),
> > attr & 4 ? 'W' : '-');
> > - size >>= 10;
> > - if (size < 1024) {
> > - qemu_printf("(%d KiB)\n", size);
> > + zone_len >>= 10;
> > + if (zone_len < 1024) {
> > + qemu_printf("(%d KiB)\n", (int)zone_len);
> > } else {
> > - size >>= 10;
> > - if (size < 1024) {
> > - qemu_printf("(%d MiB)\n", size);
> > + zone_len >>= 10;
> > + if (zone_len < 1024) {
> > + qemu_printf("(%d MiB)\n", (int)zone_len);
> > } else {
> > - size >>= 10;
> > - qemu_printf("(%d GiB)\n", size);
> > + zone_len >>= 10;
> > + qemu_printf("(%d GiB)\n", (int)zone_len);
> > }
> > }
> > }
> >
> > -static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> > +static void dump_address_map(CPUM68KState *env, uint32_t root_pointer,
> > + hwaddr start, hwaddr end)
> > {
> > int tic_size, tic_shift;
> > uint32_t tib_mask;
> > @@ -550,7 +569,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> > size = last_logical + (1 << tic_shift) -
> > first_logical;
> > print_address_zone(first_logical,
> > - first_physical, size, last_attr);
> > + first_physical, size, last_attr,
> > + start, end);
> > }
> > first_logical = logical;
> > first_physical = physical;
> > @@ -560,7 +580,8 @@ static void dump_address_map(CPUM68KState *env, uint32_t root_pointer)
> > }
> > if (first_logical != logical || (attr & 4) != (last_attr & 4)) {
> > size = logical + (1 << tic_shift) - first_logical;
> > - print_address_zone(first_logical, first_physical, size, last_attr);
> > + print_address_zone(first_logical, first_physical, size, last_attr,
> > + start, end);
> > }
> > }
> >
> > @@ -610,7 +631,7 @@ static void dump_ttr(uint32_t ttr)
> > M68K_DESC_USERATTR_SHIFT);
> > }
> >
> > -void dump_mmu(CPUM68KState *env)
> > +void dump_mmu(CPUM68KState *env, hwaddr start, hwaddr end)
> > {
> > if ((env->mmu.tcr & M68K_TCR_ENABLED) == 0) {
> > qemu_printf("Translation disabled\n");
> > @@ -675,10 +696,10 @@ void dump_mmu(CPUM68KState *env)
> > dump_ttr(env->mmu.ttr[M68K_DTTR1]);
> >
> > qemu_printf("SRP: 0x%08x\n", env->mmu.srp);
> > - dump_address_map(env, env->mmu.srp);
> > + dump_address_map(env, env->mmu.srp, start, end);
> >
> > qemu_printf("URP: 0x%08x\n", env->mmu.urp);
> > - dump_address_map(env, env->mmu.urp);
> > + dump_address_map(env, env->mmu.urp, start, end);
> > }
> >
> > static int check_TTR(uint32_t ttr, int *prot, target_ulong addr,
> > diff --git a/target/m68k/monitor.c b/target/m68k/monitor.c
> > index 3e0df40a6b..bcb528d8e3 100644
> > --- a/target/m68k/monitor.c
> > +++ b/target/m68k/monitor.c
> > @@ -9,15 +9,28 @@
> > #include "cpu.h"
> > #include "monitor/hmp.h"
> > #include "monitor/monitor.h"
> > +#include "qobject/qdict.h"
> >
> > void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > {
> > CPUArchState *env1 = mon_get_cpu_env(mon);
> > + hwaddr start = 0, end = HWADDR_MAX;
> >
> > if (!env1) {
> > monitor_printf(mon, "No CPU available\n");
> > return;
> > }
> >
> > - dump_mmu(env1);
> > + if (qdict_haskey(qdict, "start")) {
> > + start = (hwaddr)qdict_get_int(qdict, "start");
> > + }
> > + if (qdict_haskey(qdict, "end")) {
> > + end = (hwaddr)qdict_get_int(qdict, "end");
> > + }
> > + if (start > end) {
> > + monitor_printf(mon, "Invalid address range: start > end.\n");
> > + return;
> > + }
> > +
> > + dump_mmu(env1, start, end);
> > }
> > diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c
> > index 7c88e0e2bd..753ed6b93d 100644
> > --- a/target/ppc/monitor.c
> > +++ b/target/ppc/monitor.c
> > @@ -10,6 +10,7 @@
> > #include "monitor/monitor.h"
> > #include "monitor/hmp.h"
> > #include "cpu.h"
> > +#include "qobject/qdict.h"
> >
> > void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > {
> > @@ -19,5 +20,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > monitor_printf(mon, "No CPU available\n");
> > return;
> > }
> > +
> > + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> > + monitor_printf(mon, "The range arguments will be ignored.\n");
> > + }
> > +
> > dump_mmu(env1);
> > }
> > diff --git a/target/sh4/monitor.c b/target/sh4/monitor.c
> > index 50324d3600..c804824bab 100644
> > --- a/target/sh4/monitor.c
> > +++ b/target/sh4/monitor.c
> > @@ -25,6 +25,7 @@
> > #include "cpu.h"
> > #include "monitor/monitor.h"
> > #include "monitor/hmp.h"
> > +#include "qobject/qdict.h"
> >
> > static void print_tlb(Monitor *mon, int idx, tlb_t *tlb)
> > {
> > @@ -48,6 +49,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > return;
> > }
> >
> > + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> > + monitor_printf(mon, "The range arguments will be ignored.\n");
> > + }
> > +
> > monitor_printf (mon, "ITLB:\n");
> > for (i = 0 ; i < ITLB_SIZE ; i++)
> > print_tlb (mon, i, &env->itlb[i]);
> > diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
> > index 5f583ed9de..11f94b6bd8 100644
> > --- a/target/sparc/cpu.h
> > +++ b/target/sparc/cpu.h
> > @@ -601,6 +601,9 @@ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
> > bool probe, uintptr_t retaddr);
> > target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
> > void dump_mmu(CPUSPARCState *env);
> > +#ifndef TARGET_SPARC64
> > +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end);
> > +#endif
> >
> > #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
> > int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
> > diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
> > index 07ba25dfce..65a30b438b 100644
> > --- a/target/sparc/mmu_helper.c
> > +++ b/target/sparc/mmu_helper.c
> > @@ -354,7 +354,32 @@ target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev)
> > return 0;
> > }
> >
> > +static void print_tlb_entry(target_ulong va, hwaddr pa, uint32_t pde,
> > + const char *indent, const char *kind,
> > + hwaddr size, hwaddr start, hwaddr end)
> > +{
> > + hwaddr va_hw_start = (hwaddr)va;
> > + hwaddr va_hw_end = (hwaddr)(va + size - 1);
> > +
> > + /*
> > + * Print current region [va_hw_start, va_hw_end] only if it overlaps the
> > + * requested virtual address range [start, end].
> > + */
> > + if (va_hw_start > end || va_hw_end < start) {
> > + return;
> > + }
> > +
> > + qemu_printf("%sVA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> > + " %s: " TARGET_FMT_lx "\n",
> > + indent, va, pa, kind, pde);
> > +}
> > +
> > void dump_mmu(CPUSPARCState *env)
> > +{
> > + dump_mmu_range(env, 0, HWADDR_MAX);
> > +}
> > +
> > +void dump_mmu_range(CPUSPARCState *env, hwaddr start, hwaddr end)
> > {
> > CPUState *cs = env_cpu(env);
> > target_ulong va, va1, va2;
> > @@ -373,8 +398,8 @@ void dump_mmu(CPUSPARCState *env)
> > } else {
> > pa = tres.physaddr;
> > }
> > - qemu_printf("VA: " TARGET_FMT_lx ", PA: " HWADDR_FMT_plx
> > - " PDE: " TARGET_FMT_lx "\n", va, pa, pde);
> > + print_tlb_entry(va, pa, pde, "", "PDE", 16 * 1024 * 1024,
> > + start, end);
> > for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) {
> > pde = mmu_probe(env, va1, 1);
> > if (pde) {
> > @@ -383,9 +408,8 @@ void dump_mmu(CPUSPARCState *env)
> > } else {
> > pa = tres.physaddr;
> > }
> > - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> > - HWADDR_FMT_plx " PDE: " TARGET_FMT_lx "\n",
> > - va1, pa, pde);
> > + print_tlb_entry(va1, pa, pde, " ", "PDE", 256 * 1024,
> > + start, end);
> > for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) {
> > pde = mmu_probe(env, va2, 0);
> > if (pde) {
> > @@ -394,10 +418,8 @@ void dump_mmu(CPUSPARCState *env)
> > } else {
> > pa = tres.physaddr;
> > }
> > - qemu_printf(" VA: " TARGET_FMT_lx ", PA: "
> > - HWADDR_FMT_plx " PTE: "
> > - TARGET_FMT_lx "\n",
> > - va2, pa, pde);
> > + print_tlb_entry(va2, pa, pde, " ", "PTE",
> > + 4 * 1024, start, end);
> > }
> > }
> > }
> > diff --git a/target/sparc/monitor.c b/target/sparc/monitor.c
> > index 36f3d8d58e..993e411a37 100644
> > --- a/target/sparc/monitor.c
> > +++ b/target/sparc/monitor.c
> > @@ -25,6 +25,7 @@
> > #include "cpu.h"
> > #include "monitor/monitor.h"
> > #include "monitor/hmp.h"
> > +#include "qobject/qdict.h"
> >
> >
> > void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > @@ -35,5 +36,27 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > monitor_printf(mon, "No CPU available\n");
> > return;
> > }
> > +
> > +#ifdef TARGET_SPARC64
> > + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> > + monitor_printf(mon, "The range arguments will be ignored.\n");
> > + }
> > +
> > dump_mmu(env1);
> > +#else
> > + hwaddr start = 0, end = HWADDR_MAX;
> > +
> > + if (qdict_haskey(qdict, "start")) {
> > + start = (hwaddr)qdict_get_int(qdict, "start");
> > + }
> > + if (qdict_haskey(qdict, "end")) {
> > + end = (hwaddr)qdict_get_int(qdict, "end");
> > + }
> > + if (start > end) {
> > + monitor_printf(mon, "Invalid address range: start > end.\n");
> > + return;
> > + }
> > +
> > + dump_mmu_range(env1, start, end);
> > +#endif
> > }
> > diff --git a/target/xtensa/monitor.c b/target/xtensa/monitor.c
> > index 2af84934f8..df572cba42 100644
> > --- a/target/xtensa/monitor.c
> > +++ b/target/xtensa/monitor.c
> > @@ -25,6 +25,7 @@
> > #include "cpu.h"
> > #include "monitor/monitor.h"
> > #include "monitor/hmp.h"
> > +#include "qobject/qdict.h"
> >
> > void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > {
> > @@ -34,5 +35,10 @@ void hmp_info_tlb(Monitor *mon, const QDict *qdict)
> > monitor_printf(mon, "No CPU available\n");
> > return;
> > }
> > +
> > + if (qdict_haskey(qdict, "start") || qdict_haskey(qdict, "end")) {
> > + monitor_printf(mon, "The range arguments will be ignored.\n");
> > + }
> > +
> > dump_mmu(env1);
> > }
> > --
> > 2.43.0
> >
> >
>
>
> --
> Marc-André Lureau
--
-----Open up your eyes, open up your mind, open up your code -------
/ Dr. David Alan Gilbert | Running GNU/Linux | Happy \
\ dave @ treblig.org | | In Hex /
\ _________________________|_____ http://www.treblig.org |_______/
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-08-18 23:09 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 13:25 [PATCH v3] monitor: Refine 'info tlb' command AlanoSong
2026-08-18 13:36 ` Philippe Mathieu-Daudé
2026-08-18 15:12 ` Marc-André Lureau
2026-08-18 23:08 ` Dr. David Alan Gilbert
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.