31e4c79600ce412b39ddddd68bb5db7ff121ad50
[openocd.git] / src / target / armv8_cache.c
1 /***************************************************************************
2 * Copyright (C) 2016 by Matthias Welwarsky *
3 * matthias.welwarsky@sysgo.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "armv8_cache.h"
24 #include "armv8_dpm.h"
25 #include "armv8_opcodes.h"
26
27 /* CLIDR cache types */
28 #define CACHE_LEVEL_HAS_UNIFIED_CACHE 0x4
29 #define CACHE_LEVEL_HAS_D_CACHE 0x2
30 #define CACHE_LEVEL_HAS_I_CACHE 0x1
31
32 static int armv8_d_cache_sanity_check(struct armv8_common *armv8)
33 {
34 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
35
36 if (armv8_cache->d_u_cache_enabled)
37 return ERROR_OK;
38
39 return ERROR_TARGET_INVALID;
40 }
41
42 static int armv8_i_cache_sanity_check(struct armv8_common *armv8)
43 {
44 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
45
46 if (armv8_cache->i_cache_enabled)
47 return ERROR_OK;
48
49 return ERROR_TARGET_INVALID;
50 }
51
52 static int armv8_cache_d_inner_flush_level(struct arm_dpm *dpm, struct armv8_cachesize *size, int cl)
53 {
54 int retval = ERROR_OK;
55 int32_t c_way, c_index = size->index;
56
57 LOG_DEBUG("cl %" PRId32, cl);
58 do {
59 c_way = size->way;
60 do {
61 uint32_t value = (c_index << size->index_shift)
62 | (c_way << size->way_shift) | (cl << 1);
63 /*
64 * DC CISW - Clean and invalidate data cache
65 * line by Set/Way.
66 */
67 retval = dpm->instr_write_data_r0(dpm,
68 ARMV8_SYS(SYSTEM_DCCISW, 0), value);
69 if (retval != ERROR_OK)
70 goto done;
71 c_way -= 1;
72 } while (c_way >= 0);
73 c_index -= 1;
74 } while (c_index >= 0);
75
76 done:
77 return retval;
78 }
79
80 static int armv8_cache_d_inner_clean_inval_all(struct armv8_common *armv8)
81 {
82 struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
83 struct arm_dpm *dpm = armv8->arm.dpm;
84 int cl;
85 int retval;
86
87 retval = armv8_d_cache_sanity_check(armv8);
88 if (retval != ERROR_OK)
89 return retval;
90
91 retval = dpm->prepare(dpm);
92 if (retval != ERROR_OK)
93 goto done;
94
95 for (cl = 0; cl < cache->loc; cl++) {
96 /* skip i-only caches */
97 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
98 continue;
99
100 armv8_cache_d_inner_flush_level(dpm, &cache->arch[cl].d_u_size, cl);
101 }
102
103 retval = dpm->finish(dpm);
104 return retval;
105
106 done:
107 LOG_ERROR("clean invalidate failed");
108 dpm->finish(dpm);
109
110 return retval;
111 }
112
113 int armv8_cache_d_inner_flush_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
114 {
115 struct arm_dpm *dpm = armv8->arm.dpm;
116 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
117 uint64_t linelen = armv8_cache->dminline;
118 target_addr_t va_line, va_end;
119 int retval;
120
121 retval = armv8_d_cache_sanity_check(armv8);
122 if (retval != ERROR_OK)
123 return retval;
124
125 retval = dpm->prepare(dpm);
126 if (retval != ERROR_OK)
127 goto done;
128
129 va_line = va & (-linelen);
130 va_end = va + size;
131
132 while (va_line < va_end) {
133 /* DC CIVAC */
134 /* Aarch32: DCCIMVAC: ARMV4_5_MCR(15, 0, 0, 7, 14, 1) */
135 retval = dpm->instr_write_data_r0_64(dpm,
136 ARMV8_SYS(SYSTEM_DCCIVAC, 0), va_line);
137 if (retval != ERROR_OK)
138 goto done;
139 va_line += linelen;
140 }
141
142 dpm->finish(dpm);
143 return retval;
144
145 done:
146 LOG_ERROR("d-cache invalidate failed");
147 dpm->finish(dpm);
148
149 return retval;
150 }
151
152 int armv8_cache_i_inner_inval_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
153 {
154 struct arm_dpm *dpm = armv8->arm.dpm;
155 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
156 uint64_t linelen = armv8_cache->iminline;
157 target_addr_t va_line, va_end;
158 int retval;
159
160 retval = armv8_i_cache_sanity_check(armv8);
161 if (retval != ERROR_OK)
162 return retval;
163
164 retval = dpm->prepare(dpm);
165 if (retval != ERROR_OK)
166 goto done;
167
168 va_line = va & (-linelen);
169 va_end = va + size;
170
171 while (va_line < va_end) {
172 /* IC IVAU - Invalidate instruction cache by VA to PoU. */
173 retval = dpm->instr_write_data_r0_64(dpm,
174 ARMV8_SYS(SYSTEM_ICIVAU, 0), va_line);
175 if (retval != ERROR_OK)
176 goto done;
177 va_line += linelen;
178 }
179
180 dpm->finish(dpm);
181 return retval;
182
183 done:
184 LOG_ERROR("d-cache invalidate failed");
185 dpm->finish(dpm);
186
187 return retval;
188 }
189
190 static int armv8_handle_inner_cache_info_command(struct command_context *cmd_ctx,
191 struct armv8_cache_common *armv8_cache)
192 {
193 int cl;
194
195 if (armv8_cache->info == -1) {
196 command_print(cmd_ctx, "cache not yet identified");
197 return ERROR_OK;
198 }
199
200 for (cl = 0; cl < armv8_cache->loc; cl++) {
201 struct armv8_arch_cache *arch = &(armv8_cache->arch[cl]);
202
203 if (arch->ctype & 1) {
204 command_print(cmd_ctx,
205 "L%d I-Cache: linelen %" PRIi32
206 ", associativity %" PRIi32
207 ", nsets %" PRIi32
208 ", cachesize %" PRId32 " KBytes",
209 cl+1,
210 arch->i_size.linelen,
211 arch->i_size.associativity,
212 arch->i_size.nsets,
213 arch->i_size.cachesize);
214 }
215
216 if (arch->ctype >= 2) {
217 command_print(cmd_ctx,
218 "L%d D-Cache: linelen %" PRIi32
219 ", associativity %" PRIi32
220 ", nsets %" PRIi32
221 ", cachesize %" PRId32 " KBytes",
222 cl+1,
223 arch->d_u_size.linelen,
224 arch->d_u_size.associativity,
225 arch->d_u_size.nsets,
226 arch->d_u_size.cachesize);
227 }
228 }
229
230 return ERROR_OK;
231 }
232
233 static int _armv8_flush_all_data(struct target *target)
234 {
235 return armv8_cache_d_inner_clean_inval_all(target_to_armv8(target));
236 }
237
238 static int armv8_flush_all_data(struct target *target)
239 {
240 int retval = ERROR_FAIL;
241 /* check that armv8_cache is correctly identify */
242 struct armv8_common *armv8 = target_to_armv8(target);
243 if (armv8->armv8_mmu.armv8_cache.info == -1) {
244 LOG_ERROR("trying to flush un-identified cache");
245 return retval;
246 }
247
248 if (target->smp) {
249 /* look if all the other target have been flushed in order to flush level
250 * 2 */
251 struct target_list *head;
252 struct target *curr;
253 head = target->head;
254 while (head != (struct target_list *)NULL) {
255 curr = head->target;
256 if (curr->state == TARGET_HALTED) {
257 LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid);
258 retval = _armv8_flush_all_data(curr);
259 }
260 head = head->next;
261 }
262 } else
263 retval = _armv8_flush_all_data(target);
264 return retval;
265 }
266
267 static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg)
268 {
269 struct armv8_common *armv8 = dpm->arm->arch_info;
270 int retval = ERROR_OK;
271
272 /* select cache level */
273 retval = dpm->instr_write_data_r0(dpm,
274 armv8_opcode(armv8, WRITE_REG_CSSELR),
275 (cl << 1) | (ct == 1 ? 1 : 0));
276 if (retval != ERROR_OK)
277 goto done;
278
279 retval = dpm->instr_read_data_r0(dpm,
280 armv8_opcode(armv8, READ_REG_CCSIDR),
281 cache_reg);
282 done:
283 return retval;
284 }
285
286 static struct armv8_cachesize decode_cache_reg(uint32_t cache_reg)
287 {
288 struct armv8_cachesize size;
289 int i = 0;
290
291 size.linelen = 16 << (cache_reg & 0x7);
292 size.associativity = ((cache_reg >> 3) & 0x3ff) + 1;
293 size.nsets = ((cache_reg >> 13) & 0x7fff) + 1;
294 size.cachesize = size.linelen * size.associativity * size.nsets / 1024;
295
296 /* compute info for set way operation on cache */
297 size.index_shift = (cache_reg & 0x7) + 4;
298 size.index = (cache_reg >> 13) & 0x7fff;
299 size.way = ((cache_reg >> 3) & 0x3ff);
300
301 while (((size.way << i) & 0x80000000) == 0)
302 i++;
303 size.way_shift = i;
304
305 return size;
306 }
307
308 int armv8_identify_cache(struct armv8_common *armv8)
309 {
310 /* read cache descriptor */
311 int retval = ERROR_FAIL;
312 struct arm_dpm *dpm = armv8->arm.dpm;
313 uint32_t csselr, clidr, ctr;
314 uint32_t cache_reg;
315 int cl, ctype;
316 struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
317
318 retval = dpm->prepare(dpm);
319 if (retval != ERROR_OK)
320 goto done;
321
322 /* retrieve CTR */
323 retval = dpm->instr_read_data_r0(dpm,
324 armv8_opcode(armv8, READ_REG_CTR), &ctr);
325 if (retval != ERROR_OK)
326 goto done;
327
328 cache->iminline = 4UL << (ctr & 0xf);
329 cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
330 LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
331 ctr, cache->iminline, cache->dminline);
332
333 /* retrieve CLIDR */
334 retval = dpm->instr_read_data_r0(dpm,
335 armv8_opcode(armv8, READ_REG_CLIDR), &clidr);
336 if (retval != ERROR_OK)
337 goto done;
338
339 cache->loc = (clidr & 0x7000000) >> 24;
340 LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
341
342 /* retrieve selected cache for later restore
343 * MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
344 retval = dpm->instr_read_data_r0(dpm,
345 armv8_opcode(armv8, READ_REG_CSSELR), &csselr);
346 if (retval != ERROR_OK)
347 goto done;
348
349 /* retrieve all available inner caches */
350 for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
351
352 /* isolate cache type at current level */
353 ctype = clidr & 7;
354
355 /* skip reserved values */
356 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
357 continue;
358
359 /* separate d or unified d/i cache at this level ? */
360 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
361 /* retrieve d-cache info */
362 retval = get_cache_info(dpm, cl, 0, &cache_reg);
363 if (retval != ERROR_OK)
364 goto done;
365 cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
366
367 LOG_DEBUG("data/unified cache index %d << %d, way %d << %d",
368 cache->arch[cl].d_u_size.index,
369 cache->arch[cl].d_u_size.index_shift,
370 cache->arch[cl].d_u_size.way,
371 cache->arch[cl].d_u_size.way_shift);
372
373 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
374 cache->arch[cl].d_u_size.linelen,
375 cache->arch[cl].d_u_size.cachesize,
376 cache->arch[cl].d_u_size.associativity);
377 }
378
379 /* separate i-cache at this level ? */
380 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
381 /* retrieve i-cache info */
382 retval = get_cache_info(dpm, cl, 1, &cache_reg);
383 if (retval != ERROR_OK)
384 goto done;
385 cache->arch[cl].i_size = decode_cache_reg(cache_reg);
386
387 LOG_DEBUG("instruction cache index %d << %d, way %d << %d",
388 cache->arch[cl].i_size.index,
389 cache->arch[cl].i_size.index_shift,
390 cache->arch[cl].i_size.way,
391 cache->arch[cl].i_size.way_shift);
392
393 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
394 cache->arch[cl].i_size.linelen,
395 cache->arch[cl].i_size.cachesize,
396 cache->arch[cl].i_size.associativity);
397 }
398
399 cache->arch[cl].ctype = ctype;
400 }
401
402 /* restore selected cache */
403 dpm->instr_write_data_r0(dpm,
404 armv8_opcode(armv8, WRITE_REG_CSSELR), csselr);
405 if (retval != ERROR_OK)
406 goto done;
407
408 armv8->armv8_mmu.armv8_cache.info = 1;
409
410 /* if no l2 cache initialize l1 data cache flush function function */
411 if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache == NULL) {
412 armv8->armv8_mmu.armv8_cache.display_cache_info =
413 armv8_handle_inner_cache_info_command;
414 armv8->armv8_mmu.armv8_cache.flush_all_data_cache =
415 armv8_flush_all_data;
416 }
417
418 done:
419 dpm->finish(dpm);
420 return retval;
421
422 }

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)