armv7a_mmu: Add support for decoding Super Sections
[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 target_addr_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, value;
45 uint32_t NOS, NS, INNER, OUTER, SS;
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 &value);
60 if (retval != ERROR_OK)
61 goto done;
62
63 /* decode memory attribute */
64 SS = (value >> 1) & 1;
65 #if !BUILD_TARGET64
66 if (SS) {
67 LOG_ERROR("Super section found with no-64 bit address support");
68 return ERROR_FAIL;
69 }
70 #endif
71 NOS = (value >> 10) & 1; /* Not Outer shareable */
72 NS = (value >> 9) & 1; /* Non secure */
73 INNER = (value >> 4) & 0x7;
74 OUTER = (value >> 2) & 0x3;
75
76 if (SS) {
77 /* PAR[31:24] contains PA[31:24] */
78 *val = value & 0xff000000;
79 /* PAR [23:16] contains PA[39:32] */
80 *val |= (target_addr_t)(value & 0x00ff0000) << 16;
81 /* PA[23:12] is the same as VA[23:12] */
82 *val |= (va & 0xffffff);
83 } else {
84 *val = (value & ~0xfff) + (va & 0xfff);
85 }
86 if (meminfo) {
87 LOG_INFO("%" PRIx32 " : %" TARGET_PRIxADDR " %s outer shareable %s secured %s super section",
88 va, *val,
89 NOS == 1 ? "not" : " ",
90 NS == 1 ? "not" : "",
91 SS == 0 ? "not" : "");
92 switch (OUTER) {
93 case 0:
94 LOG_INFO("outer: Non-Cacheable");
95 break;
96 case 1:
97 LOG_INFO("outer: Write-Back, Write-Allocate");
98 break;
99 case 2:
100 LOG_INFO("outer: Write-Through, No Write-Allocate");
101 break;
102 case 3:
103 LOG_INFO("outer: Write-Back, no Write-Allocate");
104 break;
105 }
106 switch (INNER) {
107 case 0:
108 LOG_INFO("inner: Non-Cacheable");
109 break;
110 case 1:
111 LOG_INFO("inner: Strongly-ordered");
112 break;
113 case 3:
114 LOG_INFO("inner: Device");
115 break;
116 case 5:
117 LOG_INFO("inner: Write-Back, Write-Allocate");
118 break;
119 case 6:
120 LOG_INFO("inner: Write-Through");
121 break;
122 case 7:
123 LOG_INFO("inner: Write-Back, no Write-Allocate");
124 break;
125 default:
126 LOG_INFO("inner: %" PRIx32 " ???", INNER);
127 }
128 }
129
130 done:
131 dpm->finish(dpm);
132
133 return retval;
134 }
135
136 static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe)
137 {
138 static char bits_string[64];
139 unsigned int len;
140
141 if (afe) {
142 bool acc_r = true;
143 bool acc_w = !ap2;
144 bool priv = !(ap10 & 2);
145 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s",
146 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "",
147 priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O ");
148 } else {
149 bool priv_acc_w = !ap2;
150 bool priv_acc_r = true;
151 bool unpriv_acc_w = priv_acc_w;
152 bool unpriv_acc_r = priv_acc_r;
153
154 switch (ap10) {
155 case 0:
156 priv_acc_r = priv_acc_w = false;
157 unpriv_acc_r = unpriv_acc_w = false;
158 break;
159 case 1:
160 unpriv_acc_r = unpriv_acc_w = false;
161 break;
162 case 2:
163 unpriv_acc_w = false;
164 break;
165 default:
166 break;
167 }
168
169 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s",
170 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O",
171 unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O");
172 }
173
174 if (len >= sizeof(bits_string))
175 bits_string[63] = 0;
176
177 return bits_string;
178 }
179
180 static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe)
181 {
182 bool c_bit = !!(l2_desc & (1 << 3));
183 bool b_bit = !!(l2_desc & (1 << 2));
184 bool s_bit = !!(l2_desc & (1 << 10));
185 bool ap2 = !!(l2_desc & (1 << 9));
186 int ap10 = (l2_desc >> 4) & 3;
187
188 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
189 }
190
191 static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe)
192 {
193 bool c_bit = !!(l1_desc & (1 << 3));
194 bool b_bit = !!(l1_desc & (1 << 2));
195 bool s_bit = !!(l1_desc & (1 << 16));
196 bool ap2 = !!(l1_desc & (1 << 15));
197 int ap10 = (l1_desc >> 10) & 3;
198
199 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
200 }
201
202 COMMAND_HANDLER(armv7a_mmu_dump_table)
203 {
204 struct target *target = get_current_target(CMD_CTX);
205 struct cortex_a_common *cortex_a = target_to_cortex_a(target);
206 struct armv7a_common *armv7a = target_to_armv7a(target);
207 struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu;
208 struct armv7a_cache_common *cache = &mmu->armv7a_cache;
209 uint32_t *first_lvl_ptbl;
210 target_addr_t ttb;
211 int ttbidx = 0;
212 int retval;
213 int pt_idx;
214 int max_pt_idx = 4095;
215 bool afe;
216
217 if (CMD_ARGC < 1)
218 return ERROR_COMMAND_SYNTAX_ERROR;
219
220 if (!strcmp(CMD_ARGV[0], "addr")) {
221 if (CMD_ARGC < 2)
222 return ERROR_COMMAND_SYNTAX_ERROR;
223
224 COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb);
225
226 if (CMD_ARGC > 2) {
227 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx);
228
229 if (max_pt_idx < 1 || max_pt_idx > 4096)
230 return ERROR_COMMAND_ARGUMENT_INVALID;
231 max_pt_idx -= 1;
232 }
233 } else {
234 if (mmu->cached != 1) {
235 LOG_ERROR("TTB not cached!");
236 return ERROR_FAIL;
237 }
238
239 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx);
240 if (ttbidx < 0 || ttbidx > 1)
241 return ERROR_COMMAND_ARGUMENT_INVALID;
242
243 ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx];
244
245 if (ttbidx == 0) {
246 int ttbcr_n = mmu->ttbcr & 0x7;
247 max_pt_idx = 0x0fff >> ttbcr_n;
248 }
249 }
250
251 LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb);
252
253 first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1));
254 if (first_lvl_ptbl == NULL)
255 return ERROR_FAIL;
256
257 /*
258 * this may or may not be necessary depending on whether
259 * the table walker is configured to use the cache or not.
260 */
261 cache->flush_all_data_cache(target);
262
263 retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl);
264 if (retval != ERROR_OK) {
265 LOG_ERROR("Failed to read first-level page table!");
266 return retval;
267 }
268
269 afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE);
270
271 for (pt_idx = 0; pt_idx <= max_pt_idx;) {
272 uint32_t first_lvl_descriptor = target_buffer_get_u32(target,
273 (uint8_t *)&first_lvl_ptbl[pt_idx]);
274
275 LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor);
276
277 /* skip empty entries in the first level table */
278 if ((first_lvl_descriptor & 3) == 0) {
279 pt_idx++;
280 } else
281 if ((first_lvl_descriptor & 0x40002) == 2) {
282 /* section descriptor */
283 uint32_t va_range = 1024*1024-1; /* 1MB range */
284 uint32_t va_start = pt_idx << 20;
285 uint32_t va_end = va_start + va_range;
286
287 uint32_t pa_start = (first_lvl_descriptor & 0xfff00000);
288 uint32_t pa_end = pa_start + va_range;
289
290 LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
291 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
292 pt_idx++;
293 } else
294 if ((first_lvl_descriptor & 0x40002) == 0x40002) {
295 /* supersection descriptor */
296 uint32_t va_range = 16*1024*1024-1; /* 16MB range */
297 uint32_t va_start = pt_idx << 20;
298 uint32_t va_end = va_start + va_range;
299
300 uint32_t pa_start = (first_lvl_descriptor & 0xff000000);
301 uint32_t pa_end = pa_start + va_range;
302
303 LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
304 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
305
306 /* skip next 15 entries, they're duplicating the first entry */
307 pt_idx += 16;
308 } else {
309 target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00;
310 uint32_t second_lvl_descriptor;
311 uint32_t *pt2;
312 int pt2_idx;
313
314 /* page table, always 1KB long */
315 pt2 = malloc(1024);
316 retval = mmu->read_physical_memory(target, second_lvl_ptbl,
317 4, 256, (uint8_t *)pt2);
318 if (retval != ERROR_OK) {
319 LOG_ERROR("Failed to read second-level page table!");
320 return ERROR_FAIL;
321 }
322
323 for (pt2_idx = 0; pt2_idx < 256; ) {
324 second_lvl_descriptor = target_buffer_get_u32(target,
325 (uint8_t *)&pt2[pt2_idx]);
326
327 if ((second_lvl_descriptor & 3) == 0) {
328 /* skip entry */
329 pt2_idx++;
330 } else
331 if ((second_lvl_descriptor & 3) == 1) {
332 /* large page */
333 uint32_t va_range = 64*1024-1; /* 64KB range */
334 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
335 uint32_t va_end = va_start + va_range;
336
337 uint32_t pa_start = (second_lvl_descriptor & 0xffff0000);
338 uint32_t pa_end = pa_start + va_range;
339
340 LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
341 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
342
343 pt2_idx += 16;
344 } else {
345 /* small page */
346 uint32_t va_range = 4*1024-1; /* 4KB range */
347 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
348 uint32_t va_end = va_start + va_range;
349
350 uint32_t pa_start = (second_lvl_descriptor & 0xfffff000);
351 uint32_t pa_end = pa_start + va_range;
352
353 LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
354 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
355
356 pt2_idx++;
357 }
358 }
359 free(pt2);
360 pt_idx++;
361 }
362 }
363
364 free(first_lvl_ptbl);
365 return ERROR_OK;
366 }
367
368 static const struct command_registration armv7a_mmu_group_handlers[] = {
369 {
370 .name = "dump",
371 .handler = armv7a_mmu_dump_table,
372 .mode = COMMAND_ANY,
373 .help = "dump translation table 0, 1 or from <address>",
374 .usage = "(0|1|addr <address> [num_entries])",
375 },
376 COMMAND_REGISTRATION_DONE
377 };
378
379 const struct command_registration armv7a_mmu_command_handlers[] = {
380 {
381 .name = "mmu",
382 .mode = COMMAND_ANY,
383 .help = "mmu command group",
384 .usage = "",
385 .chain = armv7a_mmu_group_handlers,
386 },
387 COMMAND_REGISTRATION_DONE
388 };

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)