]> git.sur5r.net Git - openocd/blob - src/target/armv7a_cache.c
add armv7a_cache handlers
[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("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_clean_inval_all(struct target *target)
64 {
65         struct armv7a_common *armv7a = target_to_armv7a(target);
66         struct arm_dpm *dpm = armv7a->arm.dpm;
67         struct armv7a_cachesize *d_u_size =
68                 &(armv7a->armv7a_mmu.armv7a_cache.d_u_size);
69         int32_t c_way, c_index = d_u_size->index;
70         int retval;
71
72         retval = armv7a_l1_d_cache_sanity_check(target);
73         if (retval != ERROR_OK)
74                 return retval;
75
76         retval = dpm->prepare(dpm);
77         if (retval != ERROR_OK)
78                 goto done;
79
80         do {
81                 c_way = d_u_size->way;
82                 do {
83                         uint32_t value = (c_index << d_u_size->index_shift)
84                                 | (c_way << d_u_size->way_shift);
85                         /*
86                          * DCCISW - Clean and invalidate data cache
87                          * line by Set/Way.
88                          */
89                         retval = dpm->instr_write_data_r0(dpm,
90                                         ARMV4_5_MCR(15, 0, 0, 7, 14, 2),
91                                         value);
92                         if (retval != ERROR_OK)
93                                 goto done;
94                         c_way -= 1;
95                 } while (c_way >= 0);
96                 c_index -= 1;
97         } while (c_index >= 0);
98
99         return retval;
100
101 done:
102         LOG_ERROR("clean invalidate failed");
103         dpm->finish(dpm);
104
105         return retval;
106 }
107
108 int armv7a_cache_auto_flush_all_data(struct target *target)
109 {
110         int retval = ERROR_FAIL;
111         struct armv7a_common *armv7a = target_to_armv7a(target);
112
113         if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
114                 return ERROR_OK;
115
116         if (target->smp) {
117                 struct target_list *head;
118                 struct target *curr;
119                 head = target->head;
120                 while (head != (struct target_list *)NULL) {
121                         curr = head->target;
122                         if (curr->state == TARGET_HALTED)
123                                 retval = armv7a_l1_d_cache_clean_inval_all(curr);
124
125                         head = head->next;
126                 }
127         } else
128                 retval = armv7a_l1_d_cache_clean_inval_all(target);
129
130         /* FIXME: do l2x flushing here */
131
132         return retval;
133 }
134
135
136 static int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
137                                         uint32_t size)
138 {
139         struct armv7a_common *armv7a = target_to_armv7a(target);
140         struct arm_dpm *dpm = armv7a->arm.dpm;
141         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
142         uint32_t i, linelen = armv7a_cache->dminline;
143         int retval;
144
145         retval = armv7a_l1_d_cache_sanity_check(target);
146         if (retval != ERROR_OK)
147                 return retval;
148
149         retval = dpm->prepare(dpm);
150         if (retval != ERROR_OK)
151                 goto done;
152
153         for (i = 0; i < size; i += linelen) {
154                 uint32_t offs = virt + i;
155
156                 /* DCIMVAC - Clean and invalidate data cache line by VA to PoC. */
157                 retval = dpm->instr_write_data_r0(dpm,
158                                 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), offs);
159                 if (retval != ERROR_OK)
160                         goto done;
161         }
162         return retval;
163
164 done:
165         LOG_ERROR("d-cache invalidate failed");
166         dpm->finish(dpm);
167
168         return retval;
169 }
170
171 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
172                                         unsigned int size)
173 {
174         struct armv7a_common *armv7a = target_to_armv7a(target);
175         struct arm_dpm *dpm = armv7a->arm.dpm;
176         struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
177         uint32_t i, linelen = armv7a_cache->dminline;
178         int retval;
179
180         retval = armv7a_l1_d_cache_sanity_check(target);
181         if (retval != ERROR_OK)
182                 return retval;
183
184         retval = dpm->prepare(dpm);
185         if (retval != ERROR_OK)
186                 goto done;
187
188         for (i = 0; i < size; i += linelen) {
189                 uint32_t offs = virt + i;
190
191                 /* FIXME: do we need DCCVAC or DCCVAU */
192                 /* FIXME: in both cases it is not enough for i-cache */
193                 retval = dpm->instr_write_data_r0(dpm,
194                                 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), offs);
195                 if (retval != ERROR_OK)
196                         goto done;
197         }
198         return retval;
199
200 done:
201         LOG_ERROR("d-cache invalidate failed");
202         dpm->finish(dpm);
203
204         return retval;
205 }
206
207 int armv7a_l1_i_cache_inval_all(struct target *target)
208 {
209         struct armv7a_common *armv7a = target_to_armv7a(target);
210         struct arm_dpm *dpm = armv7a->arm.dpm;
211         int retval;
212
213         retval = armv7a_l1_i_cache_sanity_check(target);
214         if (retval != ERROR_OK)
215                 return retval;
216
217         retval = dpm->prepare(dpm);
218         if (retval != ERROR_OK)
219                 goto done;
220
221         if (target->smp) {
222                 /* ICIALLUIS */
223                 retval = dpm->instr_write_data_r0(dpm,
224                                 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
225         } else {
226                 /* ICIALLU */
227                 retval = dpm->instr_write_data_r0(dpm,
228                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
229         }
230
231         if (retval != ERROR_OK)
232                 goto done;
233
234         dpm->finish(dpm);
235         return retval;
236
237 done:
238         LOG_ERROR("i-cache invalidate failed");
239         dpm->finish(dpm);
240
241         return retval;
242 }
243
244 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
245                                         uint32_t size)
246 {
247         struct armv7a_common *armv7a = target_to_armv7a(target);
248         struct arm_dpm *dpm = armv7a->arm.dpm;
249         struct armv7a_cache_common *armv7a_cache =
250                                 &armv7a->armv7a_mmu.armv7a_cache;
251         uint32_t linelen = armv7a_cache->iminline;
252         uint32_t va_line, va_end;
253         int retval;
254
255         retval = armv7a_l1_i_cache_sanity_check(target);
256         if (retval != ERROR_OK)
257                 return retval;
258
259         retval = dpm->prepare(dpm);
260         if (retval != ERROR_OK)
261                 goto done;
262
263         va_line = virt & (-linelen);
264         va_end = virt + size;
265
266         while (va_line < va_end) {
267                 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
268                 retval = dpm->instr_write_data_r0(dpm,
269                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
270                 if (retval != ERROR_OK)
271                         goto done;
272                 /* BPIMVA */
273                 retval = dpm->instr_write_data_r0(dpm,
274                                 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
275                 if (retval != ERROR_OK)
276                         goto done;
277                 va_line += linelen;
278         }
279         return retval;
280
281 done:
282         LOG_ERROR("i-cache invalidate failed");
283         dpm->finish(dpm);
284
285         return retval;
286 }
287
288
289 /*
290  * We assume that target core was chosen correctly. It means if same data
291  * was handled by two cores, other core will loose the changes. Since it
292  * is impossible to know (FIXME) which core has correct data, keep in mind
293  * that some kind of data lost or korruption is possible.
294  * Possible scenario:
295  *  - core1 loaded and changed data on 0x12345678
296  *  - we halted target and modified same data on core0
297  *  - data on core1 will be lost.
298  */
299 int armv7a_cache_auto_flush_on_write(struct target *target, uint32_t virt,
300                                         uint32_t size)
301 {
302         struct armv7a_common *armv7a = target_to_armv7a(target);
303         int retval = ERROR_OK;
304
305         if (!armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled)
306                 return ERROR_OK;
307
308         armv7a_l1_d_cache_clean_virt(target, virt, size);
309         armv7a_l2x_cache_flush_virt(target, virt, size);
310
311         if (target->smp) {
312                 struct target_list *head;
313                 struct target *curr;
314                 head = target->head;
315                 while (head != (struct target_list *)NULL) {
316                         curr = head->target;
317                         if (curr->state == TARGET_HALTED) {
318                                 retval = armv7a_l1_i_cache_inval_all(curr);
319                                 if (retval != ERROR_OK)
320                                         return retval;
321                                 retval = armv7a_l1_d_cache_inval_virt(target,
322                                                 virt, size);
323                                 if (retval != ERROR_OK)
324                                         return retval;
325                         }
326                         head = head->next;
327                 }
328         } else {
329                 retval = armv7a_l1_i_cache_inval_all(target);
330                 if (retval != ERROR_OK)
331                         return retval;
332                 retval = armv7a_l1_d_cache_inval_virt(target, virt, size);
333                 if (retval != ERROR_OK)
334                         return retval;
335         }
336
337         return retval;
338 }
339
340 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
341 {
342         struct target *target = get_current_target(CMD_CTX);
343         struct armv7a_common *armv7a = target_to_armv7a(target);
344
345         return armv7a_handle_cache_info_command(CMD_CTX,
346                         &armv7a->armv7a_mmu.armv7a_cache);
347 }
348
349 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
350 {
351         struct target *target = get_current_target(CMD_CTX);
352
353         armv7a_l1_d_cache_clean_inval_all(target);
354
355         return 0;
356 }
357
358 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
359 {
360         struct target *target = get_current_target(CMD_CTX);
361         uint32_t virt, size;
362
363         if (CMD_ARGC == 0 || CMD_ARGC > 2)
364                 return ERROR_COMMAND_SYNTAX_ERROR;
365
366         if (CMD_ARGC == 2)
367                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
368         else
369                 size = 1;
370
371         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
372
373         return armv7a_l1_d_cache_inval_virt(target, virt, size);
374 }
375
376 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
377 {
378         struct target *target = get_current_target(CMD_CTX);
379         uint32_t virt, size;
380
381         if (CMD_ARGC == 0 || CMD_ARGC > 2)
382                 return ERROR_COMMAND_SYNTAX_ERROR;
383
384         if (CMD_ARGC == 2)
385                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
386         else
387                 size = 1;
388
389         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
390
391         return armv7a_l1_d_cache_clean_virt(target, virt, size);
392 }
393
394 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
395 {
396         struct target *target = get_current_target(CMD_CTX);
397
398         armv7a_l1_i_cache_inval_all(target);
399
400         return 0;
401 }
402
403 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
404 {
405         struct target *target = get_current_target(CMD_CTX);
406         uint32_t virt, size;
407
408         if (CMD_ARGC == 0 || CMD_ARGC > 2)
409                 return ERROR_COMMAND_SYNTAX_ERROR;
410
411         if (CMD_ARGC == 2)
412                 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
413         else
414                 size = 1;
415
416         COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
417
418         return armv7a_l1_i_cache_inval_virt(target, virt, size);
419 }
420
421 COMMAND_HANDLER(arm7a_cache_disable_auto_cmd)
422 {
423         struct target *target = get_current_target(CMD_CTX);
424         struct armv7a_common *armv7a = target_to_armv7a(target);
425
426         if (CMD_ARGC == 0) {
427                 command_print(CMD_CTX, "auto cache is %s",
428                         armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled ? "enabled" : "disabled");
429                 return ERROR_OK;
430         }
431
432         if (CMD_ARGC == 1) {
433                 uint32_t set;
434
435                 COMMAND_PARSE_ENABLE(CMD_ARGV[0], set);
436                 armv7a->armv7a_mmu.armv7a_cache.auto_cache_enabled = !!set;
437                 return ERROR_OK;
438         }
439
440         return ERROR_COMMAND_SYNTAX_ERROR;
441 }
442
443 static const struct command_registration arm7a_l1_d_cache_commands[] = {
444         {
445                 .name = "flush_all",
446                 .handler = armv7a_l1_d_cache_clean_inval_all_cmd,
447                 .mode = COMMAND_ANY,
448                 .help = "flush (clean and invalidate) complete l1 d-cache",
449                 .usage = "",
450         },
451         {
452                 .name = "inval",
453                 .handler = arm7a_l1_d_cache_inval_virt_cmd,
454                 .mode = COMMAND_ANY,
455                 .help = "invalidate l1 d-cache by virtual address offset and range size",
456                 .usage = "<virt_addr> [size]",
457         },
458         {
459                 .name = "clean",
460                 .handler = arm7a_l1_d_cache_clean_virt_cmd,
461                 .mode = COMMAND_ANY,
462                 .help = "clean l1 d-cache by virtual address address offset and range size",
463                 .usage = "<virt_addr> [size]",
464         },
465         COMMAND_REGISTRATION_DONE
466 };
467
468 static const struct command_registration arm7a_l1_i_cache_commands[] = {
469         {
470                 .name = "inval_all",
471                 .handler = armv7a_i_cache_clean_inval_all_cmd,
472                 .mode = COMMAND_ANY,
473                 .help = "invalidate complete l1 i-cache",
474                 .usage = "",
475         },
476         {
477                 .name = "inval",
478                 .handler = arm7a_l1_i_cache_inval_virt_cmd,
479                 .mode = COMMAND_ANY,
480                 .help = "invalidate l1 i-cache by virtual address offset and range size",
481                 .usage = "<virt_addr> [size]",
482         },
483         COMMAND_REGISTRATION_DONE
484 };
485
486 const struct command_registration arm7a_l1_di_cache_group_handlers[] = {
487         {
488                 .name = "info",
489                 .handler = arm7a_l1_cache_info_cmd,
490                 .mode = COMMAND_ANY,
491                 .help = "print cache realted information",
492                 .usage = "",
493         },
494         {
495                 .name = "d",
496                 .mode = COMMAND_ANY,
497                 .help = "l1 d-cache command group",
498                 .usage = "",
499                 .chain = arm7a_l1_d_cache_commands,
500         },
501         {
502                 .name = "i",
503                 .mode = COMMAND_ANY,
504                 .help = "l1 i-cache command group",
505                 .usage = "",
506                 .chain = arm7a_l1_i_cache_commands,
507         },
508         COMMAND_REGISTRATION_DONE
509 };
510
511 const struct command_registration arm7a_cache_group_handlers[] = {
512         {
513                 .name = "auto",
514                 .handler = arm7a_cache_disable_auto_cmd,
515                 .mode = COMMAND_ANY,
516                 .help = "disable or enable automatic cache handling.",
517                 .usage = "(1|0)",
518         },
519         {
520                 .name = "l1",
521                 .mode = COMMAND_ANY,
522                 .help = "l1 cache command group",
523                 .usage = "",
524                 .chain = arm7a_l1_di_cache_group_handlers,
525         },
526         {
527                 .chain = arm7a_l2x_cache_command_handler,
528         },
529         COMMAND_REGISTRATION_DONE
530 };
531
532 const struct command_registration arm7a_cache_command_handlers[] = {
533         {
534                 .name = "cache",
535                 .mode = COMMAND_ANY,
536                 .help = "cache command group",
537                 .usage = "",
538                 .chain = arm7a_cache_group_handlers,
539         },
540         COMMAND_REGISTRATION_DONE
541 };