add armv7a_cache handlers
[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_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 };

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)