6118417fce7d50331092dd0ad976e7eb603b4558
[fw/openocd] / 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         uint32_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;
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                         val);
60         if (retval != ERROR_OK)
61                 goto done;
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;
67
68         *val = (*val & ~0xfff)  +  (va & 0xfff);
69         if (meminfo) {
70                 LOG_INFO("%" PRIx32 " : %" PRIx32 " %s outer shareable %s secured",
71                         va, *val,
72                         NOS == 1 ? "not" : " ",
73                         NS == 1 ? "not" : "");
74                 switch (OUTER) {
75                         case 0:
76                                 LOG_INFO("outer: Non-Cacheable");
77                                 break;
78                         case 1:
79                                 LOG_INFO("outer: Write-Back, Write-Allocate");
80                                 break;
81                         case 2:
82                                 LOG_INFO("outer: Write-Through, No Write-Allocate");
83                                 break;
84                         case 3:
85                                 LOG_INFO("outer: Write-Back, no Write-Allocate");
86                                 break;
87                 }
88                 switch (INNER) {
89                         case 0:
90                                 LOG_INFO("inner: Non-Cacheable");
91                                 break;
92                         case 1:
93                                 LOG_INFO("inner: Strongly-ordered");
94                                 break;
95                         case 3:
96                                 LOG_INFO("inner: Device");
97                                 break;
98                         case 5:
99                                 LOG_INFO("inner: Write-Back, Write-Allocate");
100                                 break;
101                         case 6:
102                                 LOG_INFO("inner:  Write-Through");
103                                 break;
104                         case 7:
105                                 LOG_INFO("inner: Write-Back, no Write-Allocate");
106                                 break;
107                         default:
108                                 LOG_INFO("inner: %" PRIx32 " ???", INNER);
109                 }
110         }
111
112 done:
113         dpm->finish(dpm);
114
115         return retval;
116 }
117
118 static const char *desc_bits_to_string(bool c_bit, bool b_bit, bool s_bit, bool ap2, int ap10, bool afe)
119 {
120         static char bits_string[64];
121         unsigned int len;
122
123         if (afe) {
124                 bool acc_r = true;
125                 bool acc_w = !ap2;
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 ");
130         } else {
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;
135
136                 switch (ap10) {
137                 case 0:
138                         priv_acc_r = priv_acc_w = false;
139                         unpriv_acc_r = unpriv_acc_w = false;
140                         break;
141                 case 1:
142                         unpriv_acc_r = unpriv_acc_w = false;
143                         break;
144                 case 2:
145                         unpriv_acc_w = false;
146                         break;
147                 default:
148                         break;
149                 }
150
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");
154         }
155
156         if (len >= sizeof(bits_string))
157                 bits_string[63] = 0;
158
159         return bits_string;
160 }
161
162 static const char *l2_desc_bits_to_string(uint32_t l2_desc, bool afe)
163 {
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;
169
170         return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
171 }
172
173 static const char *l1_desc_bits_to_string(uint32_t l1_desc, bool afe)
174 {
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;
180
181         return desc_bits_to_string(c_bit, b_bit, s_bit, ap2, ap10, afe);
182 }
183
184 COMMAND_HANDLER(armv7a_mmu_dump_table)
185 {
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;
192         target_addr_t ttb;
193         int ttbidx = 0;
194         int retval;
195         int pt_idx;
196         int max_pt_idx = 4095;
197         bool afe;
198
199         if (CMD_ARGC < 1)
200                 return ERROR_COMMAND_SYNTAX_ERROR;
201
202         if (!strcmp(CMD_ARGV[0], "addr")) {
203                 if (CMD_ARGC < 2)
204                         return ERROR_COMMAND_SYNTAX_ERROR;
205
206                 COMMAND_PARSE_NUMBER(target_addr, CMD_ARGV[1], ttb);
207
208                 if (CMD_ARGC > 2) {
209                         COMMAND_PARSE_NUMBER(int, CMD_ARGV[2], max_pt_idx);
210
211                         if (max_pt_idx < 1 || max_pt_idx > 4096)
212                                 return ERROR_COMMAND_ARGUMENT_INVALID;
213                         max_pt_idx -= 1;
214                 }
215         } else {
216                 if (mmu->cached != 1) {
217                         LOG_ERROR("TTB not cached!");
218                         return ERROR_FAIL;
219                 }
220
221                 COMMAND_PARSE_NUMBER(int, CMD_ARGV[0], ttbidx);
222                 if (ttbidx < 0 || ttbidx > 1)
223                         return ERROR_COMMAND_ARGUMENT_INVALID;
224
225                 ttb = mmu->ttbr[ttbidx] & mmu->ttbr_mask[ttbidx];
226
227                 if (ttbidx == 0) {
228                         int ttbcr_n = mmu->ttbcr & 0x7;
229                         max_pt_idx = 0x0fff >> ttbcr_n;
230                 }
231         }
232
233         LOG_USER("Page Directory at (phys): %8.8" TARGET_PRIxADDR, ttb);
234
235         first_lvl_ptbl = malloc(sizeof(uint32_t)*(max_pt_idx+1));
236         if (first_lvl_ptbl == NULL)
237                 return ERROR_FAIL;
238
239         /*
240          * this may or may not be necessary depending on whether
241          * the table walker is configured to use the cache or not.
242          */
243         cache->flush_all_data_cache(target);
244
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!");
248                 return retval;
249         }
250
251         afe = !!(cortex_a->cp15_control_reg & SCTLR_BIT_AFE);
252
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]);
256
257                 LOG_DEBUG("L1 desc[%8.8"PRIx32"]: %8.8"PRIx32, pt_idx << 20, first_lvl_descriptor);
258
259                 /* skip empty entries in the first level table */
260                 if ((first_lvl_descriptor & 3) == 0) {
261                         pt_idx++;
262                 } else
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;
268
269                         uint32_t pa_start = (first_lvl_descriptor & 0xfff00000);
270                         uint32_t pa_end = pa_start + va_range;
271
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));
274                         pt_idx++;
275                 } else
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;
281
282                         uint32_t pa_start = (first_lvl_descriptor & 0xff000000);
283                         uint32_t pa_end = pa_start + va_range;
284
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));
287
288                         /* skip next 15 entries, they're duplicating the first entry */
289                         pt_idx += 16;
290                 } else {
291                         target_addr_t second_lvl_ptbl = first_lvl_descriptor & 0xfffffc00;
292                         uint32_t second_lvl_descriptor;
293                         uint32_t *pt2;
294                         int pt2_idx;
295
296                         /* page table, always 1KB long */
297                         pt2 = malloc(1024);
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!");
302                                 return ERROR_FAIL;
303                         }
304
305                         for (pt2_idx = 0; pt2_idx < 256; ) {
306                                 second_lvl_descriptor = target_buffer_get_u32(target,
307                                                 (uint8_t *)&pt2[pt2_idx]);
308
309                                 if ((second_lvl_descriptor & 3) == 0) {
310                                         /* skip entry */
311                                         pt2_idx++;
312                                 } else
313                                 if ((second_lvl_descriptor & 3) == 1) {
314                                         /* large page */
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;
318
319                                         uint32_t pa_start = (second_lvl_descriptor & 0xffff0000);
320                                         uint32_t pa_end = pa_start + va_range;
321
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));
324
325                                         pt2_idx += 16;
326                                 } else {
327                                         /* small page */
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;
331
332                                         uint32_t pa_start = (second_lvl_descriptor & 0xfffff000);
333                                         uint32_t pa_end = pa_start + va_range;
334
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));
337
338                                         pt2_idx++;
339                                 }
340                         }
341                         free(pt2);
342                         pt_idx++;
343                 }
344         }
345
346         free(first_lvl_ptbl);
347         return ERROR_OK;
348 }
349
350 static const struct command_registration armv7a_mmu_group_handlers[] = {
351         {
352                 .name = "dump",
353                 .handler = armv7a_mmu_dump_table,
354                 .mode = COMMAND_ANY,
355                 .help = "dump translation table 0, 1 or from <address>",
356                 .usage = "(0|1|addr <address> [num_entries])",
357         },
358         COMMAND_REGISTRATION_DONE
359 };
360
361 const struct command_registration armv7a_mmu_command_handlers[] = {
362         {
363                 .name = "mmu",
364                 .mode = COMMAND_ANY,
365                 .help = "mmu command group",
366                 .usage = "",
367                 .chain = armv7a_mmu_group_handlers,
368         },
369         COMMAND_REGISTRATION_DONE
370 };