openocd: fix SPDX tag format for files .c
[fw/openocd] / src / target / armv7a_mmu.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4  *   Copyright (C) 2016 by Matthias Welwarsky                              *
5  *   matthias.welwarsky@sysgo.com                                          *
6  *                                                                         *
7  *   Copyright (C) ST-Ericsson SA 2011 michel.jaouen@stericsson.com        *
8  ***************************************************************************/
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #include <helper/binarybuffer.h>
15 #include <helper/command.h>
16
17 #include "jtag/interface.h"
18 #include "arm.h"
19 #include "armv7a.h"
20 #include "armv7a_mmu.h"
21 #include "arm_opcodes.h"
22 #include "cortex_a.h"
23
24 #define SCTLR_BIT_AFE (1 << 29)
25
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)
29 {
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;
35         *val = 0xdeadbeef;
36         retval = dpm->prepare(dpm);
37         if (retval != ERROR_OK)
38                 goto done;
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),
43                         virt);
44         if (retval != ERROR_OK)
45                 goto done;
46         retval = dpm->instr_read_data_r0(dpm,
47                         ARMV4_5_MRC(15, 0, 0, 7, 4, 0),
48                         &value);
49         if (retval != ERROR_OK)
50                 goto done;
51
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;
58
59         if (SS) {
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);
66         } else {
67                 *val = (value & ~0xfff)  +  (va & 0xfff);
68         }
69         if (meminfo) {
70                 LOG_INFO("%" PRIx32 " : %" TARGET_PRIxADDR " %s outer shareable %s secured %s super section",
71                         va, *val,
72                         NOS == 1 ? "not" : " ",
73                         NS == 1 ? "not" : "",
74                         SS == 0 ? "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)
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.8x]: %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 };