armv7a: add d-cache virtual address range flush function
[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("l1 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("l1 data 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 i, linelen = armv7a_cache->dminline;
162 int retval;
163
164 retval = armv7a_l1_d_cache_sanity_check(target);
165 if (retval != ERROR_OK)
166 return retval;
167
168 retval = dpm->prepare(dpm);
169 if (retval != ERROR_OK)
170 goto done;
171
172 for (i = 0; i < size; i += linelen) {
173 uint32_t offs = virt + i;
174
175 /* DCIMVAC - Clean and invalidate data cache line by VA to PoC. */
176 retval = dpm->instr_write_data_r0(dpm,
177 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), offs);
178 if (retval != ERROR_OK)
179 goto done;
180 }
181 return retval;
182
183 done:
184 LOG_ERROR("d-cache invalidate failed");
185 dpm->finish(dpm);
186
187 return retval;
188 }
189
190 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
191 unsigned int size)
192 {
193 struct armv7a_common *armv7a = target_to_armv7a(target);
194 struct arm_dpm *dpm = armv7a->arm.dpm;
195 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
196 uint32_t i, linelen = armv7a_cache->dminline;
197 int retval;
198
199 retval = armv7a_l1_d_cache_sanity_check(target);
200 if (retval != ERROR_OK)
201 return retval;
202
203 retval = dpm->prepare(dpm);
204 if (retval != ERROR_OK)
205 goto done;
206
207 for (i = 0; i < size; i += linelen) {
208 uint32_t offs = virt + i;
209
210 /* FIXME: do we need DCCVAC or DCCVAU */
211 /* FIXME: in both cases it is not enough for i-cache */
212 retval = dpm->instr_write_data_r0(dpm,
213 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), offs);
214 if (retval != ERROR_OK)
215 goto done;
216 }
217 return retval;
218
219 done:
220 LOG_ERROR("d-cache invalidate failed");
221 dpm->finish(dpm);
222
223 return retval;
224 }
225
226 int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
227 unsigned int size)
228 {
229 struct armv7a_common *armv7a = target_to_armv7a(target);
230 struct arm_dpm *dpm = armv7a->arm.dpm;
231 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
232 uint32_t i, linelen = armv7a_cache->dminline;
233 int retval;
234
235 retval = armv7a_l1_d_cache_sanity_check(target);
236 if (retval != ERROR_OK)
237 return retval;
238
239 retval = dpm->prepare(dpm);
240 if (retval != ERROR_OK)
241 goto done;
242
243 for (i = 0; i < size; i += linelen) {
244 uint32_t offs = virt + i;
245
246 /* DCCIMVAC */
247 retval = dpm->instr_write_data_r0(dpm,
248 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), offs);
249 if (retval != ERROR_OK)
250 goto done;
251 }
252 return retval;
253
254 done:
255 LOG_ERROR("d-cache invalidate failed");
256 dpm->finish(dpm);
257
258 return retval;
259 }
260
261 int armv7a_l1_i_cache_inval_all(struct target *target)
262 {
263 struct armv7a_common *armv7a = target_to_armv7a(target);
264 struct arm_dpm *dpm = armv7a->arm.dpm;
265 int retval;
266
267 retval = armv7a_l1_i_cache_sanity_check(target);
268 if (retval != ERROR_OK)
269 return retval;
270
271 retval = dpm->prepare(dpm);
272 if (retval != ERROR_OK)
273 goto done;
274
275 if (target->smp) {
276 /* ICIALLUIS */
277 retval = dpm->instr_write_data_r0(dpm,
278 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
279 } else {
280 /* ICIALLU */
281 retval = dpm->instr_write_data_r0(dpm,
282 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
283 }
284
285 if (retval != ERROR_OK)
286 goto done;
287
288 dpm->finish(dpm);
289 return retval;
290
291 done:
292 LOG_ERROR("i-cache invalidate failed");
293 dpm->finish(dpm);
294
295 return retval;
296 }
297
298 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
299 uint32_t size)
300 {
301 struct armv7a_common *armv7a = target_to_armv7a(target);
302 struct arm_dpm *dpm = armv7a->arm.dpm;
303 struct armv7a_cache_common *armv7a_cache =
304 &armv7a->armv7a_mmu.armv7a_cache;
305 uint32_t linelen = armv7a_cache->iminline;
306 uint32_t va_line, va_end;
307 int retval;
308
309 retval = armv7a_l1_i_cache_sanity_check(target);
310 if (retval != ERROR_OK)
311 return retval;
312
313 retval = dpm->prepare(dpm);
314 if (retval != ERROR_OK)
315 goto done;
316
317 va_line = virt & (-linelen);
318 va_end = virt + size;
319
320 while (va_line < va_end) {
321 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
322 retval = dpm->instr_write_data_r0(dpm,
323 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
324 if (retval != ERROR_OK)
325 goto done;
326 /* BPIMVA */
327 retval = dpm->instr_write_data_r0(dpm,
328 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
329 if (retval != ERROR_OK)
330 goto done;
331 va_line += linelen;
332 }
333 return retval;
334
335 done:
336 LOG_ERROR("i-cache invalidate failed");
337 dpm->finish(dpm);
338
339 return retval;
340 }
341
342
343 /*
344 * We assume that target core was chosen correctly. It means if same data
345 * was handled by two cores, other core will loose the changes. Since it
346 * is impossible to know (FIXME) which core has correct data, keep in mind
347 * that some kind of data lost or korruption is possible.
348 * Possible scenario:
349 * - core1 loaded and changed data on 0x12345678
350 * - we halted target and modified same data on core0
351 * - data on core1 will be lost.
352 */
353 int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
354 uint32_t size)
355 {
356 struct armv7a_common *armv7a = target_to_armv7a(target);
357 int retval = ERROR_OK;
358
359 if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
360 return ERROR_OK;
361
362 armv7a_l1_d_cache_clean_virt(target, virt, size);
363 armv7a_l2x_cache_flush_virt(target, virt, size);
364
365 if (target->smp) {
366 struct target_list *head;
367 struct target *curr;
368 head = target->head;
369 while (head != (struct target_list *)NULL) {
370 curr = head->target;
371 if (curr->state == TARGET_HALTED) {
372 retval = armv7a_l1_i_cache_inval_all(curr);
373 if (retval != ERROR_OK)
374 return retval;
375 retval = armv7a_l1_d_cache_inval_virt(target,
376 virt, size);
377 if (retval != ERROR_OK)
378 return retval;
379 }
380 head = head->next;
381 }
382 } else {
383 retval = armv7a_l1_i_cache_inval_all(target);
384 if (retval != ERROR_OK)
385 return retval;
386 retval = armv7a_l1_d_cache_inval_virt(target, virt, size);
387 if (retval != ERROR_OK)
388 return retval;
389 }
390
391 return retval;
392 }
393
394 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
395 {
396 struct target *target = get_current_target(CMD_CTX);
397 struct armv7a_common *armv7a = target_to_armv7a(target);
398
399 return armv7a_handle_cache_info_command(CMD_CTX,
400 &armv7a->armv7a_mmu.armv7a_cache);
401 }
402
403 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
404 {
405 struct target *target = get_current_target(CMD_CTX);
406
407 armv7a_l1_d_cache_clean_inval_all(target);
408
409 return 0;
410 }
411
412 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
413 {
414 struct target *target = get_current_target(CMD_CTX);
415 uint32_t virt, size;
416
417 if (CMD_ARGC == 0 || CMD_ARGC > 2)
418 return ERROR_COMMAND_SYNTAX_ERROR;
419
420 if (CMD_ARGC == 2)
421 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
422 else
423 size = 1;
424
425 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
426
427 return armv7a_l1_d_cache_inval_virt(target, virt, size);
428 }
429
430 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
431 {
432 struct target *target = get_current_target(CMD_CTX);
433 uint32_t virt, size;
434
435 if (CMD_ARGC == 0 || CMD_ARGC > 2)
436 return ERROR_COMMAND_SYNTAX_ERROR;
437
438 if (CMD_ARGC == 2)
439 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
440 else
441 size = 1;
442
443 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
444
445 return armv7a_l1_d_cache_clean_virt(target, virt, size);
446 }
447
448 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
449 {
450 struct target *target = get_current_target(CMD_CTX);
451
452 armv7a_l1_i_cache_inval_all(target);
453
454 return 0;
455 }
456
457 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
458 {
459 struct target *target = get_current_target(CMD_CTX);
460 uint32_t virt, size;
461
462 if (CMD_ARGC == 0 || CMD_ARGC > 2)
463 return ERROR_COMMAND_SYNTAX_ERROR;
464
465 if (CMD_ARGC == 2)
466 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
467 else
468 size = 1;
469
470 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
471
472 return armv7a_l1_i_cache_inval_virt(target, virt, size);
473 }
474
475 COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
476 {
477 struct target *target = get_current_target(CMD_CTX);
478 struct armv7a_common *armv7a = target_to_armv7a(target);
479
480 if (CMD_ARGC == 0) {
481 command_print(CMD_CTX, "auto cache is %s",
482 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
483 return ERROR_OK;
484 }
485
486 if (CMD_ARGC == 1) {
487 uint32_t set;
488
489 COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
490 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
491 return ERROR_OK;
492 }
493
494 return ERROR_COMMAND_SYNTAX_ERROR;
495 }
496
497 static const struct command_registration arm7a_l1_d_cache_commands[] = {
498 {
499 .name = "flush_all",
500 .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
501 .mode = COMMAND_ANY,
502 .help = "flush (clean and invalidate) complete l1 d-cache",
503 .usage = "",
504 },
505 {
506 .name = "inval",
507 .handler = arm7a_l1_d_cache_inval_virt_cmd,
508 .mode = COMMAND_ANY,
509 .help = "invalidate l1 d-cache by virtual address offset and range size",
510 .usage = "<virt_addr> [size]",
511 },
512 {
513 .name = "clean",
514 .handler = arm7a_l1_d_cache_clean_virt_cmd,
515 .mode = COMMAND_ANY,
516 .help = "clean l1 d-cache by virtual address address offset and range size",
517 .usage = "<virt_addr> [size]",
518 },
519 COMMAND_REGISTRATION_DONE
520 };
521
522 static const struct command_registration arm7a_l1_i_cache_commands[] = {
523 {
524 .name = "inval_all",
525 .handler = armv7a_i_cache_clean_inval_all_cmd,
526 .mode = COMMAND_ANY,
527 .help = "invalidate complete l1 i-cache",
528 .usage = "",
529 },
530 {
531 .name = "inval",
532 .handler = arm7a_l1_i_cache_inval_virt_cmd,
533 .mode = COMMAND_ANY,
534 .help = "invalidate l1 i-cache by virtual address offset and range size",
535 .usage = "<virt_addr> [size]",
536 },
537 COMMAND_REGISTRATION_DONE
538 };
539
540 const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
541 {
542 .name = "info",
543 .handler = arm7a_l1_cache_info_cmd,
544 .mode = COMMAND_ANY,
545 .help = "print cache realted information",
546 .usage = "",
547 },
548 {
549 .name = "d",
550 .mode = COMMAND_ANY,
551 .help = "l1 d-cache command group",
552 .usage = "",
553 .chain = arm7a_l1_d_cache_commands,
554 },
555 {
556 .name = "i",
557 .mode = COMMAND_ANY,
558 .help = "l1 i-cache command group",
559 .usage = "",
560 .chain = arm7a_l1_i_cache_commands,
561 },
562 COMMAND_REGISTRATION_DONE
563 };
564
565 const struct command_registration arm7a_cache_group_handlers[] = {
566 {
567 .name = "auto",
568 .handler = arm7a_cache_disable_auto_cmd,
569 .mode = COMMAND_ANY,
570 .help = "disable or enable automatic cache handling.",
571 .usage = "(1|0)",
572 },
573 {
574 .name = "l1",
575 .mode = COMMAND_ANY,
576 .help = "l1 cache command group",
577 .usage = "",
578 .chain = arm7a_l1_di_cache_group_handlers,
579 },
580 {
581 .chain = arm7a_l2x_cache_command_handler,
582 },
583 COMMAND_REGISTRATION_DONE
584 };
585
586 const struct command_registration arm7a_cache_command_handlers[] = {
587 {
588 .name = "cache",
589 .mode = COMMAND_ANY,
590 .help = "cache command group",
591 .usage = "",
592 .chain = arm7a_cache_group_handlers,
593 },
594 COMMAND_REGISTRATION_DONE
595 };

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)