target/xtensa: avoid IHI for writes to non-executable memory
[openocd.git] / src / target / armv7a_cache.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 /***************************************************************************
4 * Copyright (C) 2015 by Oleksij Rempel *
5 * linux@rempel-privat.de *
6 ***************************************************************************/
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "jtag/interface.h"
13 #include "arm.h"
14 #include "armv7a.h"
15 #include "armv7a_cache.h"
16 #include <helper/time_support.h>
17 #include "arm_opcodes.h"
18 #include "smp.h"
19
20 static int armv7a_l1_d_cache_sanity_check(struct target *target)
21 {
22 struct armv7a_common *armv7a = target_to_armv7a(target);
23
24 if (target->state != TARGET_HALTED) {
25 LOG_TARGET_ERROR(target, "not halted");
26 return ERROR_TARGET_NOT_HALTED;
27 }
28
29 /* check that cache data is on at target halt */
30 if (!armv7a->armv7a_mmu.armv7a_cache.d_u_cache_enabled) {
31 LOG_DEBUG("data cache is not enabled");
32 return ERROR_TARGET_INVALID;
33 }
34
35 return ERROR_OK;
36 }
37
38 static int armv7a_l1_i_cache_sanity_check(struct target *target)
39 {
40 struct armv7a_common *armv7a = target_to_armv7a(target);
41
42 if (target->state != TARGET_HALTED) {
43 LOG_TARGET_ERROR(target, "not halted");
44 return ERROR_TARGET_NOT_HALTED;
45 }
46
47 /* check that cache data is on at target halt */
48 if (!armv7a->armv7a_mmu.armv7a_cache.i_cache_enabled) {
49 LOG_DEBUG("instruction cache is not enabled");
50 return ERROR_TARGET_INVALID;
51 }
52
53 return ERROR_OK;
54 }
55
56 static int armv7a_l1_d_cache_flush_level(struct arm_dpm *dpm, struct armv7a_cachesize *size, int cl)
57 {
58 int retval = ERROR_OK;
59 int32_t c_way, c_index = size->index;
60
61 LOG_DEBUG("cl %" PRId32, cl);
62 do {
63 keep_alive();
64 c_way = size->way;
65 do {
66 uint32_t value = (c_index << size->index_shift)
67 | (c_way << size->way_shift) | (cl << 1);
68 /*
69 * DCCISW - Clean and invalidate data cache
70 * line by Set/Way.
71 */
72 retval = dpm->instr_write_data_r0(dpm,
73 ARMV4_5_MCR(15, 0, 0, 7, 14, 2),
74 value);
75 if (retval != ERROR_OK)
76 goto done;
77 c_way -= 1;
78 } while (c_way >= 0);
79 c_index -= 1;
80 } while (c_index >= 0);
81
82 done:
83 keep_alive();
84 return retval;
85 }
86
87 static int armv7a_l1_d_cache_clean_inval_all(struct target *target)
88 {
89 struct armv7a_common *armv7a = target_to_armv7a(target);
90 struct armv7a_cache_common *cache = &(armv7a->armv7a_mmu.armv7a_cache);
91 struct arm_dpm *dpm = armv7a->arm.dpm;
92 int cl;
93 int retval;
94
95 retval = armv7a_l1_d_cache_sanity_check(target);
96 if (retval != ERROR_OK)
97 return retval;
98
99 retval = dpm->prepare(dpm);
100 if (retval != ERROR_OK)
101 goto done;
102
103 for (cl = 0; cl < cache->loc; cl++) {
104 /* skip i-only caches */
105 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
106 continue;
107
108 armv7a_l1_d_cache_flush_level(dpm, &cache->arch[cl].d_u_size, cl);
109 }
110
111 retval = dpm->finish(dpm);
112 return retval;
113
114 done:
115 LOG_ERROR("clean invalidate failed");
116 dpm->finish(dpm);
117
118 return retval;
119 }
120
121 int armv7a_cache_flush_all_data(struct target *target)
122 {
123 int retval = ERROR_FAIL;
124
125 if (target->smp) {
126 struct target_list *head;
127 foreach_smp_target(head, target->smp_targets) {
128 struct target *curr = head->target;
129 if (curr->state == TARGET_HALTED) {
130 int retval1 = armv7a_l1_d_cache_clean_inval_all(curr);
131 if (retval1 != ERROR_OK)
132 retval = retval1;
133 }
134 }
135 } else
136 retval = armv7a_l1_d_cache_clean_inval_all(target);
137
138 if (retval != ERROR_OK)
139 return retval;
140
141 /* do outer cache flushing after inner caches have been flushed */
142 return arm7a_l2x_flush_all_data(target);
143 }
144
145
146 int armv7a_l1_d_cache_inval_virt(struct target *target, uint32_t virt,
147 uint32_t size)
148 {
149 struct armv7a_common *armv7a = target_to_armv7a(target);
150 struct arm_dpm *dpm = armv7a->arm.dpm;
151 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
152 uint32_t linelen = armv7a_cache->dminline;
153 uint32_t va_line, va_end;
154 int retval, i = 0;
155
156 retval = armv7a_l1_d_cache_sanity_check(target);
157 if (retval != ERROR_OK)
158 return retval;
159
160 retval = dpm->prepare(dpm);
161 if (retval != ERROR_OK)
162 goto done;
163
164 va_line = virt & (-linelen);
165 va_end = virt + size;
166
167 /* handle unaligned start */
168 if (virt != va_line) {
169 /* DCCIMVAC */
170 retval = dpm->instr_write_data_r0(dpm,
171 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
172 if (retval != ERROR_OK)
173 goto done;
174 va_line += linelen;
175 }
176
177 /* handle unaligned end */
178 if ((va_end & (linelen-1)) != 0) {
179 va_end &= (-linelen);
180 /* DCCIMVAC */
181 retval = dpm->instr_write_data_r0(dpm,
182 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_end);
183 if (retval != ERROR_OK)
184 goto done;
185 }
186
187 while (va_line < va_end) {
188 if ((i++ & 0x3f) == 0)
189 keep_alive();
190 /* DCIMVAC - Invalidate data cache line by VA to PoC. */
191 retval = dpm->instr_write_data_r0(dpm,
192 ARMV4_5_MCR(15, 0, 0, 7, 6, 1), va_line);
193 if (retval != ERROR_OK)
194 goto done;
195 va_line += linelen;
196 }
197
198 keep_alive();
199 dpm->finish(dpm);
200 return retval;
201
202 done:
203 LOG_ERROR("d-cache invalidate failed");
204 keep_alive();
205 dpm->finish(dpm);
206
207 return retval;
208 }
209
210 int armv7a_l1_d_cache_clean_virt(struct target *target, uint32_t virt,
211 unsigned int size)
212 {
213 struct armv7a_common *armv7a = target_to_armv7a(target);
214 struct arm_dpm *dpm = armv7a->arm.dpm;
215 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
216 uint32_t linelen = armv7a_cache->dminline;
217 uint32_t va_line, va_end;
218 int retval, i = 0;
219
220 retval = armv7a_l1_d_cache_sanity_check(target);
221 if (retval != ERROR_OK)
222 return retval;
223
224 retval = dpm->prepare(dpm);
225 if (retval != ERROR_OK)
226 goto done;
227
228 va_line = virt & (-linelen);
229 va_end = virt + size;
230
231 while (va_line < va_end) {
232 if ((i++ & 0x3f) == 0)
233 keep_alive();
234 /* DCCMVAC - Data Cache Clean by MVA to PoC */
235 retval = dpm->instr_write_data_r0(dpm,
236 ARMV4_5_MCR(15, 0, 0, 7, 10, 1), va_line);
237 if (retval != ERROR_OK)
238 goto done;
239 va_line += linelen;
240 }
241
242 keep_alive();
243 dpm->finish(dpm);
244 return retval;
245
246 done:
247 LOG_ERROR("d-cache invalidate failed");
248 keep_alive();
249 dpm->finish(dpm);
250
251 return retval;
252 }
253
254 int armv7a_l1_d_cache_flush_virt(struct target *target, uint32_t virt,
255 unsigned int size)
256 {
257 struct armv7a_common *armv7a = target_to_armv7a(target);
258 struct arm_dpm *dpm = armv7a->arm.dpm;
259 struct armv7a_cache_common *armv7a_cache = &armv7a->armv7a_mmu.armv7a_cache;
260 uint32_t linelen = armv7a_cache->dminline;
261 uint32_t va_line, va_end;
262 int retval, i = 0;
263
264 retval = armv7a_l1_d_cache_sanity_check(target);
265 if (retval != ERROR_OK)
266 return retval;
267
268 retval = dpm->prepare(dpm);
269 if (retval != ERROR_OK)
270 goto done;
271
272 va_line = virt & (-linelen);
273 va_end = virt + size;
274
275 while (va_line < va_end) {
276 if ((i++ & 0x3f) == 0)
277 keep_alive();
278 /* DCCIMVAC */
279 retval = dpm->instr_write_data_r0(dpm,
280 ARMV4_5_MCR(15, 0, 0, 7, 14, 1), va_line);
281 if (retval != ERROR_OK)
282 goto done;
283 va_line += linelen;
284 }
285
286 keep_alive();
287 dpm->finish(dpm);
288 return retval;
289
290 done:
291 LOG_ERROR("d-cache invalidate failed");
292 keep_alive();
293 dpm->finish(dpm);
294
295 return retval;
296 }
297
298 int armv7a_l1_i_cache_inval_all(struct target *target)
299 {
300 struct armv7a_common *armv7a = target_to_armv7a(target);
301 struct arm_dpm *dpm = armv7a->arm.dpm;
302 int retval;
303
304 retval = armv7a_l1_i_cache_sanity_check(target);
305 if (retval != ERROR_OK)
306 return retval;
307
308 retval = dpm->prepare(dpm);
309 if (retval != ERROR_OK)
310 goto done;
311
312 if (target->smp) {
313 /* ICIALLUIS */
314 retval = dpm->instr_write_data_r0(dpm,
315 ARMV4_5_MCR(15, 0, 0, 7, 1, 0), 0);
316 } else {
317 /* ICIALLU */
318 retval = dpm->instr_write_data_r0(dpm,
319 ARMV4_5_MCR(15, 0, 0, 7, 5, 0), 0);
320 }
321
322 if (retval != ERROR_OK)
323 goto done;
324
325 dpm->finish(dpm);
326 return retval;
327
328 done:
329 LOG_ERROR("i-cache invalidate failed");
330 dpm->finish(dpm);
331
332 return retval;
333 }
334
335 int armv7a_l1_i_cache_inval_virt(struct target *target, uint32_t virt,
336 uint32_t size)
337 {
338 struct armv7a_common *armv7a = target_to_armv7a(target);
339 struct arm_dpm *dpm = armv7a->arm.dpm;
340 struct armv7a_cache_common *armv7a_cache =
341 &armv7a->armv7a_mmu.armv7a_cache;
342 uint32_t linelen = armv7a_cache->iminline;
343 uint32_t va_line, va_end;
344 int retval, i = 0;
345
346 retval = armv7a_l1_i_cache_sanity_check(target);
347 if (retval != ERROR_OK)
348 return retval;
349
350 retval = dpm->prepare(dpm);
351 if (retval != ERROR_OK)
352 goto done;
353
354 va_line = virt & (-linelen);
355 va_end = virt + size;
356
357 while (va_line < va_end) {
358 if ((i++ & 0x3f) == 0)
359 keep_alive();
360 /* ICIMVAU - Invalidate instruction cache by VA to PoU. */
361 retval = dpm->instr_write_data_r0(dpm,
362 ARMV4_5_MCR(15, 0, 0, 7, 5, 1), va_line);
363 if (retval != ERROR_OK)
364 goto done;
365 /* BPIMVA */
366 retval = dpm->instr_write_data_r0(dpm,
367 ARMV4_5_MCR(15, 0, 0, 7, 5, 7), va_line);
368 if (retval != ERROR_OK)
369 goto done;
370 va_line += linelen;
371 }
372 keep_alive();
373 dpm->finish(dpm);
374 return retval;
375
376 done:
377 LOG_ERROR("i-cache invalidate failed");
378 keep_alive();
379 dpm->finish(dpm);
380
381 return retval;
382 }
383
384 int armv7a_cache_flush_virt(struct target *target, uint32_t virt,
385 uint32_t size)
386 {
387 armv7a_l1_d_cache_flush_virt(target, virt, size);
388 armv7a_l2x_cache_flush_virt(target, virt, size);
389
390 return ERROR_OK;
391 }
392
393 COMMAND_HANDLER(arm7a_l1_cache_info_cmd)
394 {
395 struct target *target = get_current_target(CMD_CTX);
396 struct armv7a_common *armv7a = target_to_armv7a(target);
397
398 return armv7a_handle_cache_info_command(CMD,
399 &armv7a->armv7a_mmu.armv7a_cache);
400 }
401
402 COMMAND_HANDLER(armv7a_l1_d_cache_clean_inval_all_cmd)
403 {
404 struct target *target = get_current_target(CMD_CTX);
405
406 armv7a_l1_d_cache_clean_inval_all(target);
407
408 return 0;
409 }
410
411 COMMAND_HANDLER(arm7a_l1_d_cache_inval_virt_cmd)
412 {
413 struct target *target = get_current_target(CMD_CTX);
414 uint32_t virt, size;
415
416 if (CMD_ARGC == 0 || CMD_ARGC > 2)
417 return ERROR_COMMAND_SYNTAX_ERROR;
418
419 if (CMD_ARGC == 2)
420 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
421 else
422 size = 1;
423
424 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
425
426 return armv7a_l1_d_cache_inval_virt(target, virt, size);
427 }
428
429 COMMAND_HANDLER(arm7a_l1_d_cache_clean_virt_cmd)
430 {
431 struct target *target = get_current_target(CMD_CTX);
432 uint32_t virt, size;
433
434 if (CMD_ARGC == 0 || CMD_ARGC > 2)
435 return ERROR_COMMAND_SYNTAX_ERROR;
436
437 if (CMD_ARGC == 2)
438 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
439 else
440 size = 1;
441
442 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
443
444 return armv7a_l1_d_cache_clean_virt(target, virt, size);
445 }
446
447 COMMAND_HANDLER(armv7a_i_cache_clean_inval_all_cmd)
448 {
449 struct target *target = get_current_target(CMD_CTX);
450
451 armv7a_l1_i_cache_inval_all(target);
452
453 return 0;
454 }
455
456 COMMAND_HANDLER(arm7a_l1_i_cache_inval_virt_cmd)
457 {
458 struct target *target = get_current_target(CMD_CTX);
459 uint32_t virt, size;
460
461 if (CMD_ARGC == 0 || CMD_ARGC > 2)
462 return ERROR_COMMAND_SYNTAX_ERROR;
463
464 if (CMD_ARGC == 2)
465 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], size);
466 else
467 size = 1;
468
469 COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], virt);
470
471 return armv7a_l1_i_cache_inval_virt(target, virt, size);
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 static 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 related 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 static const struct command_registration arm7a_cache_group_handlers[] = {
543 {
544 .name = "l1",
545 .mode = COMMAND_ANY,
546 .help = "l1 cache command group",
547 .usage = "",
548 .chain = arm7a_l1_di_cache_group_handlers,
549 },
550 {
551 .chain = arm7a_l2x_cache_command_handler,
552 },
553 COMMAND_REGISTRATION_DONE
554 };
555
556 const struct command_registration arm7a_cache_command_handlers[] = {
557 {
558 .name = "cache",
559 .mode = COMMAND_ANY,
560 .help = "cache command group",
561 .usage = "",
562 .chain = arm7a_cache_group_handlers,
563 },
564 COMMAND_REGISTRATION_DONE
565 };

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)