armv7a: correctly handle invalidation of inner data caches
[openocd.git] / src / target / armv7a_cache.c
1 /***************************************************************************
2 * Copyright (C) 2015 by Oleksij Rempel *
3 * linux@rempel-privat.de *
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
16 #ifdef HAVE_CONFIG_H
17 #include "config.h"
18 #endif
19
20 #include "jtag/interface.h"
21 #include "arm.h"
22 #include "armv7a.h"
23 #include "armv7a_cache.h"
24 #include <helper/time_support.h>
25 #include "arm_opcodes.h"
26
27 static int armv7a_l1_d_cache_sanity_check(struct target *target)
28 {
29 struct armv7a_common *armv7a = target_to_armv7a(target);
30
31 if (target->state != TARGET_HALTED) {
32 LOG_ERROR("%s: target not halted", __func__);
33 return ERROR_TARGET_NOT_HALTED;
34 }
35
36 /* check that cache data is on at target halt */
37 if (!armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled) {
38 LOG_DEBUG("data cache is not enabled");
39 return ERROR_TARGET_INVALID;
40 }
41
42 return ERROR_OK;
43 }
44
45 static int armv7a_l1_i_cache_sanity_check(struct target *target)
46 {
47 struct armv7a_common *armv7a = target_to_armv7a(target);
48
49 if (target->state != TARGET_HALTED) {
50 LOG_ERROR("%s: target not halted", __func__);
51 return ERROR_TARGET_NOT_HALTED;
52 }
53
54 /* check that cache data is on at target halt */
55 if (!armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled) {
56 LOG_DEBUG("instruction cache is not enabled");
57 return ERROR_TARGET_INVALID;
58 }
59
60 return ERROR_OK;
61 }
62
63 static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cachesize *size, int cl)
64 {
65 int retval = ERROR_OK;
66 int32_t c_way, c_index = size->index;
67
68 LOG_DEBUG("cl %" PRId32, cl);
69 do {
70 c_way = size->way;
71 do {
72 uint32_t value = (c_index << size->index_shift)
73 | (c_way << size->way_shift) | (cl << 1);
74 /*
75 * DCCISW - Clean and invalidate data cache
76 * line by Set/Way.
77 */
78 retval = dpm->instr_write_data_r0(dpm,
79 ARMV4_5_MCR(15, 0, 0, 7, 14, 2),
80 value);
81 if (retval != ERROR_OK)
82 goto done;
83 c_way -= 1;
84 } while (c_way >= 0);
85 c_index -= 1;
86 } while (c_index >= 0);
87
88 done:
89 return retval;
90 }
91
92 static int armv7a_l1_d_cache_clean_inval_all(struct target *target)
93 {
94 struct armv7a_common *armv7a = target_to_armv7a(target);
95 struct armv7a_cache_common *cache = &(armv7a->armv7a_mmu.armv7a_cache);
96 struct arm_dpm *dpm = armv7a->arm.dpm;
97 int cl;
98 int retval;
99
100 retval = armv7a_l1_d_cache_sanity_check(target);
101 if (retval != ERROR_OK)
102 return retval;
103
104 retval = dpm->prepare(dpm);
105 if (retval != ERROR_OK)
106 goto done;
107
108 for (cl = 0; cl < cache->loc; cl++) {
109 /* skip i-only caches */
110 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
111 continue;
112
113 armv7a_l1_d_cache_flush_level(dpm, &cache->arch[cl].d_u_size, cl);
114 }
115
116 retval = dpm->finish(dpm);
117 return retval;
118
119 done:
120 LOG_ERROR("clean invalidate failed");
121 dpm->finish(dpm);
122
123 return retval;
124 }
125
126 int armv7a_cache_auto_flush_all_data(struct target *target)
127 {
128 int retval = ERROR_FAIL;
129 struct armv7a_common *armv7a = target_to_armv7a(target);
130
131 if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
132 return ERROR_OK;
133
134 if (target->smp) {
135 struct target_list *head;
136 struct target *curr;
137 head = target->head;
138 while (head != (struct target_list *)NULL) {
139 curr = head->target;
140 if (curr->state == TARGET_HALTED)
141 retval = armv7a_l1_d_cache_clean_inval_all(curr);
142
143 head = head->next;
144 }
145 } else
146 retval = armv7a_l1_d_cache_clean_inval_all(target);
147
148 /* do outer cache flushing after inner caches have been flushed */
149 retval = arm7a_l2x_flush_all_data(target);
150
151 return retval;
152 }
153
154
155 static int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
156 uint32_t size)
157 {
158 struct armv7a_common *armv7a = target_to_armv7a(target);
159 struct arm_dpm *dpm = armv7a->arm.dpm;
160 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
161 uint32_t linelen = armv7a_cache->dminline;
162 uint32_t va_line, va_end;
163 int retval;
164
165 retval = armv7a_l1_d_cache_sanity_check(target);
166 if (retval != ERROR_OK)
167 return retval;
168
169 retval = dpm->prepare(dpm);
170 if (retval != ERROR_OK)
171 goto done;
172
173 va_line = virt & (-linelen);
174 va_end = virt + size;
175
176 /* handle unaligned start */
177 if (virt != va_line) {
178 /* DCCIMVAC */
179 retval = dpm->instr_write_data_r0(dpm,
180 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
181 if (retval != ERROR_OK)
182 goto done;
183 va_line += linelen;
184 }
185
186 /* handle unaligned end */
187 if ((va_end & (linelen-1)) != 0) {
188 va_end &= (-linelen);
189 /* DCCIMVAC */
190 retval = dpm->instr_write_data_r0(dpm,
191 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_end);
192 if (retval != ERROR_OK)
193 goto done;
194 }
195
196 while (va_line < va_end) {
197 /* DCIMVAC - Invalidate data cache line by VA to PoC. */
198 retval = dpm->instr_write_data_r0(dpm,
199 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
200 if (retval != ERROR_OK)
201 goto done;
202 va_line += linelen;
203 }
204
205 dpm->finish(dpm);
206 return retval;
207
208 done:
209 LOG_ERROR("d-cache invalidate failed");
210 dpm->finish(dpm);
211
212 return retval;
213 }
214
215 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
216 unsigned int size)
217 {
218 struct armv7a_common *armv7a = target_to_armv7a(target);
219 struct arm_dpm *dpm = armv7a->arm.dpm;
220 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
221 uint32_t i, linelen = armv7a_cache->dminline;
222 int retval;
223
224 retval = armv7a_l1_d_cache_sanity_check(target);
225 if (retval != ERROR_OK)
226 return retval;
227
228 retval = dpm->prepare(dpm);
229 if (retval != ERROR_OK)
230 goto done;
231
232 for (i = 0; i < size; i += linelen) {
233 uint32_t offs = virt + i;
234
235 /* DCCMVAC - Data Cache Clean by MVA to PoC */
236 retval = dpm->instr_write_data_r0(dpm,
237 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), offs);
238 if (retval != ERROR_OK)
239 goto done;
240 }
241 return retval;
242
243 done:
244 LOG_ERROR("d-cache invalidate failed");
245 dpm->finish(dpm);
246
247 return retval;
248 }
249
250 int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
251 unsigned int size)
252 {
253 struct armv7a_common *armv7a = target_to_armv7a(target);
254 struct arm_dpm *dpm = armv7a->arm.dpm;
255 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
256 uint32_t i, linelen = armv7a_cache->dminline;
257 int retval;
258
259 retval = armv7a_l1_d_cache_sanity_check(target);
260 if (retval != ERROR_OK)
261 return retval;
262
263 retval = dpm->prepare(dpm);
264 if (retval != ERROR_OK)
265 goto done;
266
267 for (i = 0; i < size; i += linelen) {
268 uint32_t offs = virt + i;
269
270 /* DCCIMVAC */
271 retval = dpm->instr_write_data_r0(dpm,
272 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), offs);
273 if (retval != ERROR_OK)
274 goto done;
275 }
276 return retval;
277
278 done:
279 LOG_ERROR("d-cache invalidate failed");
280 dpm->finish(dpm);
281
282 return retval;
283 }
284
285 int armv7a_l1_i_cache_inval_all(struct target *target)
286 {
287 struct armv7a_common *armv7a = target_to_armv7a(target);
288 struct arm_dpm *dpm = armv7a->arm.dpm;
289 int retval;
290
291 retval = armv7a_l1_i_cache_sanity_check(target);
292 if (retval != ERROR_OK)
293 return retval;
294
295 retval = dpm->prepare(dpm);
296 if (retval != ERROR_OK)
297 goto done;
298
299 if (target->smp) {
300 /* ICIALLUIS */
301 retval = dpm->instr_write_data_r0(dpm,
302 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
303 } else {
304 /* ICIALLU */
305 retval = dpm->instr_write_data_r0(dpm,
306 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
307 }
308
309 if (retval != ERROR_OK)
310 goto done;
311
312 dpm->finish(dpm);
313 return retval;
314
315 done:
316 LOG_ERROR("i-cache invalidate failed");
317 dpm->finish(dpm);
318
319 return retval;
320 }
321
322 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
323 uint32_t size)
324 {
325 struct armv7a_common *armv7a = target_to_armv7a(target);
326 struct arm_dpm *dpm = armv7a->arm.dpm;
327 struct armv7a_cache_common *armv7a_cache =
328 &armv7a->armv7a_mmu.armv7a_cache;
329 uint32_t linelen = armv7a_cache->iminline;
330 uint32_t va_line, va_end;
331 int retval;
332
333 retval = armv7a_l1_i_cache_sanity_check(target);
334 if (retval != ERROR_OK)
335 return retval;
336
337 retval = dpm->prepare(dpm);
338 if (retval != ERROR_OK)
339 goto done;
340
341 va_line = virt & (-linelen);
342 va_end = virt + size;
343
344 while (va_line < va_end) {
345 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
346 retval = dpm->instr_write_data_r0(dpm,
347 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
348 if (retval != ERROR_OK)
349 goto done;
350 /* BPIMVA */
351 retval = dpm->instr_write_data_r0(dpm,
352 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
353 if (retval != ERROR_OK)
354 goto done;
355 va_line += linelen;
356 }
357 return retval;
358
359 done:
360 LOG_ERROR("i-cache invalidate failed");
361 dpm->finish(dpm);
362
363 return retval;
364 }
365
366 int armv7a_cache_flush_virt(struct target *target, uint32_t virt,
367 uint32_t size)
368 {
369 armv7a_l1_d_cache_flush_virt(target, virt, size);
370 armv7a_l2x_cache_flush_virt(target, virt, size);
371
372 return ERROR_OK;
373 }
374
375 /*
376 * We assume that target core was chosen correctly. It means if same data
377 * was handled by two cores, other core will loose the changes. Since it
378 * is impossible to know (FIXME) which core has correct data, keep in mind
379 * that some kind of data lost or korruption is possible.
380 * Possible scenario:
381 * - core1 loaded and changed data on 0x12345678
382 * - we halted target and modified same data on core0
383 * - data on core1 will be lost.
384 */
385 int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
386 uint32_t size)
387 {
388 struct armv7a_common *armv7a = target_to_armv7a(target);
389
390 if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
391 return ERROR_OK;
392
393 return armv7a_cache_flush_virt(target, virt, size);
394 }
395
396 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
397 {
398 struct target *target = get_current_target(CMD_CTX);
399 struct armv7a_common *armv7a = target_to_armv7a(target);
400
401 return armv7a_handle_cache_info_command(CMD_CTX,
402 &armv7a->armv7a_mmu.armv7a_cache);
403 }
404
405 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
406 {
407 struct target *target = get_current_target(CMD_CTX);
408
409 armv7a_l1_d_cache_clean_inval_all(target);
410
411 return 0;
412 }
413
414 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
415 {
416 struct target *target = get_current_target(CMD_CTX);
417 uint32_t virt, size;
418
419 if (CMD_ARGC == 0 || CMD_ARGC > 2)
420 return ERROR_COMMAND_SYNTAX_ERROR;
421
422 if (CMD_ARGC == 2)
423 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
424 else
425 size = 1;
426
427 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
428
429 return armv7a_l1_d_cache_inval_virt(target, virt, size);
430 }
431
432 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
433 {
434 struct target *target = get_current_target(CMD_CTX);
435 uint32_t virt, size;
436
437 if (CMD_ARGC == 0 || CMD_ARGC > 2)
438 return ERROR_COMMAND_SYNTAX_ERROR;
439
440 if (CMD_ARGC == 2)
441 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
442 else
443 size = 1;
444
445 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
446
447 return armv7a_l1_d_cache_clean_virt(target, virt, size);
448 }
449
450 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
451 {
452 struct target *target = get_current_target(CMD_CTX);
453
454 armv7a_l1_i_cache_inval_all(target);
455
456 return 0;
457 }
458
459 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
460 {
461 struct target *target = get_current_target(CMD_CTX);
462 uint32_t virt, size;
463
464 if (CMD_ARGC == 0 || CMD_ARGC > 2)
465 return ERROR_COMMAND_SYNTAX_ERROR;
466
467 if (CMD_ARGC == 2)
468 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
469 else
470 size = 1;
471
472 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
473
474 return armv7a_l1_i_cache_inval_virt(target, virt, size);
475 }
476
477 COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
478 {
479 struct target *target = get_current_target(CMD_CTX);
480 struct armv7a_common *armv7a = target_to_armv7a(target);
481
482 if (CMD_ARGC == 0) {
483 command_print(CMD_CTX, "auto cache is %s",
484 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
485 return ERROR_OK;
486 }
487
488 if (CMD_ARGC == 1) {
489 uint32_t set;
490
491 COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
492 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
493 return ERROR_OK;
494 }
495
496 return ERROR_COMMAND_SYNTAX_ERROR;
497 }
498
499 static const struct command_registration arm7a_l1_d_cache_commands[] = {
500 {
501 .name = "flush_all",
502 .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
503 .mode = COMMAND_ANY,
504 .help = "flush (clean and invalidate) complete l1 d-cache",
505 .usage = "",
506 },
507 {
508 .name = "inval",
509 .handler = arm7a_l1_d_cache_inval_virt_cmd,
510 .mode = COMMAND_ANY,
511 .help = "invalidate l1 d-cache by virtual address offset and range size",
512 .usage = "<virt_addr> [size]",
513 },
514 {
515 .name = "clean",
516 .handler = arm7a_l1_d_cache_clean_virt_cmd,
517 .mode = COMMAND_ANY,
518 .help = "clean l1 d-cache by virtual address address offset and range size",
519 .usage = "<virt_addr> [size]",
520 },
521 COMMAND_REGISTRATION_DONE
522 };
523
524 static const struct command_registration arm7a_l1_i_cache_commands[] = {
525 {
526 .name = "inval_all",
527 .handler = armv7a_i_cache_clean_inval_all_cmd,
528 .mode = COMMAND_ANY,
529 .help = "invalidate complete l1 i-cache",
530 .usage = "",
531 },
532 {
533 .name = "inval",
534 .handler = arm7a_l1_i_cache_inval_virt_cmd,
535 .mode = COMMAND_ANY,
536 .help = "invalidate l1 i-cache by virtual address offset and range size",
537 .usage = "<virt_addr> [size]",
538 },
539 COMMAND_REGISTRATION_DONE
540 };
541
542 const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
543 {
544 .name = "info",
545 .handler = arm7a_l1_cache_info_cmd,
546 .mode = COMMAND_ANY,
547 .help = "print cache realted information",
548 .usage = "",
549 },
550 {
551 .name = "d",
552 .mode = COMMAND_ANY,
553 .help = "l1 d-cache command group",
554 .usage = "",
555 .chain = arm7a_l1_d_cache_commands,
556 },
557 {
558 .name = "i",
559 .mode = COMMAND_ANY,
560 .help = "l1 i-cache command group",
561 .usage = "",
562 .chain = arm7a_l1_i_cache_commands,
563 },
564 COMMAND_REGISTRATION_DONE
565 };
566
567 const struct command_registration arm7a_cache_group_handlers[] = {
568 {
569 .name = "auto",
570 .handler = arm7a_cache_disable_auto_cmd,
571 .mode = COMMAND_ANY,
572 .help = "disable or enable automatic cache handling.",
573 .usage = "(1|0)",
574 },
575 {
576 .name = "l1",
577 .mode = COMMAND_ANY,
578 .help = "l1 cache command group",
579 .usage = "",
580 .chain = arm7a_l1_di_cache_group_handlers,
581 },
582 {
583 .chain = arm7a_l2x_cache_command_handler,
584 },
585 COMMAND_REGISTRATION_DONE
586 };
587
588 const struct command_registration arm7a_cache_command_handlers[] = {
589 {
590 .name = "cache",
591 .mode = COMMAND_ANY,
592 .help = "cache command group",
593 .usage = "",
594 .chain = arm7a_cache_group_handlers,
595 },
596 COMMAND_REGISTRATION_DONE
597 };

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)