armv7a: correctly handle invalidation of inner data caches
[fw/openocd] / 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 };