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

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)