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

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)