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

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)