1 /* 2 * QEMU monitor for RISC-V 3 * 4 * Copyright (c) 2019 Bin Meng <bmeng.cn@gmail.com> 5 * 6 * RISC-V specific monitor commands implementation 7 * 8 * This program is free software; you can redistribute it and/or modify it 9 * under the terms and conditions of the GNU General Public License, 10 * version 2 or later, as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along with 18 * this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "cpu_bits.h" 24 #include "monitor/monitor.h" 25 #include "monitor/hmp-target.h" 26 27 #ifdef TARGET_RISCV64 28 #define PTE_HEADER_FIELDS "vaddr paddr "\ 29 "size attr\n" 30 #define PTE_HEADER_DELIMITER "---------------- ---------------- "\ 31 "---------------- -------\n" 32 #else 33 #define PTE_HEADER_FIELDS "vaddr paddr size attr\n" 34 #define PTE_HEADER_DELIMITER "-------- ---------------- -------- -------\n" 35 #endif 36 37 /* Perform linear address sign extension */ 38 static target_ulong addr_canonical(int va_bits, target_ulong addr) 39 { 40 #ifdef TARGET_RISCV64 41 if (addr & (1UL << (va_bits - 1))) { 42 addr |= (hwaddr)-(1L << va_bits); 43 } 44 #endif 45 46 return addr; 47 } 48 49 static void print_pte_header(Monitor *mon) 50 { 51 monitor_printf(mon, PTE_HEADER_FIELDS); 52 monitor_printf(mon, PTE_HEADER_DELIMITER); 53 } 54 55 static void print_pte(Monitor *mon, int va_bits, target_ulong vaddr, 56 hwaddr paddr, target_ulong size, int attr) 57 { 58 /* santity check on vaddr */ 59 if (vaddr >= (1UL << va_bits)) { 60 return; 61 } 62 63 if (!size) { 64 return; 65 } 66 67 monitor_printf(mon, TARGET_FMT_lx " " TARGET_FMT_plx " " TARGET_FMT_lx 68 " %c%c%c%c%c%c%c\n", 69 addr_canonical(va_bits, vaddr), 70 paddr, size, 71 attr & PTE_R ? 'r' : '-', 72 attr & PTE_W ? 'w' : '-', 73 attr & PTE_X ? 'x' : '-', 74 attr & PTE_U ? 'u' : '-', 75 attr & PTE_G ? 'g' : '-', 76 attr & PTE_A ? 'a' : '-', 77 attr & PTE_D ? 'd' : '-'); 78 } 79 80 static void walk_pte(Monitor *mon, hwaddr base, target_ulong start, 81 int level, int ptidxbits, int ptesize, int va_bits, 82 target_ulong *vbase, hwaddr *pbase, hwaddr *last_paddr, 83 target_ulong *last_size, int *last_attr) 84 { 85 hwaddr pte_addr; 86 hwaddr paddr; 87 target_ulong pgsize; 88 target_ulong pte; 89 int ptshift; 90 int attr; 91 int idx; 92 93 if (level < 0) { 94 return; 95 } 96 97 ptshift = level * ptidxbits; 98 pgsize = 1UL << (PGSHIFT + ptshift); 99 100 for (idx = 0; idx < (1UL << ptidxbits); idx++) { 101 pte_addr = base + idx * ptesize; 102 cpu_physical_memory_read(pte_addr, &pte, ptesize); 103 104 paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT; 105 attr = pte & 0xff; 106 107 /* PTE has to be valid */ 108 if (attr & PTE_V) { 109 if (attr & (PTE_R | PTE_W | PTE_X)) { 110 /* 111 * A leaf PTE has been found 112 * 113 * If current PTE's permission bits differ from the last one, 114 * or current PTE's ppn does not make a contiguous physical 115 * address block together with the last one, print out the last 116 * contiguous mapped block details. 117 */ 118 if ((*last_attr != attr) || 119 (*last_paddr + *last_size != paddr)) { 120 print_pte(mon, va_bits, *vbase, *pbase, 121 *last_paddr + *last_size - *pbase, *last_attr); 122 123 *vbase = start; 124 *pbase = paddr; 125 *last_attr = attr; 126 } 127 128 *last_paddr = paddr; 129 *last_size = pgsize; 130 } else { 131 /* pointer to the next level of the page table */ 132 walk_pte(mon, paddr, start, level - 1, ptidxbits, ptesize, 133 va_bits, vbase, pbase, last_paddr, 134 last_size, last_attr); 135 } 136 } 137 138 start += pgsize; 139 } 140 141 } 142 143 static void mem_info_svxx(Monitor *mon, CPUArchState *env) 144 { 145 int levels, ptidxbits, ptesize, vm, va_bits; 146 hwaddr base; 147 target_ulong vbase; 148 hwaddr pbase; 149 hwaddr last_paddr; 150 target_ulong last_size; 151 int last_attr; 152 153 base = (hwaddr)get_field(env->satp, SATP_PPN) << PGSHIFT; 154 155 vm = get_field(env->satp, SATP_MODE); 156 switch (vm) { 157 case VM_1_10_SV32: 158 levels = 2; 159 ptidxbits = 10; 160 ptesize = 4; 161 break; 162 case VM_1_10_SV39: 163 levels = 3; 164 ptidxbits = 9; 165 ptesize = 8; 166 break; 167 case VM_1_10_SV48: 168 levels = 4; 169 ptidxbits = 9; 170 ptesize = 8; 171 break; 172 case VM_1_10_SV57: 173 levels = 5; 174 ptidxbits = 9; 175 ptesize = 8; 176 break; 177 default: 178 g_assert_not_reached(); 179 break; 180 } 181 182 /* calculate virtual address bits */ 183 va_bits = PGSHIFT + levels * ptidxbits; 184 185 /* print header */ 186 print_pte_header(mon); 187 188 vbase = -1; 189 pbase = -1; 190 last_paddr = -1; 191 last_size = 0; 192 last_attr = 0; 193 194 /* walk page tables, starting from address 0 */ 195 walk_pte(mon, base, 0, levels - 1, ptidxbits, ptesize, va_bits, 196 &vbase, &pbase, &last_paddr, &last_size, &last_attr); 197 198 /* don't forget the last one */ 199 print_pte(mon, va_bits, vbase, pbase, 200 last_paddr + last_size - pbase, last_attr); 201 } 202 203 void hmp_info_mem(Monitor *mon, const QDict *qdict) 204 { 205 CPUArchState *env; 206 207 env = mon_get_cpu_env(); 208 if (!env) { 209 monitor_printf(mon, "No CPU available\n"); 210 return; 211 } 212 213 if (!riscv_feature(env, RISCV_FEATURE_MMU)) { 214 monitor_printf(mon, "S-mode MMU unavailable\n"); 215 return; 216 } 217 218 if (!(env->satp & SATP_MODE)) { 219 monitor_printf(mon, "No translation or protection\n"); 220 return; 221 } 222 223 mem_info_svxx(mon, env); 224 } 225