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

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)