7f610c953536714f3e36eecbce102929bb4f282c
[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_dpm *dpm = armv8->arm.dpm;
314 uint32_t csselr, clidr, ctr;
315 uint32_t cache_reg;
316 int cl, ctype;
317 struct armv8_cache_common *cache = &(armv8->armv8_mmu.armv8_cache);
318
319 retval = dpm->prepare(dpm);
320 if (retval != ERROR_OK)
321 goto done;
322
323 /* retrieve CTR */
324 retval = dpm->instr_read_data_r0(dpm,
325 armv8_opcode(armv8, READ_REG_CTR), &ctr);
326 if (retval != ERROR_OK)
327 goto done;
328
329 cache->iminline = 4UL << (ctr & 0xf);
330 cache->dminline = 4UL << ((ctr & 0xf0000) >> 16);
331 LOG_DEBUG("ctr %" PRIx32 " ctr.iminline %" PRId32 " ctr.dminline %" PRId32,
332 ctr, cache->iminline, cache->dminline);
333
334 /* retrieve CLIDR */
335 retval = dpm->instr_read_data_r0(dpm,
336 armv8_opcode(armv8, READ_REG_CLIDR), &clidr);
337 if (retval != ERROR_OK)
338 goto done;
339
340 cache->loc = (clidr & 0x7000000) >> 24;
341 LOG_DEBUG("Number of cache levels to PoC %" PRId32, cache->loc);
342
343 /* retrieve selected cache for later restore
344 * MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
345 retval = dpm->instr_read_data_r0(dpm,
346 armv8_opcode(armv8, READ_REG_CSSELR), &csselr);
347 if (retval != ERROR_OK)
348 goto done;
349
350 /* retrieve all available inner caches */
351 for (cl = 0; cl < cache->loc; clidr >>= 3, cl++) {
352
353 /* isolate cache type at current level */
354 ctype = clidr & 7;
355
356 /* skip reserved values */
357 if (ctype > CACHE_LEVEL_HAS_UNIFIED_CACHE)
358 continue;
359
360 /* separate d or unified d/i cache at this level ? */
361 if (ctype & (CACHE_LEVEL_HAS_UNIFIED_CACHE | CACHE_LEVEL_HAS_D_CACHE)) {
362 /* retrieve d-cache info */
363 retval = get_cache_info(dpm, cl, 0, &cache_reg);
364 if (retval != ERROR_OK)
365 goto done;
366 cache->arch[cl].d_u_size = decode_cache_reg(cache_reg);
367
368 LOG_DEBUG("data/unified cache index %d << %d, way %d << %d",
369 cache->arch[cl].d_u_size.index,
370 cache->arch[cl].d_u_size.index_shift,
371 cache->arch[cl].d_u_size.way,
372 cache->arch[cl].d_u_size.way_shift);
373
374 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
375 cache->arch[cl].d_u_size.linelen,
376 cache->arch[cl].d_u_size.cachesize,
377 cache->arch[cl].d_u_size.associativity);
378 }
379
380 /* separate i-cache at this level ? */
381 if (ctype & CACHE_LEVEL_HAS_I_CACHE) {
382 /* retrieve i-cache info */
383 retval = get_cache_info(dpm, cl, 1, &cache_reg);
384 if (retval != ERROR_OK)
385 goto done;
386 cache->arch[cl].i_size = decode_cache_reg(cache_reg);
387
388 LOG_DEBUG("instruction cache index %d << %d, way %d << %d",
389 cache->arch[cl].i_size.index,
390 cache->arch[cl].i_size.index_shift,
391 cache->arch[cl].i_size.way,
392 cache->arch[cl].i_size.way_shift);
393
394 LOG_DEBUG("cacheline %d bytes %d KBytes asso %d ways",
395 cache->arch[cl].i_size.linelen,
396 cache->arch[cl].i_size.cachesize,
397 cache->arch[cl].i_size.associativity);
398 }
399
400 cache->arch[cl].ctype = ctype;
401 }
402
403 /* restore selected cache */
404 dpm->instr_write_data_r0(dpm,
405 armv8_opcode(armv8, WRITE_REG_CSSELR), csselr);
406 if (retval != ERROR_OK)
407 goto done;
408
409 armv8->armv8_mmu.armv8_cache.info = 1;
410
411 /* if no l2 cache initialize l1 data cache flush function function */
412 if (armv8->armv8_mmu.armv8_cache.flush_all_data_cache == NULL) {
413 armv8->armv8_mmu.armv8_cache.display_cache_info =
414 armv8_handle_inner_cache_info_command;
415 armv8->armv8_mmu.armv8_cache.flush_all_data_cache =
416 armv8_flush_all_data;
417 }
418
419 done:
420 dpm->finish(dpm);
421 return retval;
422
423 }

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)