target/adi_v5_swd: update cached value on write to DP_SELECT
[openocd.git] / src / target / armv8_cache.c
1 /***************************************************************************
2 * Copyright (C) 2016 by Matthias Welwarsky *
3 * matthias.welwarsky@sysgo.com *
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 * You should have received a copy of the GNU General Public License *
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
17 ***************************************************************************/
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include "armv8_cache.h"
24 #include "armv8_dpm.h"
25 #include "armv8_opcodes.h"
26
27 /* CLIDR cache types */
28 #define CACHE_LEVEL_HAS_UNIFIED_CACHE 0x4
29 #define CACHE_LEVEL_HAS_D_CACHE 0x2
30 #define CACHE_LEVEL_HAS_I_CACHE 0x1
31
32 static int armv8_d_cache_sanity_check(struct armv8_common *armv8)
33 {
34 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
35
36 if (armv8_cache->d_u_cache_enabled)
37 return ERROR_OK;
38
39 return ERROR_TARGET_INVALID;
40 }
41
42 static int armv8_i_cache_sanity_check(struct armv8_common *armv8)
43 {
44 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
45
46 if (armv8_cache->i_cache_enabled)
47 return ERROR_OK;
48
49 return ERROR_TARGET_INVALID;
50 }
51
52 static int armv8_cache_d_inner_flush_level(struct armv8_common *armv8, struct armv8_cachesize *size, int cl)
53 {
54 struct arm_dpm *dpm = armv8->arm.dpm;
55 int retval = ERROR_OK;
56 int32_t c_way, c_index = size->index;
57
58 LOG_DEBUG("cl %" PRId32, cl);
59 do {
60 c_way = size->way;
61 do {
62 uint32_t value = (c_index << size->index_shift)
63 | (c_way << size->way_shift) | (cl << 1);
64 /*
65 * DC CISW - Clean and invalidate data cache
66 * line by Set/Way.
67 */
68 retval = dpm->instr_write_data_r0(dpm,
69 armv8_opcode(armv8, ARMV8_OPC_DCCISW), value);
70 if (retval != ERROR_OK)
71 goto done;
72 c_way -= 1;
73 } while (c_way >= 0);
74 c_index -= 1;
75 } while (c_index >= 0);
76
77 done:
78 return retval;
79 }
80
81 static int armv8_cache_d_inner_clean_inval_all(struct armv8_common *armv8)
82 {
83 struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
84 struct arm_dpm *dpm = armv8->arm.dpm;
85 int cl;
86 int retval;
87
88 retval = armv8_d_cache_sanity_check(armv8);
89 if (retval != ERROR_OK)
90 return retval;
91
92 retval = dpm->prepare(dpm);
93 if (retval != ERROR_OK)
94 goto done;
95
96 for (cl = 0; cl < cache->loc; cl++) {
97 /* skip i-only caches */
98 if (cache->arch[cl].ctype < CACHE_LEVEL_HAS_D_CACHE)
99 continue;
100
101 armv8_cache_d_inner_flush_level(armv8, &cache->arch[cl].d_u_size, cl);
102 }
103
104 retval = dpm->finish(dpm);
105 return retval;
106
107 done:
108 LOG_ERROR("clean invalidate failed");
109 dpm->finish(dpm);
110
111 return retval;
112 }
113
114 int armv8_cache_d_inner_flush_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
115 {
116 struct arm_dpm *dpm = armv8->arm.dpm;
117 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
118 uint64_t linelen = armv8_cache->dminline;
119 target_addr_t va_line, va_end;
120 int retval;
121
122 retval = armv8_d_cache_sanity_check(armv8);
123 if (retval != ERROR_OK)
124 return retval;
125
126 retval = dpm->prepare(dpm);
127 if (retval != ERROR_OK)
128 goto done;
129
130 va_line = va & (-linelen);
131 va_end = va + size;
132
133 while (va_line < va_end) {
134 /* DC CIVAC */
135 /* Aarch32: DCCIMVAC: ARMV4_5_MCR(15, 0, 0, 7, 14, 1) */
136 retval = dpm->instr_write_data_r0_64(dpm,
137 armv8_opcode(armv8, ARMV8_OPC_DCCIVAC), va_line);
138 if (retval != ERROR_OK)
139 goto done;
140 va_line += linelen;
141 }
142
143 dpm->finish(dpm);
144 return retval;
145
146 done:
147 LOG_ERROR("d-cache invalidate failed");
148 dpm->finish(dpm);
149
150 return retval;
151 }
152
153 int armv8_cache_i_inner_inval_virt(struct armv8_common *armv8, target_addr_t va, size_t size)
154 {
155 struct arm_dpm *dpm = armv8->arm.dpm;
156 struct armv8_cache_common *armv8_cache = &armv8->armv8_mmu.armv8_cache;
157 uint64_t linelen = armv8_cache->iminline;
158 target_addr_t va_line, va_end;
159 int retval;
160
161 retval = armv8_i_cache_sanity_check(armv8);
162 if (retval != ERROR_OK)
163 return retval;
164
165 retval = dpm->prepare(dpm);
166 if (retval != ERROR_OK)
167 goto done;
168
169 va_line = va & (-linelen);
170 va_end = va + size;
171
172 while (va_line < va_end) {
173 /* IC IVAU - Invalidate instruction cache by VA to PoU. */
174 retval = dpm->instr_write_data_r0_64(dpm,
175 armv8_opcode(armv8, ARMV8_OPC_ICIVAU), va_line);
176 if (retval != ERROR_OK)
177 goto done;
178 va_line += linelen;
179 }
180
181 dpm->finish(dpm);
182 return retval;
183
184 done:
185 LOG_ERROR("d-cache invalidate failed");
186 dpm->finish(dpm);
187
188 return retval;
189 }
190
191 static int armv8_handle_inner_cache_info_command(struct command_context *cmd_ctx,
192 struct armv8_cache_common *armv8_cache)
193 {
194 int cl;
195
196 if (armv8_cache->info == -1) {
197 command_print(cmd_ctx, "cache not yet identified");
198 return ERROR_OK;
199 }
200
201 for (cl = 0; cl < armv8_cache->loc; cl++) {
202 struct armv8_arch_cache *arch = &(armv8_cache->arch[cl]);
203
204 if (arch->ctype & 1) {
205 command_print(cmd_ctx,
206 "L%d I-Cache: linelen %" PRIi32
207 ", associativity %" PRIi32
208 ", nsets %" PRIi32
209 ", cachesize %" PRId32 " KBytes",
210 cl+1,
211 arch->i_size.linelen,
212 arch->i_size.associativity,
213 arch->i_size.nsets,
214 arch->i_size.cachesize);
215 }
216
217 if (arch->ctype >= 2) {
218 command_print(cmd_ctx,
219 "L%d D-Cache: linelen %" PRIi32
220 ", associativity %" PRIi32
221 ", nsets %" PRIi32
222 ", cachesize %" PRId32 " KBytes",
223 cl+1,
224 arch->d_u_size.linelen,
225 arch->d_u_size.associativity,
226 arch->d_u_size.nsets,
227 arch->d_u_size.cachesize);
228 }
229 }
230
231 return ERROR_OK;
232 }
233
234 static int _armv8_flush_all_data(struct target *target)
235 {
236 return armv8_cache_d_inner_clean_inval_all(target_to_armv8(target));
237 }
238
239 static int armv8_flush_all_data(struct target *target)
240 {
241 int retval = ERROR_FAIL;
242 /* check that armv8_cache is correctly identify */
243 struct armv8_common *armv8 = target_to_armv8(target);
244 if (armv8->armv8_mmu.armv8_cache.info == -1) {
245 LOG_ERROR("trying to flush un-identified cache");
246 return retval;
247 }
248
249 if (target->smp) {
250 /* look if all the other target have been flushed in order to flush level
251 * 2 */
252 struct target_list *head;
253 struct target *curr;
254 head = target->head;
255 while (head != (struct target_list *)NULL) {
256 curr = head->target;
257 if (curr->state == TARGET_HALTED) {
258 LOG_INFO("Wait flushing data l1 on core %" PRId32, curr->coreid);
259 retval = _armv8_flush_all_data(curr);
260 }
261 head = head->next;
262 }
263 } else
264 retval = _armv8_flush_all_data(target);
265 return retval;
266 }
267
268 static int get_cache_info(struct arm_dpm *dpm, int cl, int ct, uint32_t *cache_reg)
269 {
270 struct armv8_common *armv8 = dpm->arm->arch_info;
271 int retval = ERROR_OK;
272
273 /* select cache level */
274 retval = dpm->instr_write_data_r0(dpm,
275 armv8_opcode(armv8, WRITE_REG_CSSELR),
276 (cl << 1) | (ct == 1 ? 1 : 0));
277 if (retval != ERROR_OK)
278 goto done;
279
280 retval = dpm->instr_read_data_r0(dpm,
281 armv8_opcode(armv8, READ_REG_CCSIDR),
282 cache_reg);
283 done:
284 return retval;
285 }
286
287 static struct armv8_cachesize decode_cache_reg(uint32_t cache_reg)
288 {
289 struct armv8_cachesize size;
290 int i = 0;
291
292 size.linelen = 16 << (cache_reg & 0x7);
293 size.associativity = ((cache_reg >> 3) & 0x3ff) + 1;
294 size.nsets = ((cache_reg >> 13) & 0x7fff) + 1;
295 size.cachesize = size.linelen * size.associativity * size.nsets / 1024;
296
297 /* compute info for set way operation on cache */
298 size.index_shift = (cache_reg & 0x7) + 4;
299 size.index = (cache_reg >> 13) & 0x7fff;
300 size.way = ((cache_reg >> 3) & 0x3ff);
301
302 while (((size.way << i) & 0x80000000) == 0)
303 i++;
304 size.way_shift = i;
305
306 return size;
307 }
308
309 int armv8_identify_cache(struct armv8_common *armv8)
310 {
311 /* read cache descriptor */
312 int retval = ERROR_FAIL;
313 struct arm *arm = &armv8->arm;
314 struct arm_dpm *dpm = armv8->arm.dpm;
315 uint32_t csselr, clidr, ctr;
316 uint32_t cache_reg;
317 int cl, ctype;
318 struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
319
320 retval = dpm->prepare(dpm);
321 if (retval != ERROR_OK)
322 goto done;
323
324 /* check if we're in an unprivileged mode */
325 if (armv8_curel_from_core_mode(arm->core_mode) < SYSTEM_CUREL_EL1) {
326 retval = armv8_dpm_modeswitch(dpm, ARMV8_64_EL1H);
327 if (retval != ERROR_OK)
328 return retval;
329 }
330
331 /* retrieve CTR */
332 retval = dpm->instr_read_data_r0(dpm,
333 armv8_opcode(armv8, READ_REG_CTR), &ctr);
334 if (retval != ERROR_OK)
335 goto done;
336
337 cache->iminline = 4UL << (ctr & 0xf);
338 cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
339 LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
340 ctr, cache->iminline, cache->dminline);
341
342 /* retrieve CLIDR */
343 retval = dpm->instr_read_data_r0(dpm,
344 armv8_opcode(armv8, READ_REG_CLIDR), &clidr);
345 if (retval != ERROR_OK)
346 goto done;
347
348 cache->loc = (clidr & 0x7000000) >> 24;
349 LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
350
351 /* retrieve selected cache for later restore
352 * MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
353 retval = dpm->instr_read_data_r0(dpm,
354 armv8_opcode(armv8, READ_REG_CSSELR), &csselr);
355 if (retval != ERROR_OK)
356 goto done;
357
358 /* retrieve all available inner caches */
359 for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
360
361 /* isolate cache type at current level */
362 ctype = clidr & 7;
363
364 /* skip reserved values */
365 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
366 continue;
367
368 /* separate d or unified d/i cache at this level ? */
369 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
370 /* retrieve d-cache info */
371 retval = get_cache_info(dpm, cl, 0, &cache_reg);
372 if (retval != ERROR_OK)
373 goto done;
374 cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
375
376 LOG_DEBUG("data/unified cache index %d << %d, way %d << %d",
377 cache->arch[cl].d_u_size.index,
378 cache->arch[cl].d_u_size.index_shift,
379 cache->arch[cl].d_u_size.way,
380 cache->arch[cl].d_u_size.way_shift);
381
382 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
383 cache->arch[cl].d_u_size.linelen,
384 cache->arch[cl].d_u_size.cachesize,
385 cache->arch[cl].d_u_size.associativity);
386 }
387
388 /* separate i-cache at this level ? */
389 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
390 /* retrieve i-cache info */
391 retval = get_cache_info(dpm, cl, 1, &cache_reg);
392 if (retval != ERROR_OK)
393 goto done;
394 cache->arch[cl].i_size = decode_cache_reg(cache_reg);
395
396 LOG_DEBUG("instruction cache index %d << %d, way %d << %d",
397 cache->arch[cl].i_size.index,
398 cache->arch[cl].i_size.index_shift,
399 cache->arch[cl].i_size.way,
400 cache->arch[cl].i_size.way_shift);
401
402 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
403 cache->arch[cl].i_size.linelen,
404 cache->arch[cl].i_size.cachesize,
405 cache->arch[cl].i_size.associativity);
406 }
407
408 cache->arch[cl].ctype = ctype;
409 }
410
411 /* restore selected cache */
412 dpm->instr_write_data_r0(dpm,
413 armv8_opcode(armv8, WRITE_REG_CSSELR), csselr);
414 if (retval != ERROR_OK)
415 goto done;
416
417 armv8->armv8_mmu.armv8_cache.info = 1;
418
419 /* if no l2 cache initialize l1 data cache flush function function */
420 if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache == NULL) {
421 armv8->armv8_mmu.armv8_cache.display_cache_info =
422 armv8_handle_inner_cache_info_command;
423 armv8->armv8_mmu.armv8_cache.flush_all_data_cache =
424 armv8_flush_all_data;
425 }
426
427 done:
428 armv8_dpm_modeswitch(dpm, ARM_MODE_ANY);
429 dpm->finish(dpm);
430 return retval;
431
432 }

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)