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 };