/* * QEMU monitor for RISC-V * * Copyright (c) 2019 Bin Meng * * RISC-V specific monitor commands implementation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2 or later, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include "qemu/osdep.h" #include "cpu.h" #include "cpu_bits.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" #ifdef TARGET_RISCV64 #define PTE_HEADER_FIELDS "vaddr paddr "\ "size attr\n" #define PTE_HEADER_DELIMITER "---------------- ---------------- "\ "---------------- -------\n" #else #define PTE_HEADER_FIELDS "vaddr paddr size attr\n" #define PTE_HEADER_DELIMITER "-------- ---------------- -------- -------\n" #endif /* Perform linear address sign extension */ static target_ulong addr_canonical(int va_bits, target_ulong addr) { #ifdef TARGET_RISCV64 if (addr & (1UL << (va_bits - 1))) { addr |= (hwaddr)-(1L << va_bits); } #endif return addr; } static void print_pte_header(Monitor *mon) { monitor_printf(mon, PTE_HEADER_FIELDS); monitor_printf(mon, PTE_HEADER_DELIMITER); } static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr, hwaddr paddr, target_ulong size, int attr) { /* santity check on vaddr */ if (vaddr >= (1UL << va_bits)) { return; } if (!size) { return; } monitor_printf(mon, TARGET_FMT_lx " " TARGET_FMT_plx " " TARGET_FMT_lx " %c%c%c%c%c%c%c\n", addr_canonical(va_bits, vaddr), paddr, size, attr & PTE_R ? 'r' : '-', attr & PTE_W ? 'w' : '-', attr & PTE_X ? 'x' : '-', attr & PTE_U ? 'u' : '-', attr & PTE_G ? 'g' : '-', attr & PTE_A ? 'a' : '-', attr & PTE_D ? 'd' : '-'); } static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, int level, int ptidxbits, int ptesize, int va_bits, target_ulong *vbase, hwaddr *pbase, hwaddr *last_paddr, target_ulong *last_size, int *last_attr) { hwaddr pte_addr; hwaddr paddr; target_ulong pgsize; target_ulong pte; int ptshift; int attr; int idx; if (level < 0) { return; } ptshift = level * ptidxbits; pgsize = 1UL << (PGSHIFT + ptshift); for (idx = 0; idx < (1UL << ptidxbits); idx++) { pte_addr = base + idx * ptesize; cpu_physical_memory_read(pte_addr, &pte, ptesize); paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT; attr = pte & 0xff; /* PTE has to be valid */ if (attr & PTE_V) { if (attr & (PTE_R | PTE_W | PTE_X)) { /* * A leaf PTE has been found * * If current PTE's permission bits differ from the last one, * or current PTE's ppn does not make a contiguous physical * address block together with the last one, print out the last * contiguous mapped block details. */ if ((*last_attr != attr) || (*last_paddr + *last_size != paddr)) { print_pte(mon, va_bits, *vbase, *pbase, *last_paddr + *last_size - *pbase, *last_attr); *vbase = start; *pbase = paddr; *last_attr = attr; } *last_paddr = paddr; *last_size = pgsize; } else { /* pointer to the next level of the page table */ walk_pte(mon, paddr, start, level - 1, ptidxbits, ptesize, va_bits, vbase, pbase, last_paddr, last_size, last_attr); } } start += pgsize; } } static void mem_info_svxx(Monitor *mon, CPUArchState *env) { int levels, ptidxbits, ptesize, vm, va_bits; hwaddr base; target_ulong vbase; hwaddr pbase; hwaddr last_paddr; target_ulong last_size; int last_attr; base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; vm = get_field(env->satp, SATP_MODE); switch (vm) { case VM_1_10_SV32: levels = 2; ptidxbits = 10; ptesize = 4; break; case VM_1_10_SV39: levels = 3; ptidxbits = 9; ptesize = 8; break; case VM_1_10_SV48: levels = 4; ptidxbits = 9; ptesize = 8; break; case VM_1_10_SV57: levels = 5; ptidxbits = 9; ptesize = 8; break; default: g_assert_not_reached(); break; } /* calculate virtual address bits */ va_bits = PGSHIFT + levels * ptidxbits; /* print header */ print_pte_header(mon); vbase = -1; pbase = -1; last_paddr = -1; last_size = 0; last_attr = 0; /* walk page tables, starting from address 0 */ walk_pte(mon, base, 0, levels - 1, ptidxbits, ptesize, va_bits, &vbase, &pbase, &last_paddr, &last_size, &last_attr); /* don't forget the last one */ print_pte(mon, va_bits, vbase, pbase, last_paddr + last_size - pbase, last_attr); } void hmp_info_mem(Monitor *mon, const QDict *qdict) { CPUArchState *env; env = mon_get_cpu_env(); if (!env) { monitor_printf(mon, "No CPU available\n"); return; } if (!riscv_feature(env, RISCV_FEATURE_MMU)) { monitor_printf(mon, "S-mode MMU unavailable\n"); return; } if (env->priv_ver < PRIV_VERSION_1_10_0) { monitor_printf(mon, "Privileged mode < 1.10 unsupported\n"); return; } if (!(env->satp & SATP_MODE)) { monitor_printf(mon, "No translation or protection\n"); return; } mem_info_svxx(mon, env); }