]>
Commit | Line | Data |
---|---|---|
df42fdd6 BM |
1 | /* |
2 | * QEMU monitor for RISC-V | |
3 | * | |
4 | * Copyright (c) 2019 Bin Meng <[email protected]> | |
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; | |
457a86a0 | 87 | target_ulong last_start = -1; |
df42fdd6 BM |
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, | |
457a86a0 RR |
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. | |
df42fdd6 BM |
118 | */ |
119 | if ((*last_attr != attr) || | |
457a86a0 RR |
120 | (*last_paddr + *last_size != paddr) || |
121 | (last_start + *last_size != start)) { | |
df42fdd6 BM |
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 | ||
457a86a0 | 130 | last_start = start; |
df42fdd6 BM |
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 | ||
db23e5d9 | 156 | if (riscv_cpu_mxl(env) == MXL_RV32) { |
419ddf00 AF |
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 | } | |
df42fdd6 | 163 | |
df42fdd6 BM |
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 | ||
e7cff9c6 | 215 | env = mon_get_cpu_env(mon); |
df42fdd6 BM |
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 | ||
db23e5d9 | 226 | if (riscv_cpu_mxl(env) == MXL_RV32) { |
419ddf00 AF |
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 | } | |
df42fdd6 BM |
236 | } |
237 | ||
238 | mem_info_svxx(mon, env); | |
239 | } |