6118417fce7d50331092dd0ad976e7eb603b4558
[openocd.git] / src / target / armv7a_mmu.c
1 /***************************************************************************
2 * Copyright (C) 2016 by Matthias Welwarsky *
3 * matthias.welwarsky@sysgo.com *
4 * *
5 * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com *
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 ***************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <helper/binarybuffer.h>
26 #include <helper/command.h>
27
28 #include "jtag/interface.h"
29 #include "arm.h"
30 #include "armv7a.h"
31 #include "armv7a_mmu.h"
32 #include "arm_opcodes.h"
33 #include "cortex_a.h"
34
35 #define SCTLR_BIT_AFE (1 << 29)
36
37 /* V7 method VA TO PA */
38 int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va,
39 uint32_t *val, int meminfo)
40 {
41 int retval = ERROR_FAIL;
42 struct armv7a_common *armv7a = target_to_armv7a(target);
43 struct arm_dpm *dpm = armv7a->arm.dpm;
44 uint32_t virt = va & ~0xfff;
45 uint32_t NOS, NS, INNER, OUTER;
46 *val = 0xdeadbeef;
47 retval = dpm->prepare(dpm);
48 if (retval != ERROR_OK)
49 goto done;
50 /* mmu must be enable in order to get a correct translation
51 * use VA to PA CP15 register for conversion */
52 retval = dpm->instr_write_data_r0(dpm,
53 ARMV4_5_MCR(15, 0, 0, 7, 8, 0),
54 virt);
55 if (retval != ERROR_OK)
56 goto done;
57 retval = dpm->instr_read_data_r0(dpm,
58 ARMV4_5_MRC(15, 0, 0, 7, 4, 0),
59 val);
60 if (retval != ERROR_OK)
61 goto done;
62 /* decode memory attribute */
63 NOS = (*val >> 10) & 1; /* Not Outer shareable */
64 NS = (*val >> 9) & 1; /* Non secure */
65 INNER = (*val >> 4) & 0x7;
66 OUTER = (*val >> 2) & 0x3;
67
68 *val = (*val & ~0xfff) + (va & 0xfff);
69 if (meminfo) {
70 LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured",
71 va, *val,
72 NOS == 1 ? "not" : " ",
73 NS == 1 ? "not" : "");
74 switch (OUTER) {
75 case 0:
76 LOG_INFO("outer: Non-Cacheable");
77 break;
78 case 1:
79 LOG_INFO("outer: Write-Back, Write-Allocate");
80 break;
81 case 2:
82 LOG_INFO("outer: Write-Through, No Write-Allocate");
83 break;
84 case 3:
85 LOG_INFO("outer: Write-Back, no Write-Allocate");
86 break;
87 }
88 switch (INNER) {
89 case 0:
90 LOG_INFO("inner: Non-Cacheable");
91 break;
92 case 1:
93 LOG_INFO("inner: Strongly-ordered");
94 break;
95 case 3:
96 LOG_INFO("inner: Device");
97 break;
98 case 5:
99 LOG_INFO("inner: Write-Back, Write-Allocate");
100 break;
101 case 6:
102 LOG_INFO("inner: Write-Through");
103 break;
104 case 7:
105 LOG_INFO("inner: Write-Back, no Write-Allocate");
106 break;
107 default:
108 LOG_INFO("inner: %" PRIx32 " ???", INNER);
109 }
110 }
111
112 done:
113 dpm->finish(dpm);
114
115 return retval;
116 }
117
118 static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe)
119 {
120 static char bits_string[64];
121 unsigned int len;
122
123 if (afe) {
124 bool acc_r = true;
125 bool acc_w = !ap2;
126 bool priv = !(ap10 & 2);
127 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s",
128 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "",
129 priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O ");
130 } else {
131 bool priv_acc_w = !ap2;
132 bool priv_acc_r = true;
133 bool unpriv_acc_w = priv_acc_w;
134 bool unpriv_acc_r = priv_acc_r;
135
136 switch (ap10) {
137 case 0:
138 priv_acc_r = priv_acc_w = false;
139 unpriv_acc_r = unpriv_acc_w = false;
140 break;
141 case 1:
142 unpriv_acc_r = unpriv_acc_w = false;
143 break;
144 case 2:
145 unpriv_acc_w = false;
146 break;
147 default:
148 break;
149 }
150
151 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s",
152 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O",
153 unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O");
154 }
155
156 if (len >= sizeof(bits_string))
157 bits_string[63] = 0;
158
159 return bits_string;
160 }
161
162 static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe)
163 {
164 bool c_bit = !!(l2_desc & (1 << 3));
165 bool b_bit = !!(l2_desc & (1 << 2));
166 bool s_bit = !!(l2_desc & (1 << 10));
167 bool ap2 = !!(l2_desc & (1 << 9));
168 int ap10 = (l2_desc >> 4) & 3;
169
170 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
171 }
172
173 static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe)
174 {
175 bool c_bit = !!(l1_desc & (1 << 3));
176 bool b_bit = !!(l1_desc & (1 << 2));
177 bool s_bit = !!(l1_desc & (1 << 16));
178 bool ap2 = !!(l1_desc & (1 << 15));
179 int ap10 = (l1_desc >> 10) & 3;
180
181 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
182 }
183
184 COMMAND_HANDLER(armv7a_mmu_dump_table)
185 {
186 struct target *target = get_current_target(CMD_CTX);
187 struct cortex_a_common *cortex_a = target_to_cortex_a(target);
188 struct armv7a_common *armv7a = target_to_armv7a(target);
189 struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu;
190 struct armv7a_cache_common *cache = &mmu->armv7a_cache;
191 uint32_t *first_lvl_ptbl;
192 target_addr_t ttb;
193 int ttbidx = 0;
194 int retval;
195 int pt_idx;
196 int max_pt_idx = 4095;
197 bool afe;
198
199 if (CMD_ARGC < 1)
200 return ERROR_COMMAND_SYNTAX_ERROR;
201
202 if (!strcmp(CMD_ARGV[0], "addr")) {
203 if (CMD_ARGC < 2)
204 return ERROR_COMMAND_SYNTAX_ERROR;
205
206 COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb);
207
208 if (CMD_ARGC > 2) {
209 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx);
210
211 if (max_pt_idx < 1 || max_pt_idx > 4096)
212 return ERROR_COMMAND_ARGUMENT_INVALID;
213 max_pt_idx -= 1;
214 }
215 } else {
216 if (mmu->cached != 1) {
217 LOG_ERROR("TTB not cached!");
218 return ERROR_FAIL;
219 }
220
221 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx);
222 if (ttbidx < 0 || ttbidx > 1)
223 return ERROR_COMMAND_ARGUMENT_INVALID;
224
225 ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx];
226
227 if (ttbidx == 0) {
228 int ttbcr_n = mmu->ttbcr & 0x7;
229 max_pt_idx = 0x0fff >> ttbcr_n;
230 }
231 }
232
233 LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb);
234
235 first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1));
236 if (first_lvl_ptbl == NULL)
237 return ERROR_FAIL;
238
239 /*
240 * this may or may not be necessary depending on whether
241 * the table walker is configured to use the cache or not.
242 */
243 cache->flush_all_data_cache(target);
244
245 retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl);
246 if (retval != ERROR_OK) {
247 LOG_ERROR("Failed to read first-level page table!");
248 return retval;
249 }
250
251 afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE);
252
253 for (pt_idx = 0; pt_idx <= max_pt_idx;) {
254 uint32_t first_lvl_descriptor = target_buffer_get_u32(target,
255 (uint8_t *)&first_lvl_ptbl[pt_idx]);
256
257 LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor);
258
259 /* skip empty entries in the first level table */
260 if ((first_lvl_descriptor & 3) == 0) {
261 pt_idx++;
262 } else
263 if ((first_lvl_descriptor & 0x40002) == 2) {
264 /* section descriptor */
265 uint32_t va_range = 1024*1024-1; /* 1MB range */
266 uint32_t va_start = pt_idx << 20;
267 uint32_t va_end = va_start + va_range;
268
269 uint32_t pa_start = (first_lvl_descriptor & 0xfff00000);
270 uint32_t pa_end = pa_start + va_range;
271
272 LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
273 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
274 pt_idx++;
275 } else
276 if ((first_lvl_descriptor & 0x40002) == 0x40002) {
277 /* supersection descriptor */
278 uint32_t va_range = 16*1024*1024-1; /* 16MB range */
279 uint32_t va_start = pt_idx << 20;
280 uint32_t va_end = va_start + va_range;
281
282 uint32_t pa_start = (first_lvl_descriptor & 0xff000000);
283 uint32_t pa_end = pa_start + va_range;
284
285 LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
286 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
287
288 /* skip next 15 entries, they're duplicating the first entry */
289 pt_idx += 16;
290 } else {
291 target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00;
292 uint32_t second_lvl_descriptor;
293 uint32_t *pt2;
294 int pt2_idx;
295
296 /* page table, always 1KB long */
297 pt2 = malloc(1024);
298 retval = mmu->read_physical_memory(target, second_lvl_ptbl,
299 4, 256, (uint8_t *)pt2);
300 if (retval != ERROR_OK) {
301 LOG_ERROR("Failed to read second-level page table!");
302 return ERROR_FAIL;
303 }
304
305 for (pt2_idx = 0; pt2_idx < 256; ) {
306 second_lvl_descriptor = target_buffer_get_u32(target,
307 (uint8_t *)&pt2[pt2_idx]);
308
309 if ((second_lvl_descriptor & 3) == 0) {
310 /* skip entry */
311 pt2_idx++;
312 } else
313 if ((second_lvl_descriptor & 3) == 1) {
314 /* large page */
315 uint32_t va_range = 64*1024-1; /* 64KB range */
316 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
317 uint32_t va_end = va_start + va_range;
318
319 uint32_t pa_start = (second_lvl_descriptor & 0xffff0000);
320 uint32_t pa_end = pa_start + va_range;
321
322 LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
323 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
324
325 pt2_idx += 16;
326 } else {
327 /* small page */
328 uint32_t va_range = 4*1024-1; /* 4KB range */
329 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
330 uint32_t va_end = va_start + va_range;
331
332 uint32_t pa_start = (second_lvl_descriptor & 0xfffff000);
333 uint32_t pa_end = pa_start + va_range;
334
335 LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
336 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
337
338 pt2_idx++;
339 }
340 }
341 free(pt2);
342 pt_idx++;
343 }
344 }
345
346 free(first_lvl_ptbl);
347 return ERROR_OK;
348 }
349
350 static const struct command_registration armv7a_mmu_group_handlers[] = {
351 {
352 .name = "dump",
353 .handler = armv7a_mmu_dump_table,
354 .mode = COMMAND_ANY,
355 .help = "dump translation table 0, 1 or from <address>",
356 .usage = "(0|1|addr <address> [num_entries])",
357 },
358 COMMAND_REGISTRATION_DONE
359 };
360
361 const struct command_registration armv7a_mmu_command_handlers[] = {
362 {
363 .name = "mmu",
364 .mode = COMMAND_ANY,
365 .help = "mmu command group",
366 .usage = "",
367 .chain = armv7a_mmu_group_handlers,
368 },
369 COMMAND_REGISTRATION_DONE
370 };

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)