1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2016 by Matthias Welwarsky *
5 * matthias.welwarsky@sysgo.com *
7 * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com *
8 ***************************************************************************/
14 #include <helper/binarybuffer.h>
15 #include <helper/command.h>
17 #include "jtag/interface.h"
20 #include "armv7a_mmu.h"
21 #include "arm_opcodes.h"
24 #define SCTLR_BIT_AFE (1 << 29)
26 /* V7 method VA TO PA */
27 int armv7a_mmu_translate_va_pa(struct target
*target
, uint32_t va
,
28 target_addr_t
*val
, int meminfo
)
30 int retval
= ERROR_FAIL
;
31 struct armv7a_common
*armv7a
= target_to_armv7a(target
);
32 struct arm_dpm
*dpm
= armv7a
->arm
.dpm
;
33 uint32_t virt
= va
& ~0xfff, value
;
34 uint32_t NOS
, NS
, INNER
, OUTER
, SS
;
36 retval
= dpm
->prepare(dpm
);
37 if (retval
!= ERROR_OK
)
39 /* mmu must be enable in order to get a correct translation
40 * use VA to PA CP15 register for conversion */
41 retval
= dpm
->instr_write_data_r0(dpm
,
42 ARMV4_5_MCR(15, 0, 0, 7, 8, 0),
44 if (retval
!= ERROR_OK
)
46 retval
= dpm
->instr_read_data_r0(dpm
,
47 ARMV4_5_MRC(15, 0, 0, 7, 4, 0),
49 if (retval
!= ERROR_OK
)
52 /* decode memory attribute */
53 SS
= (value
>> 1) & 1;
54 NOS
= (value
>> 10) & 1; /* Not Outer shareable */
55 NS
= (value
>> 9) & 1; /* Non secure */
56 INNER
= (value
>> 4) & 0x7;
57 OUTER
= (value
>> 2) & 0x3;
60 /* PAR[31:24] contains PA[31:24] */
61 *val
= value
& 0xff000000;
62 /* PAR [23:16] contains PA[39:32] */
63 *val
|= (target_addr_t
)(value
& 0x00ff0000) << 16;
64 /* PA[23:12] is the same as VA[23:12] */
65 *val
|= (va
& 0xffffff);
67 *val
= (value
& ~0xfff) + (va
& 0xfff);
70 LOG_INFO("%" PRIx32
" : %" TARGET_PRIxADDR
" %s outer shareable %s secured %s super section",
72 NOS
== 1 ? "not" : " ",
74 SS
== 0 ? "not" : "");
77 LOG_INFO("outer: Non-Cacheable");
80 LOG_INFO("outer: Write-Back, Write-Allocate");
83 LOG_INFO("outer: Write-Through, No Write-Allocate");
86 LOG_INFO("outer: Write-Back, no Write-Allocate");
91 LOG_INFO("inner: Non-Cacheable");
94 LOG_INFO("inner: Strongly-ordered");
97 LOG_INFO("inner: Device");
100 LOG_INFO("inner: Write-Back, Write-Allocate");
103 LOG_INFO("inner: Write-Through");
106 LOG_INFO("inner: Write-Back, no Write-Allocate");
109 LOG_INFO("inner: %" PRIx32
" ???", INNER
);
119 static const char *desc_bits_to_string(bool c_bit
, bool b_bit
, bool s_bit
, bool ap2
, int ap10
, bool afe
)
121 static char bits_string
[64];
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 ");
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
;
139 priv_acc_r
= priv_acc_w
= false;
140 unpriv_acc_r
= unpriv_acc_w
= false;
143 unpriv_acc_r
= unpriv_acc_w
= false;
146 unpriv_acc_w
= false;
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");
157 if (len
>= sizeof(bits_string
))
163 static const char *l2_desc_bits_to_string(uint32_t l2_desc
, bool afe
)
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;
171 return desc_bits_to_string(c_bit
, b_bit
, s_bit
, ap2
, ap10
, afe
);
174 static const char *l1_desc_bits_to_string(uint32_t l1_desc
, bool afe
)
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;
182 return desc_bits_to_string(c_bit
, b_bit
, s_bit
, ap2
, ap10
, afe
);
185 COMMAND_HANDLER(armv7a_mmu_dump_table
)
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
;
197 int max_pt_idx
= 4095;
201 return ERROR_COMMAND_SYNTAX_ERROR
;
203 if (!strcmp(CMD_ARGV
[0], "addr")) {
205 return ERROR_COMMAND_SYNTAX_ERROR
;
207 COMMAND_PARSE_NUMBER(target_addr
, CMD_ARGV
[1], ttb
);
210 COMMAND_PARSE_NUMBER(int, CMD_ARGV
[2], max_pt_idx
);
212 if (max_pt_idx
< 1 || max_pt_idx
> 4096)
213 return ERROR_COMMAND_ARGUMENT_INVALID
;
217 if (mmu
->cached
!= 1) {
218 LOG_ERROR("TTB not cached!");
222 COMMAND_PARSE_NUMBER(int, CMD_ARGV
[0], ttbidx
);
223 if (ttbidx
< 0 || ttbidx
> 1)
224 return ERROR_COMMAND_ARGUMENT_INVALID
;
226 ttb
= mmu
->ttbr
[ttbidx
] & mmu
->ttbr_mask
[ttbidx
];
229 int ttbcr_n
= mmu
->ttbcr
& 0x7;
230 max_pt_idx
= 0x0fff >> ttbcr_n
;
234 LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR
, ttb
);
236 first_lvl_ptbl
= malloc(sizeof(uint32_t)*(max_pt_idx
+1));
241 * this may or may not be necessary depending on whether
242 * the table walker is configured to use the cache or not.
244 cache
->flush_all_data_cache(target
);
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!");
252 afe
= !!(cortex_a
->cp15_control_reg
& SCTLR_BIT_AFE
);
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
]);
258 LOG_DEBUG("L1 desc[%8.8x]: %8.8"PRIx32
, pt_idx
<< 20, first_lvl_descriptor
);
260 /* skip empty entries in the first level table */
261 if ((first_lvl_descriptor
& 3) == 0) {
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
;
270 uint32_t pa_start
= (first_lvl_descriptor
& 0xfff00000);
271 uint32_t pa_end
= pa_start
+ va_range
;
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
));
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
;
283 uint32_t pa_start
= (first_lvl_descriptor
& 0xff000000);
284 uint32_t pa_end
= pa_start
+ va_range
;
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
));
289 /* skip next 15 entries, they're duplicating the first entry */
292 target_addr_t second_lvl_ptbl
= first_lvl_descriptor
& 0xfffffc00;
293 uint32_t second_lvl_descriptor
;
297 /* page table, always 1KB long */
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!");
306 for (pt2_idx
= 0; pt2_idx
< 256; ) {
307 second_lvl_descriptor
= target_buffer_get_u32(target
,
308 (uint8_t *)&pt2
[pt2_idx
]);
310 if ((second_lvl_descriptor
& 3) == 0) {
314 if ((second_lvl_descriptor
& 3) == 1) {
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
;
320 uint32_t pa_start
= (second_lvl_descriptor
& 0xffff0000);
321 uint32_t pa_end
= pa_start
+ va_range
;
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
));
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
;
333 uint32_t pa_start
= (second_lvl_descriptor
& 0xfffff000);
334 uint32_t pa_end
= pa_start
+ va_range
;
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
));
347 free(first_lvl_ptbl
);
351 static const struct command_registration armv7a_mmu_group_handlers
[] = {
354 .handler
= armv7a_mmu_dump_table
,
356 .help
= "dump translation table 0, 1 or from <address>",
357 .usage
= "(0|1|addr <address> [num_entries])",
359 COMMAND_REGISTRATION_DONE
362 const struct command_registration armv7a_mmu_command_handlers
[] = {
366 .help
= "mmu command group",
368 .chain
= armv7a_mmu_group_handlers
,
370 COMMAND_REGISTRATION_DONE
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)