1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /***************************************************************************
4 * Copyright (C) 2016 by Matthias Welwarsky *
5 * matthias.welwarsky@sysgo.com *
6 ***************************************************************************/
12 #include "armv8_cache.h"
13 #include "armv8_dpm.h"
14 #include "armv8_opcodes.h"
17 /* CLIDR cache types */
18 #define CACHE_LEVEL_HAS_UNIFIED_CACHE 0x4
19 #define CACHE_LEVEL_HAS_D_CACHE 0x2
20 #define CACHE_LEVEL_HAS_I_CACHE 0x1
22 static int armv8_d_cache_sanity_check(struct armv8_common
*armv8
)
24 struct armv8_cache_common
*armv8_cache
= &armv8
->armv8_mmu
.armv8_cache
;
26 if (armv8_cache
->d_u_cache_enabled
)
29 return ERROR_TARGET_INVALID
;
32 static int armv8_i_cache_sanity_check(struct armv8_common
*armv8
)
34 struct armv8_cache_common
*armv8_cache
= &armv8
->armv8_mmu
.armv8_cache
;
36 if (armv8_cache
->i_cache_enabled
)
39 return ERROR_TARGET_INVALID
;
42 static int armv8_cache_d_inner_flush_level(struct armv8_common
*armv8
, struct armv8_cachesize
*size
, int cl
)
44 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
45 int retval
= ERROR_OK
;
46 int32_t c_way
, c_index
= size
->index
;
48 LOG_DEBUG("cl %" PRId32
, cl
);
52 uint32_t value
= (c_index
<< size
->index_shift
)
53 | (c_way
<< size
->way_shift
) | (cl
<< 1);
55 * DC CISW - Clean and invalidate data cache
58 retval
= dpm
->instr_write_data_r0(dpm
,
59 armv8_opcode(armv8
, ARMV8_OPC_DCCISW
), value
);
60 if (retval
!= ERROR_OK
)
65 } while (c_index
>= 0);
71 static int armv8_cache_d_inner_clean_inval_all(struct armv8_common
*armv8
)
73 struct armv8_cache_common
*cache
= &(armv8
->armv8_mmu
.armv8_cache
);
74 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
78 retval
= armv8_d_cache_sanity_check(armv8
);
79 if (retval
!= ERROR_OK
)
82 retval
= dpm
->prepare(dpm
);
83 if (retval
!= ERROR_OK
)
86 for (cl
= 0; cl
< cache
->loc
; cl
++) {
87 /* skip i-only caches */
88 if (cache
->arch
[cl
].ctype
< CACHE_LEVEL_HAS_D_CACHE
)
91 armv8_cache_d_inner_flush_level(armv8
, &cache
->arch
[cl
].d_u_size
, cl
);
94 retval
= dpm
->finish(dpm
);
98 LOG_ERROR("clean invalidate failed");
104 int armv8_cache_d_inner_flush_virt(struct armv8_common
*armv8
, target_addr_t va
, size_t size
)
106 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
107 struct armv8_cache_common
*armv8_cache
= &armv8
->armv8_mmu
.armv8_cache
;
108 uint64_t linelen
= armv8_cache
->dminline
;
109 target_addr_t va_line
, va_end
;
112 retval
= armv8_d_cache_sanity_check(armv8
);
113 if (retval
!= ERROR_OK
)
116 retval
= dpm
->prepare(dpm
);
117 if (retval
!= ERROR_OK
)
120 va_line
= va
& (-linelen
);
123 while (va_line
< va_end
) {
125 /* Aarch32: DCCIMVAC: ARMV4_5_MCR(15, 0, 0, 7, 14, 1) */
126 retval
= dpm
->instr_write_data_r0_64(dpm
,
127 armv8_opcode(armv8
, ARMV8_OPC_DCCIVAC
), va_line
);
128 if (retval
!= ERROR_OK
)
137 LOG_ERROR("d-cache invalidate failed");
143 int armv8_cache_i_inner_inval_virt(struct armv8_common
*armv8
, target_addr_t va
, size_t size
)
145 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
146 struct armv8_cache_common
*armv8_cache
= &armv8
->armv8_mmu
.armv8_cache
;
147 uint64_t linelen
= armv8_cache
->iminline
;
148 target_addr_t va_line
, va_end
;
151 retval
= armv8_i_cache_sanity_check(armv8
);
152 if (retval
!= ERROR_OK
)
155 retval
= dpm
->prepare(dpm
);
156 if (retval
!= ERROR_OK
)
159 va_line
= va
& (-linelen
);
162 while (va_line
< va_end
) {
163 /* IC IVAU - Invalidate instruction cache by VA to PoU. */
164 retval
= dpm
->instr_write_data_r0_64(dpm
,
165 armv8_opcode(armv8
, ARMV8_OPC_ICIVAU
), va_line
);
166 if (retval
!= ERROR_OK
)
175 LOG_ERROR("d-cache invalidate failed");
181 static int armv8_handle_inner_cache_info_command(struct command_invocation
*cmd
,
182 struct armv8_cache_common
*armv8_cache
)
186 if (armv8_cache
->info
== -1) {
187 command_print(cmd
, "cache not yet identified");
191 for (cl
= 0; cl
< armv8_cache
->loc
; cl
++) {
192 struct armv8_arch_cache
*arch
= &(armv8_cache
->arch
[cl
]);
194 if (arch
->ctype
& 1) {
196 "L%d I-Cache: linelen %" PRIu32
197 ", associativity %" PRIu32
199 ", cachesize %" PRIu32
" KBytes",
201 arch
->i_size
.linelen
,
202 arch
->i_size
.associativity
,
204 arch
->i_size
.cachesize
);
207 if (arch
->ctype
>= 2) {
209 "L%d D-Cache: linelen %" PRIu32
210 ", associativity %" PRIu32
212 ", cachesize %" PRIu32
" KBytes",
214 arch
->d_u_size
.linelen
,
215 arch
->d_u_size
.associativity
,
216 arch
->d_u_size
.nsets
,
217 arch
->d_u_size
.cachesize
);
224 static int _armv8_flush_all_data(struct target
*target
)
226 return armv8_cache_d_inner_clean_inval_all(target_to_armv8(target
));
229 static int armv8_flush_all_data(struct target
*target
)
231 int retval
= ERROR_FAIL
;
232 /* check that armv8_cache is correctly identify */
233 struct armv8_common
*armv8
= target_to_armv8(target
);
234 if (armv8
->armv8_mmu
.armv8_cache
.info
== -1) {
235 LOG_ERROR("trying to flush un-identified cache");
240 /* look if all the other target have been flushed in order to flush level
242 struct target_list
*head
;
243 foreach_smp_target(head
, target
->smp_targets
) {
244 struct target
*curr
= head
->target
;
245 if (curr
->state
== TARGET_HALTED
) {
246 LOG_INFO("Wait flushing data l1 on core %" PRId32
, curr
->coreid
);
247 retval
= _armv8_flush_all_data(curr
);
251 retval
= _armv8_flush_all_data(target
);
255 static int get_cache_info(struct arm_dpm
*dpm
, int cl
, int ct
, uint32_t *cache_reg
)
257 struct armv8_common
*armv8
= dpm
->arm
->arch_info
;
258 int retval
= ERROR_OK
;
260 /* select cache level */
261 retval
= dpm
->instr_write_data_r0(dpm
,
262 armv8_opcode(armv8
, WRITE_REG_CSSELR
),
263 (cl
<< 1) | (ct
== 1 ? 1 : 0));
264 if (retval
!= ERROR_OK
)
267 retval
= dpm
->instr_read_data_r0(dpm
,
268 armv8_opcode(armv8
, READ_REG_CCSIDR
),
274 static struct armv8_cachesize
decode_cache_reg(uint32_t cache_reg
)
276 struct armv8_cachesize size
;
279 size
.linelen
= 16 << (cache_reg
& 0x7);
280 size
.associativity
= ((cache_reg
>> 3) & 0x3ff) + 1;
281 size
.nsets
= ((cache_reg
>> 13) & 0x7fff) + 1;
282 size
.cachesize
= size
.linelen
* size
.associativity
* size
.nsets
/ 1024;
284 /* compute info for set way operation on cache */
285 size
.index_shift
= (cache_reg
& 0x7) + 4;
286 size
.index
= (cache_reg
>> 13) & 0x7fff;
287 size
.way
= ((cache_reg
>> 3) & 0x3ff);
289 while (((size
.way
<< i
) & 0x80000000) == 0)
296 int armv8_identify_cache(struct armv8_common
*armv8
)
298 /* read cache descriptor */
299 int retval
= ERROR_FAIL
;
300 struct arm
*arm
= &armv8
->arm
;
301 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
302 uint32_t csselr
, clidr
, ctr
;
305 struct armv8_cache_common
*cache
= &(armv8
->armv8_mmu
.armv8_cache
);
307 retval
= dpm
->prepare(dpm
);
308 if (retval
!= ERROR_OK
)
311 /* check if we're in an unprivileged mode */
312 if (armv8_curel_from_core_mode(arm
->core_mode
) < SYSTEM_CUREL_EL1
) {
313 retval
= armv8_dpm_modeswitch(dpm
, ARMV8_64_EL1H
);
314 if (retval
!= ERROR_OK
)
319 retval
= dpm
->instr_read_data_r0(dpm
,
320 armv8_opcode(armv8
, READ_REG_CTR
), &ctr
);
321 if (retval
!= ERROR_OK
)
324 cache
->iminline
= 4UL << (ctr
& 0xf);
325 cache
->dminline
= 4UL << ((ctr
& 0xf0000) >> 16);
326 LOG_DEBUG("ctr %" PRIx32
" ctr.iminline %" PRIu32
" ctr.dminline %" PRIu32
,
327 ctr
, cache
->iminline
, cache
->dminline
);
330 retval
= dpm
->instr_read_data_r0(dpm
,
331 armv8_opcode(armv8
, READ_REG_CLIDR
), &clidr
);
332 if (retval
!= ERROR_OK
)
335 cache
->loc
= (clidr
& 0x7000000) >> 24;
336 LOG_DEBUG("Number of cache levels to PoC %" PRId32
, cache
->loc
);
338 /* retrieve selected cache for later restore
339 * MRC p15, 2,<Rd>, c0, c0, 0; Read CSSELR */
340 retval
= dpm
->instr_read_data_r0(dpm
,
341 armv8_opcode(armv8
, READ_REG_CSSELR
), &csselr
);
342 if (retval
!= ERROR_OK
)
345 /* retrieve all available inner caches */
346 for (cl
= 0; cl
< cache
->loc
; clidr
>>= 3, cl
++) {
348 /* isolate cache type at current level */
351 /* skip reserved values */
352 if (ctype
> CACHE_LEVEL_HAS_UNIFIED_CACHE
)
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
)
361 cache
->arch
[cl
].d_u_size
= decode_cache_reg(cache_reg
);
363 LOG_DEBUG("data/unified cache index %" PRIu32
" << %" PRIu32
", way %" PRIu32
" << %" PRIu32
,
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
);
369 LOG_DEBUG("cacheline %" PRIu32
" bytes %" PRIu32
" KBytes asso %" PRIu32
" 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
);
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
)
381 cache
->arch
[cl
].i_size
= decode_cache_reg(cache_reg
);
383 LOG_DEBUG("instruction cache index %" PRIu32
" << %" PRIu32
", way %" PRIu32
" << %" PRIu32
,
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
);
389 LOG_DEBUG("cacheline %" PRIu32
" bytes %" PRIu32
" KBytes asso %" PRIu32
" ways",
390 cache
->arch
[cl
].i_size
.linelen
,
391 cache
->arch
[cl
].i_size
.cachesize
,
392 cache
->arch
[cl
].i_size
.associativity
);
395 cache
->arch
[cl
].ctype
= ctype
;
398 /* restore selected cache */
399 dpm
->instr_write_data_r0(dpm
,
400 armv8_opcode(armv8
, WRITE_REG_CSSELR
), csselr
);
401 if (retval
!= ERROR_OK
)
404 armv8
->armv8_mmu
.armv8_cache
.info
= 1;
406 /* if no l2 cache initialize l1 data cache flush function function */
407 if (!armv8
->armv8_mmu
.armv8_cache
.flush_all_data_cache
) {
408 armv8
->armv8_mmu
.armv8_cache
.display_cache_info
=
409 armv8_handle_inner_cache_info_command
;
410 armv8
->armv8_mmu
.armv8_cache
.flush_all_data_cache
=
411 armv8_flush_all_data
;
415 armv8_dpm_modeswitch(dpm
, ARM_MODE_ANY
);
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)