1 /***************************************************************************
2 * Copyright (C) 2016 by Matthias Welwarsky *
3 * matthias.welwarsky@sysgo.com *
5 * Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com *
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. *
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. *
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 ***************************************************************************/
25 #include <helper/binarybuffer.h>
26 #include <helper/command.h>
28 #include "jtag/interface.h"
31 #include "armv7a_mmu.h"
32 #include "arm_opcodes.h"
35 #define SCTLR_BIT_AFE (1 << 29)
37 /* V7 method VA TO PA */
38 int armv7a_mmu_translate_va_pa(struct target *target, uint32_t va,
39 uint32_t *val, int meminfo)
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;
45 uint32_t NOS, NS, INNER, OUTER;
47 retval = dpm->prepare(dpm);
48 if (retval != ERROR_OK)
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),
55 if (retval != ERROR_OK)
57 retval = dpm->instr_read_data_r0(dpm,
58 ARMV4_5_MRC(15, 0, 0, 7, 4, 0),
60 if (retval != ERROR_OK)
62 /* decode memory attribute */
63 NOS = (*val >> 10) & 1; /* Not Outer shareable */
64 NS = (*val >> 9) & 1; /* Non secure */
65 INNER = (*val >> 4) & 0x7;
66 OUTER = (*val >> 2) & 0x3;
68 *val = (*val & ~0xfff) + (va & 0xfff);
70 LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured",
72 NOS == 1 ? "not" : " ",
73 NS == 1 ? "not" : "");
76 LOG_INFO("outer: Non-Cacheable");
79 LOG_INFO("outer: Write-Back, Write-Allocate");
82 LOG_INFO("outer: Write-Through, No Write-Allocate");
85 LOG_INFO("outer: Write-Back, no Write-Allocate");
90 LOG_INFO("inner: Non-Cacheable");
93 LOG_INFO("inner: Strongly-ordered");
96 LOG_INFO("inner: Device");
99 LOG_INFO("inner: Write-Back, Write-Allocate");
102 LOG_INFO("inner: Write-Through");
105 LOG_INFO("inner: Write-Back, no Write-Allocate");
108 LOG_INFO("inner: %" PRIx32 " ???", INNER);
118 static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe)
120 static char bits_string[64];
126 bool priv = !(ap10 & 2);
127 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access%s: %s%s",
128 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "",
129 priv ? "(priv)" : "", acc_r ? "R" : "N", acc_w ? "W " : "O ");
131 bool priv_acc_w = !ap2;
132 bool priv_acc_r = true;
133 bool unpriv_acc_w = priv_acc_w;
134 bool unpriv_acc_r = priv_acc_r;
138 priv_acc_r = priv_acc_w = false;
139 unpriv_acc_r = unpriv_acc_w = false;
142 unpriv_acc_r = unpriv_acc_w = false;
145 unpriv_acc_w = false;
151 len = snprintf(bits_string, sizeof(bits_string), "%s%s%s access(priv): %s%s access(unpriv): %s%s",
152 s_bit ? "S " : "", c_bit ? "C " : "", b_bit ? "B " : "", priv_acc_r ? "R" : "N", priv_acc_w ? "W" : "O",
153 unpriv_acc_r ? "R" : "N", unpriv_acc_w ? "W" : "O");
156 if (len >= sizeof(bits_string))
162 static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe)
164 bool c_bit = !!(l2_desc & (1 << 3));
165 bool b_bit = !!(l2_desc & (1 << 2));
166 bool s_bit = !!(l2_desc & (1 << 10));
167 bool ap2 = !!(l2_desc & (1 << 9));
168 int ap10 = (l2_desc >> 4) & 3;
170 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
173 static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe)
175 bool c_bit = !!(l1_desc & (1 << 3));
176 bool b_bit = !!(l1_desc & (1 << 2));
177 bool s_bit = !!(l1_desc & (1 << 16));
178 bool ap2 = !!(l1_desc & (1 << 15));
179 int ap10 = (l1_desc >> 10) & 3;
181 return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
184 COMMAND_HANDLER(armv7a_mmu_dump_table)
186 struct target *target = get_current_target(CMD_CTX);
187 struct cortex_a_common *cortex_a = target_to_cortex_a(target);
188 struct armv7a_common *armv7a = target_to_armv7a(target);
189 struct armv7a_mmu_common *mmu = &armv7a->armv7a_mmu;
190 struct armv7a_cache_common *cache = &mmu->armv7a_cache;
191 uint32_t *first_lvl_ptbl;
196 int max_pt_idx = 4095;
200 return ERROR_COMMAND_SYNTAX_ERROR;
202 if (!strcmp(CMD_ARGV[0], "addr")) {
204 return ERROR_COMMAND_SYNTAX_ERROR;
206 COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb);
209 COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx);
211 if (max_pt_idx < 1 || max_pt_idx > 4096)
212 return ERROR_COMMAND_ARGUMENT_INVALID;
216 if (mmu->cached != 1) {
217 LOG_ERROR("TTB not cached!");
221 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx);
222 if (ttbidx < 0 || ttbidx > 1)
223 return ERROR_COMMAND_ARGUMENT_INVALID;
225 ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx];
228 int ttbcr_n = mmu->ttbcr & 0x7;
229 max_pt_idx = 0x0fff >> ttbcr_n;
233 LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb);
235 first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1));
236 if (first_lvl_ptbl == NULL)
240 * this may or may not be necessary depending on whether
241 * the table walker is configured to use the cache or not.
243 cache->flush_all_data_cache(target);
245 retval = mmu->read_physical_memory(target, ttb, 4, max_pt_idx+1, (uint8_t *)first_lvl_ptbl);
246 if (retval != ERROR_OK) {
247 LOG_ERROR("Failed to read first-level page table!");
251 afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE);
253 for (pt_idx = 0; pt_idx <= max_pt_idx;) {
254 uint32_t first_lvl_descriptor = target_buffer_get_u32(target,
255 (uint8_t *)&first_lvl_ptbl[pt_idx]);
257 LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor);
259 /* skip empty entries in the first level table */
260 if ((first_lvl_descriptor & 3) == 0) {
263 if ((first_lvl_descriptor & 0x40002) == 2) {
264 /* section descriptor */
265 uint32_t va_range = 1024*1024-1; /* 1MB range */
266 uint32_t va_start = pt_idx << 20;
267 uint32_t va_end = va_start + va_range;
269 uint32_t pa_start = (first_lvl_descriptor & 0xfff00000);
270 uint32_t pa_end = pa_start + va_range;
272 LOG_USER("SECT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
273 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
276 if ((first_lvl_descriptor & 0x40002) == 0x40002) {
277 /* supersection descriptor */
278 uint32_t va_range = 16*1024*1024-1; /* 16MB range */
279 uint32_t va_start = pt_idx << 20;
280 uint32_t va_end = va_start + va_range;
282 uint32_t pa_start = (first_lvl_descriptor & 0xff000000);
283 uint32_t pa_end = pa_start + va_range;
285 LOG_USER("SSCT: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
286 va_start, va_end, pa_start, pa_end, l1_desc_bits_to_string(first_lvl_descriptor, afe));
288 /* skip next 15 entries, they're duplicating the first entry */
291 target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00;
292 uint32_t second_lvl_descriptor;
296 /* page table, always 1KB long */
298 retval = mmu->read_physical_memory(target, second_lvl_ptbl,
299 4, 256, (uint8_t *)pt2);
300 if (retval != ERROR_OK) {
301 LOG_ERROR("Failed to read second-level page table!");
305 for (pt2_idx = 0; pt2_idx < 256; ) {
306 second_lvl_descriptor = target_buffer_get_u32(target,
307 (uint8_t *)&pt2[pt2_idx]);
309 if ((second_lvl_descriptor & 3) == 0) {
313 if ((second_lvl_descriptor & 3) == 1) {
315 uint32_t va_range = 64*1024-1; /* 64KB range */
316 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
317 uint32_t va_end = va_start + va_range;
319 uint32_t pa_start = (second_lvl_descriptor & 0xffff0000);
320 uint32_t pa_end = pa_start + va_range;
322 LOG_USER("LPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
323 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
328 uint32_t va_range = 4*1024-1; /* 4KB range */
329 uint32_t va_start = (pt_idx << 20) + (pt2_idx << 12);
330 uint32_t va_end = va_start + va_range;
332 uint32_t pa_start = (second_lvl_descriptor & 0xfffff000);
333 uint32_t pa_end = pa_start + va_range;
335 LOG_USER("SPGE: VA[%8.8"PRIx32" -- %8.8"PRIx32"]: PA[%8.8"PRIx32" -- %8.8"PRIx32"] %s",
336 va_start, va_end, pa_start, pa_end, l2_desc_bits_to_string(second_lvl_descriptor, afe));
346 free(first_lvl_ptbl);
350 static const struct command_registration armv7a_mmu_group_handlers[] = {
353 .handler = armv7a_mmu_dump_table,
355 .help = "dump translation table 0, 1 or from <address>",
356 .usage = "(0|1|addr <address> [num_entries])",
358 COMMAND_REGISTRATION_DONE
361 const struct command_registration armv7a_mmu_command_handlers[] = {
365 .help = "mmu command group",
367 .chain = armv7a_mmu_group_handlers,
369 COMMAND_REGISTRATION_DONE