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

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)