armv7a: ARMv7-A MMU tools
[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 /* method adapted to Cortex-A : reused ARM v4 v5 method */
38 int armv7a_mmu_translate_va(struct target *target, uint32_t va, uint32_t *val)
39 {
40 uint32_t first_lvl_descriptor = 0x0;
41 uint32_t second_lvl_descriptor = 0x0;
42 int retval;
43 struct armv7a_common *armv7a = target_to_armv7a(target);
44 uint32_t ttbidx = 0; /* default to ttbr0 */
45 uint32_t ttb_mask;
46 uint32_t va_mask;
47 uint32_t ttb;
48
49 if (target->state != TARGET_HALTED)
50 LOG_INFO("target not halted, using cached values for translation table!");
51
52 /* if va is above the range handled by ttbr0, select ttbr1 */
53 if (va > armv7a->armv7a_mmu.ttbr_range[0]) {
54 /* select ttb 1 */
55 ttbidx = 1;
56 }
57
58 ttb = armv7a->armv7a_mmu.ttbr[ttbidx];
59 ttb_mask = armv7a->armv7a_mmu.ttbr_mask[ttbidx];
60 va_mask = 0xfff00000 & armv7a->armv7a_mmu.ttbr_range[ttbidx];
61
62 LOG_DEBUG("ttb_mask %" PRIx32 " va_mask %" PRIx32 " ttbidx %i",
63 ttb_mask, va_mask, ttbidx);
64 retval = armv7a->armv7a_mmu.read_physical_memory(target,
65 (ttb & ttb_mask) | ((va & va_mask) >> 18),
66 4, 1, (uint8_t *)&first_lvl_descriptor);
67 if (retval != ERROR_OK)
68 return retval;
69 first_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *)
70 &first_lvl_descriptor);
71 /* reuse armv4_5 piece of code, specific armv7a changes may come later */
72 LOG_DEBUG("1st lvl desc: %8.8" PRIx32 "", first_lvl_descriptor);
73
74 if ((first_lvl_descriptor & 0x3) == 0) {
75 LOG_ERROR("Address translation failure");
76 return ERROR_TARGET_TRANSLATION_FAULT;
77 }
78
79 if ((first_lvl_descriptor & 0x40002) == 2) {
80 /* section descriptor */
81 *val = (first_lvl_descriptor & 0xfff00000) | (va & 0x000fffff);
82 return ERROR_OK;
83 } else if ((first_lvl_descriptor & 0x40002) == 0x40002) {
84 /* supersection descriptor */
85 if (first_lvl_descriptor & 0x00f001e0) {
86 LOG_ERROR("Physical address does not fit into 32 bits");
87 return ERROR_TARGET_TRANSLATION_FAULT;
88 }
89 *val = (first_lvl_descriptor & 0xff000000) | (va & 0x00ffffff);
90 return ERROR_OK;
91 }
92
93 /* page table */
94 retval = armv7a->armv7a_mmu.read_physical_memory(target,
95 (first_lvl_descriptor & 0xfffffc00) | ((va & 0x000ff000) >> 10),
96 4, 1, (uint8_t *)&second_lvl_descriptor);
97 if (retval != ERROR_OK)
98 return retval;
99
100 second_lvl_descriptor = target_buffer_get_u32(target, (uint8_t *)
101 &second_lvl_descriptor);
102
103 LOG_DEBUG("2nd lvl desc: %8.8" PRIx32 "", second_lvl_descriptor);
104
105 if ((second_lvl_descriptor & 0x3) == 0) {
106 LOG_ERROR("Address translation failure");
107 return ERROR_TARGET_TRANSLATION_FAULT;
108 }
109
110 if ((second_lvl_descriptor & 0x3) == 1) {
111 /* large page descriptor */
112 *val = (second_lvl_descriptor & 0xffff0000) | (va & 0x0000ffff);
113 } else {
114 /* small page descriptor */
115 *val = (second_lvl_descriptor & 0xfffff000) | (va & 0x00000fff);
116 }
117
118 return ERROR_OK;
119 }
120
121 /* V7 method VA TO PA */
122 int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va,
123 uint32_t *val, int meminfo)
124 {
125 int retval = ERROR_FAIL;
126 struct armv7a_common *armv7a = target_to_armv7a(target);
127 struct arm_dpm *dpm = armv7a->arm.dpm;
128 uint32_t virt = va & ~0xfff;
129 uint32_t NOS, NS, INNER, OUTER;
130 *val = 0xdeadbeef;
131 retval = dpm->prepare(dpm);
132 if (retval != ERROR_OK)
133 goto done;
134 /* mmu must be enable in order to get a correct translation
135 * use VA to PA CP15 register for conversion */
136 retval = dpm->instr_write_data_r0(dpm,
137 ARMV4_5_MCR(15, 0, 0, 7, 8, 0),
138 virt);
139 if (retval != ERROR_OK)
140 goto done;
141 retval = dpm->instr_read_data_r0(dpm,
142 ARMV4_5_MRC(15, 0, 0, 7, 4, 0),
143 val);
144 /* decode memory attribute */
145 NOS = (*val >> 10) & 1; /* Not Outer shareable */
146 NS = (*val >> 9) & 1; /* Non secure */
147 INNER = (*val >> 4) & 0x7;
148 OUTER = (*val >> 2) & 0x3;
149
150 if (retval != ERROR_OK)
151 goto done;
152 *val = (*val & ~0xfff) + (va & 0xfff);
153 if (*val == va)
154 LOG_WARNING("virt = phys : MMU disable !!");
155 if (meminfo) {
156 LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured",
157 va, *val,
158 NOS == 1 ? "not" : " ",
159 NS == 1 ? "not" : "");
160 switch (OUTER) {
161 case 0:
162 LOG_INFO("outer: Non-Cacheable");
163 break;
164 case 1:
165 LOG_INFO("outer: Write-Back, Write-Allocate");
166 break;
167 case 2:
168 LOG_INFO("outer: Write-Through, No Write-Allocate");
169 break;
170 case 3:
171 LOG_INFO("outer: Write-Back, no Write-Allocate");
172 break;
173 }
174 switch (INNER) {
175 case 0:
176 LOG_INFO("inner: Non-Cacheable");
177 break;
178 case 1:
179 LOG_INFO("inner: Strongly-ordered");
180 break;
181 case 3:
182 LOG_INFO("inner: Device");
183 break;
184 case 5:
185 LOG_INFO("inner: Write-Back, Write-Allocate");
186 break;
187 case 6:
188 LOG_INFO("inner: Write-Through");
189 break;
190 case 7:
191 LOG_INFO("inner: Write-Back, no Write-Allocate");
192 break;
193 default:
194 LOG_INFO("inner: %" PRIx32 " ???", INNER);
195 }
196 }
197
198 done:
199 dpm->finish(dpm);
200
201 return retval;
202 }
203
204 static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe)
205 {
206 static char bits_string[64];
207 unsigned int len;
208
209 if (afe) {
210 bool acc_r = true;
211 bool acc_w = !ap2;
212 bool priv = !(ap10 & 2);
213 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s",
214 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "",
215 priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O ");
216 } else {
217 bool priv_acc_w = !ap2;
218 bool priv_acc_r = true;
219 bool unpriv_acc_w = priv_acc_w;
220 bool unpriv_acc_r = priv_acc_r;
221
222 switch (ap10) {
223 case 0:
224 priv_acc_r = priv_acc_w = false;
225 unpriv_acc_r = unpriv_acc_w = false;
226 break;
227 case 1:
228 unpriv_acc_r = unpriv_acc_w = false;
229 break;
230 case 2:
231 unpriv_acc_w = false;
232 break;
233 default:
234 break;
235 }
236
237 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s",
238 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O",
239 unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O");
240 }
241
242 if (len >= sizeof(bits_string))
243 bits_string[63] = 0;
244
245 return bits_string;
246 }
247
248 static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe)
249 {
250 bool c_bit = !!(l2_desc & (1 << 3));
251 bool b_bit = !!(l2_desc & (1 << 2));
252 bool s_bit = !!(l2_desc & (1 << 10));
253 bool ap2 = !!(l2_desc & (1 << 9));
254 int ap10 = (l2_desc >> 4) & 3;
255
256 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
257 }
258
259 static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe)
260 {
261 bool c_bit = !!(l1_desc & (1 << 3));
262 bool b_bit = !!(l1_desc & (1 << 2));
263 bool s_bit = !!(l1_desc & (1 << 16));
264 bool ap2 = !!(l1_desc & (1 << 15));
265 int ap10 = (l1_desc >> 10) & 3;
266
267 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
268 }
269
270 COMMAND_HANDLER(armv7a_mmu_dump_table)
271 {
272 struct target *target = get_current_target(CMD_CTX);
273 struct cortex_a_common *cortex_a = target_to_cortex_a(target);
274 struct armv7a_common *armv7a = target_to_armv7a(target);
275 struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu;
276 struct armv7a_cache_common *cache = &mmu->armv7a_cache;
277 uint32_t *first_lvl_ptbl;
278 target_addr_t ttb;
279 int ttbidx = 0;
280 int retval;
281 int pt_idx;
282 int max_pt_idx = 4095;
283 bool afe;
284
285 if (CMD_ARGC < 1)
286 return ERROR_COMMAND_SYNTAX_ERROR;
287
288 if (!strcmp(CMD_ARGV[0], "addr")) {
289 if (CMD_ARGC < 2)
290 return ERROR_COMMAND_SYNTAX_ERROR;
291
292 COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb);
293
294 if (CMD_ARGC > 2) {
295 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx);
296
297 if (max_pt_idx < 1 || max_pt_idx > 4096)
298 return ERROR_COMMAND_ARGUMENT_INVALID;
299 max_pt_idx -= 1;
300 }
301 } else {
302 if (mmu->cached != 1) {
303 LOG_ERROR("TTB not cached!");
304 return ERROR_FAIL;
305 }
306
307 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx);
308 if (ttbidx < 0 || ttbidx > 1)
309 return ERROR_COMMAND_ARGUMENT_INVALID;
310
311 ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx];
312
313 if (ttbidx == 0) {
314 int ttbcr_n = mmu->ttbcr & 0x7;
315 max_pt_idx = 0x0fff >> ttbcr_n;
316 }
317 }
318
319 LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb);
320
321 first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1));
322 if (first_lvl_ptbl == NULL)
323 return ERROR_FAIL;
324
325 /*
326 * this may or may not be necessary depending on whether
327 * the table walker is configured to use the cache or not.
328 */
329 cache->flush_all_data_cache(target);
330
331 retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl);
332 if (retval != ERROR_OK) {
333 LOG_ERROR("Failed to read first-level page table!");
334 return retval;
335 }
336
337 afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE);
338
339 for (pt_idx = 0; pt_idx <= max_pt_idx;) {
340 uint32_t first_lvl_descriptor = target_buffer_get_u32(target,
341 (uint8_t *)&first_lvl_ptbl[pt_idx]);
342
343 LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor);
344
345 /* skip empty entries in the first level table */
346 if ((first_lvl_descriptor & 3) == 0) {
347 pt_idx++;
348 } else
349 if ((first_lvl_descriptor & 0x40002) == 2) {
350 /* section descriptor */
351 uint32_t va_range = 1024*1024-1; /* 1MB range */
352 uint32_t va_start = pt_idx << 20;
353 uint32_t va_end = va_start + va_range;
354
355 uint32_t pa_start = (first_lvl_descriptor & 0xfff00000);
356 uint32_t pa_end = pa_start + va_range;
357
358 LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
359 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
360 pt_idx++;
361 } else
362 if ((first_lvl_descriptor & 0x40002) == 0x40002) {
363 /* supersection descriptor */
364 uint32_t va_range = 16*1024*1024-1; /* 16MB range */
365 uint32_t va_start = pt_idx << 20;
366 uint32_t va_end = va_start + va_range;
367
368 uint32_t pa_start = (first_lvl_descriptor & 0xff000000);
369 uint32_t pa_end = pa_start + va_range;
370
371 LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
372 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
373
374 /* skip next 15 entries, they're duplicating the first entry */
375 pt_idx += 16;
376 } else {
377 target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00;
378 uint32_t second_lvl_descriptor;
379 uint32_t *pt2;
380 int pt2_idx;
381
382 /* page table, always 1KB long */
383 pt2 = malloc(1024);
384 retval = mmu->read_physical_memory(target, second_lvl_ptbl,
385 4, 256, (uint8_t *)pt2);
386 if (retval != ERROR_OK) {
387 LOG_ERROR("Failed to read second-level page table!");
388 return ERROR_FAIL;
389 }
390
391 for (pt2_idx = 0; pt2_idx < 256; ) {
392 second_lvl_descriptor = target_buffer_get_u32(target,
393 (uint8_t *)&pt2[pt2_idx]);
394
395 if ((second_lvl_descriptor & 3) == 0) {
396 /* skip entry */
397 pt2_idx++;
398 } else
399 if ((second_lvl_descriptor & 3) == 1) {
400 /* large page */
401 uint32_t va_range = 64*1024-1; /* 64KB range */
402 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
403 uint32_t va_end = va_start + va_range;
404
405 uint32_t pa_start = (second_lvl_descriptor & 0xffff0000);
406 uint32_t pa_end = pa_start + va_range;
407
408 LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
409 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
410
411 pt2_idx += 16;
412 } else {
413 /* small page */
414 uint32_t va_range = 4*1024-1; /* 4KB range */
415 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
416 uint32_t va_end = va_start + va_range;
417
418 uint32_t pa_start = (second_lvl_descriptor & 0xfffff000);
419 uint32_t pa_end = pa_start + va_range;
420
421 LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
422 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
423
424 pt2_idx++;
425 }
426 }
427 free(pt2);
428 pt_idx++;
429 }
430 }
431
432 free(first_lvl_ptbl);
433 return ERROR_OK;
434 }
435
436 static const struct command_registration armv7a_mmu_group_handlers[] = {
437 {
438 .name = "dump",
439 .handler = armv7a_mmu_dump_table,
440 .mode = COMMAND_ANY,
441 .help = "dump translation table 0, 1 or from <address>",
442 .usage = "(0|1|addr <address> [num_entries])",
443 },
444 COMMAND_REGISTRATION_DONE
445 };
446
447 const struct command_registration armv7a_mmu_command_handlers[] = {
448 {
449 .name = "mmu",
450 .mode = COMMAND_ANY,
451 .help = "mmu command group",
452 .usage = "",
453 .chain = armv7a_mmu_group_handlers,
454 },
455 COMMAND_REGISTRATION_DONE
456 };

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)