2019-08-14 18:33:32 +03:00
|
|
|
/*
|
|
|
|
* QEMU monitor for RISC-V
|
|
|
|
*
|
|
|
|
* Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com>
|
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2023-01-11 00:29:47 +03:00
|
|
|
monitor_printf(mon, TARGET_FMT_lx " " HWADDR_FMT_plx " " TARGET_FMT_lx
|
2019-08-14 18:33:32 +03:00
|
|
|
" %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;
|
2022-04-24 00:59:07 +03:00
|
|
|
target_ulong last_start = -1;
|
2019-08-14 18:33:32 +03:00
|
|
|
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,
|
2022-04-24 00:59:07 +03:00
|
|
|
* or the current PTE breaks up a contiguous virtual or
|
|
|
|
* physical mapping, address block together with the last one,
|
|
|
|
* print out the last contiguous mapped block details.
|
2019-08-14 18:33:32 +03:00
|
|
|
*/
|
|
|
|
if ((*last_attr != attr) ||
|
2022-04-24 00:59:07 +03:00
|
|
|
(*last_paddr + *last_size != paddr) ||
|
|
|
|
(last_start + *last_size != start)) {
|
2019-08-14 18:33:32 +03:00
|
|
|
print_pte(mon, va_bits, *vbase, *pbase,
|
|
|
|
*last_paddr + *last_size - *pbase, *last_attr);
|
|
|
|
|
|
|
|
*vbase = start;
|
|
|
|
*pbase = paddr;
|
|
|
|
*last_attr = attr;
|
|
|
|
}
|
|
|
|
|
2022-04-24 00:59:07 +03:00
|
|
|
last_start = start;
|
2019-08-14 18:33:32 +03:00
|
|
|
*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;
|
|
|
|
|
2021-10-20 06:16:58 +03:00
|
|
|
if (riscv_cpu_mxl(env) == MXL_RV32) {
|
2021-04-24 06:33:31 +03:00
|
|
|
base = (hwaddr)get_field(env->satp, SATP32_PPN) << PGSHIFT;
|
|
|
|
vm = get_field(env->satp, SATP32_MODE);
|
|
|
|
} else {
|
|
|
|
base = (hwaddr)get_field(env->satp, SATP64_PPN) << PGSHIFT;
|
|
|
|
vm = get_field(env->satp, SATP64_MODE);
|
|
|
|
}
|
2019-08-14 18:33:32 +03:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
hmp: Pass monitor to mon_get_cpu_env()
mon_get_cpu_env() is indirectly called monitor_parse_arguments() where
the current monitor isn't set yet. Instead of using monitor_cur_env(),
explicitly pass the Monitor pointer to the function.
Without this fix, an HMP command like "x $pc" crashes like this:
#0 0x0000555555caa01f in mon_get_cpu_sync (mon=0x0, synchronize=true) at ../monitor/misc.c:270
#1 0x0000555555caa141 in mon_get_cpu (mon=0x0) at ../monitor/misc.c:294
#2 0x0000555555caa158 in mon_get_cpu_env () at ../monitor/misc.c:299
#3 0x0000555555b19739 in monitor_get_pc (mon=0x555556ad2de0, md=0x5555565d2d40 <monitor_defs+1152>, val=0) at ../target/i386/monitor.c:607
#4 0x0000555555cadbec in get_monitor_def (mon=0x555556ad2de0, pval=0x7fffffffc208, name=0x7fffffffc220 "pc") at ../monitor/misc.c:1681
#5 0x000055555582ec4f in expr_unary (mon=0x555556ad2de0) at ../monitor/hmp.c:387
#6 0x000055555582edbb in expr_prod (mon=0x555556ad2de0) at ../monitor/hmp.c:421
#7 0x000055555582ee79 in expr_logic (mon=0x555556ad2de0) at ../monitor/hmp.c:455
#8 0x000055555582eefe in expr_sum (mon=0x555556ad2de0) at ../monitor/hmp.c:484
#9 0x000055555582efe8 in get_expr (mon=0x555556ad2de0, pval=0x7fffffffc418, pp=0x7fffffffc408) at ../monitor/hmp.c:511
#10 0x000055555582fcd4 in monitor_parse_arguments (mon=0x555556ad2de0, endp=0x7fffffffc890, cmd=0x555556675b50 <hmp_cmds+7920>) at ../monitor/hmp.c:876
#11 0x00005555558306a8 in handle_hmp_command (mon=0x555556ad2de0, cmdline=0x555556ada452 "$pc") at ../monitor/hmp.c:1087
#12 0x000055555582df14 in monitor_command_cb (opaque=0x555556ad2de0, cmdline=0x555556ada450 "x $pc", readline_opaque=0x0) at ../monitor/hmp.c:47
After this fix, nothing is left in monitor_parse_arguments() that can
indirectly call monitor_cur(), so the fix is complete.
Fixes: ff04108a0e36e822519c517bd3bddbc1c7747c18
Reported-by: lichun <lichun@ruijie.com.cn>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20201113114326.97663-4-kwolf@redhat.com>
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
2020-11-13 14:43:26 +03:00
|
|
|
env = mon_get_cpu_env(mon);
|
2019-08-14 18:33:32 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-10-20 06:16:58 +03:00
|
|
|
if (riscv_cpu_mxl(env) == MXL_RV32) {
|
2021-04-24 06:33:31 +03:00
|
|
|
if (!(env->satp & SATP32_MODE)) {
|
|
|
|
monitor_printf(mon, "No translation or protection\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(env->satp & SATP64_MODE)) {
|
|
|
|
monitor_printf(mon, "No translation or protection\n");
|
|
|
|
return;
|
|
|
|
}
|
2019-08-14 18:33:32 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mem_info_svxx(mon, env);
|
|
|
|
}
|