xref: /openbmc/qemu/target/riscv/monitor.c (revision df22fbb7)
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 last_start = -1;
88     target_ulong pgsize;
89     target_ulong pte;
90     int ptshift;
91     int attr;
92     int idx;
93 
94     if (level < 0) {
95         return;
96     }
97 
98     ptshift = level * ptidxbits;
99     pgsize = 1UL << (PGSHIFT + ptshift);
100 
101     for (idx = 0; idx < (1UL << ptidxbits); idx++) {
102         pte_addr = base + idx * ptesize;
103         cpu_physical_memory_read(pte_addr, &pte, ptesize);
104 
105         paddr = (hwaddr)(pte >> PTE_PPN_SHIFT) << PGSHIFT;
106         attr = pte & 0xff;
107 
108         /* PTE has to be valid */
109         if (attr & PTE_V) {
110             if (attr & (PTE_R | PTE_W | PTE_X)) {
111                 /*
112                  * A leaf PTE has been found
113                  *
114                  * If current PTE's permission bits differ from the last one,
115                  * or the current PTE breaks up a contiguous virtual or
116                  * physical mapping, address block together with the last one,
117                  * print out the last contiguous mapped block details.
118                  */
119                 if ((*last_attr != attr) ||
120                     (*last_paddr + *last_size != paddr) ||
121                     (last_start + *last_size != start)) {
122                     print_pte(mon, va_bits, *vbase, *pbase,
123                               *last_paddr + *last_size - *pbase, *last_attr);
124 
125                     *vbase = start;
126                     *pbase = paddr;
127                     *last_attr = attr;
128                 }
129 
130                 last_start = start;
131                 *last_paddr = paddr;
132                 *last_size = pgsize;
133             } else {
134                 /* pointer to the next level of the page table */
135                 walk_pte(mon, paddr, start, level - 1, ptidxbits, ptesize,
136                          va_bits, vbase, pbase, last_paddr,
137                          last_size, last_attr);
138             }
139         }
140 
141         start += pgsize;
142     }
143 
144 }
145 
146 static void mem_info_svxx(Monitor *mon, CPUArchState *env)
147 {
148     int levels, ptidxbits, ptesize, vm, va_bits;
149     hwaddr base;
150     target_ulong vbase;
151     hwaddr pbase;
152     hwaddr last_paddr;
153     target_ulong last_size;
154     int last_attr;
155 
156     if (riscv_cpu_mxl(env) == MXL_RV32) {
157         base = (hwaddr)get_field(env->satp, SATP32_PPN) << PGSHIFT;
158         vm = get_field(env->satp, SATP32_MODE);
159     } else {
160         base = (hwaddr)get_field(env->satp, SATP64_PPN) << PGSHIFT;
161         vm = get_field(env->satp, SATP64_MODE);
162     }
163 
164     switch (vm) {
165     case VM_1_10_SV32:
166         levels = 2;
167         ptidxbits = 10;
168         ptesize = 4;
169         break;
170     case VM_1_10_SV39:
171         levels = 3;
172         ptidxbits = 9;
173         ptesize = 8;
174         break;
175     case VM_1_10_SV48:
176         levels = 4;
177         ptidxbits = 9;
178         ptesize = 8;
179         break;
180     case VM_1_10_SV57:
181         levels = 5;
182         ptidxbits = 9;
183         ptesize = 8;
184         break;
185     default:
186         g_assert_not_reached();
187         break;
188     }
189 
190     /* calculate virtual address bits */
191     va_bits = PGSHIFT + levels * ptidxbits;
192 
193     /* print header */
194     print_pte_header(mon);
195 
196     vbase = -1;
197     pbase = -1;
198     last_paddr = -1;
199     last_size = 0;
200     last_attr = 0;
201 
202     /* walk page tables, starting from address 0 */
203     walk_pte(mon, base, 0, levels - 1, ptidxbits, ptesize, va_bits,
204              &vbase, &pbase, &last_paddr, &last_size, &last_attr);
205 
206     /* don't forget the last one */
207     print_pte(mon, va_bits, vbase, pbase,
208               last_paddr + last_size - pbase, last_attr);
209 }
210 
211 void hmp_info_mem(Monitor *mon, const QDict *qdict)
212 {
213     CPUArchState *env;
214 
215     env = mon_get_cpu_env(mon);
216     if (!env) {
217         monitor_printf(mon, "No CPU available\n");
218         return;
219     }
220 
221     if (!riscv_feature(env, RISCV_FEATURE_MMU)) {
222         monitor_printf(mon, "S-mode MMU unavailable\n");
223         return;
224     }
225 
226     if (riscv_cpu_mxl(env) == MXL_RV32) {
227         if (!(env->satp & SATP32_MODE)) {
228             monitor_printf(mon, "No translation or protection\n");
229             return;
230         }
231     } else {
232         if (!(env->satp & SATP64_MODE)) {
233             monitor_printf(mon, "No translation or protection\n");
234             return;
235         }
236     }
237 
238     mem_info_svxx(mon, env);
239 }
240