aarch64: fix software breakpoints when in aarch32 state
[fw/openocd] / 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 }