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);
290 while (((size
.way
<< i
) & 0x80000000) == 0)
297 int armv8_identify_cache(struct armv8_common
*armv8
)
299 /* read cache descriptor */
300 int retval
= ERROR_FAIL
;
301 struct arm
*arm
= &armv8
->arm
;
302 struct arm_dpm
*dpm
= armv8
->arm
.dpm
;
303 uint32_t csselr
, clidr
, ctr
;
306 struct armv8_cache_common
*cache
= &(armv8
->armv8_mmu
.armv8_cache
);
308 retval
= dpm
->prepare(dpm
);
309 if (retval
!= ERROR_OK
)
312 /* check if we're in an unprivileged mode */
313 if (armv8_curel_from_core_mode(arm
->core_mode
) < SYSTEM_CUREL_EL1
) {
314 retval
= armv8_dpm_modeswitch(dpm
, ARMV8_64_EL1H
);
315 if (retval
!= ERROR_OK
)
320 retval
= dpm
->instr_read_data_r0(dpm
,
321 armv8_opcode(armv8
, READ_REG_CTR
), &ctr
);
322 if (retval
!= ERROR_OK
)
325 cache
->iminline
= 4UL << (ctr
& 0xf);
326 cache
->dminline
= 4UL << ((ctr
& 0xf0000) >> 16);
327 LOG_DEBUG("ctr %" PRIx32
" ctr.iminline %" PRIu32
" ctr.dminline %" PRIu32
,
328 ctr
, cache
->iminline
, cache
->dminline
);
331 retval
= dpm
->instr_read_data_r0(dpm
,
332 armv8_opcode(armv8
, READ_REG_CLIDR
), &clidr
);
333 if (retval
!= ERROR_OK
)
336 cache
->loc
= (clidr
& 0x7000000) >> 24;
337 LOG_DEBUG("Number of cache levels to PoC %" PRId32
, cache
->loc
);
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
,
342 armv8_opcode(armv8
, READ_REG_CSSELR
), &csselr
);
343 if (retval
!= ERROR_OK
)
346 /* retrieve all available inner caches */
347 for (cl
= 0; cl
< cache
->loc
; clidr
>>= 3, cl
++) {
349 /* isolate cache type at current level */
352 /* skip reserved values */
353 if (ctype
> CACHE_LEVEL_HAS_UNIFIED_CACHE
)
356 /* separate d or unified d/i cache at this level ? */
357 if (ctype
& (CACHE_LEVEL_HAS_UNIFIED_CACHE
| CACHE_LEVEL_HAS_D_CACHE
)) {
358 /* retrieve d-cache info */
359 retval
= get_cache_info(dpm
, cl
, 0, &cache_reg
);
360 if (retval
!= ERROR_OK
)
362 cache
->arch
[cl
].d_u_size
= decode_cache_reg(cache_reg
);
364 LOG_DEBUG("data/unified cache index %" PRIu32
" << %" PRIu32
", way %" PRIu32
" << %" PRIu32
,
365 cache
->arch
[cl
].d_u_size
.index
,
366 cache
->arch
[cl
].d_u_size
.index_shift
,
367 cache
->arch
[cl
].d_u_size
.way
,
368 cache
->arch
[cl
].d_u_size
.way_shift
);
370 LOG_DEBUG("cacheline %" PRIu32
" bytes %" PRIu32
" KBytes asso %" PRIu32
" ways",
371 cache
->arch
[cl
].d_u_size
.linelen
,
372 cache
->arch
[cl
].d_u_size
.cachesize
,
373 cache
->arch
[cl
].d_u_size
.associativity
);
376 /* separate i-cache at this level ? */
377 if (ctype
& CACHE_LEVEL_HAS_I_CACHE
) {
378 /* retrieve i-cache info */
379 retval
= get_cache_info(dpm
, cl
, 1, &cache_reg
);
380 if (retval
!= ERROR_OK
)
382 cache
->arch
[cl
].i_size
= decode_cache_reg(cache_reg
);
384 LOG_DEBUG("instruction cache index %" PRIu32
" << %" PRIu32
", way %" PRIu32
" << %" PRIu32
,
385 cache
->arch
[cl
].i_size
.index
,
386 cache
->arch
[cl
].i_size
.index_shift
,
387 cache
->arch
[cl
].i_size
.way
,
388 cache
->arch
[cl
].i_size
.way_shift
);
390 LOG_DEBUG("cacheline %" PRIu32
" bytes %" PRIu32
" KBytes asso %" PRIu32
" ways",
391 cache
->arch
[cl
].i_size
.linelen
,
392 cache
->arch
[cl
].i_size
.cachesize
,
393 cache
->arch
[cl
].i_size
.associativity
);
396 cache
->arch
[cl
].ctype
= ctype
;
399 /* restore selected cache */
400 dpm
->instr_write_data_r0(dpm
,
401 armv8_opcode(armv8
, WRITE_REG_CSSELR
), csselr
);
402 if (retval
!= ERROR_OK
)
405 armv8
->armv8_mmu
.armv8_cache
.info
= 1;
407 /* if no l2 cache initialize l1 data cache flush function function */
408 if (!armv8
->armv8_mmu
.armv8_cache
.flush_all_data_cache
) {
409 armv8
->armv8_mmu
.armv8_cache
.display_cache_info
=
410 armv8_handle_inner_cache_info_command
;
411 armv8
->armv8_mmu
.armv8_cache
.flush_all_data_cache
=
412 armv8_flush_all_data
;
416 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)