ARM: implement mrc()/mcr() as DPM ops
[openocd.git] / src / target / arm_dpm.c
1 /*
2 * Copyright (C) 2009 by David Brownell
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "armv4_5.h" /* REVISIT to become arm.h */
25 #include "arm_dpm.h"
26 #include "jtag.h"
27 #include "register.h"
28
29
30 /**
31 * @file
32 * Implements various ARM DPM operations using architectural debug registers.
33 * These routines layer over core-specific communication methods to cope with
34 * implementation differences between cores like ARM1136 and Cortex-A8.
35 */
36
37 /*
38 * Coprocessor support
39 */
40
41 /* Read coprocessor */
42 static int dpm_mrc(struct target *target, int cpnum,
43 uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
44 uint32_t *value)
45 {
46 struct arm *arm = target_to_arm(target);
47 struct arm_dpm *dpm = arm->dpm;
48 int retval;
49
50 retval = dpm->prepare(dpm);
51 if (retval != ERROR_OK)
52 return retval;
53
54 LOG_DEBUG("MRC p%d, %d, r0, c%d, c%d, %d", cpnum, op1, CRn, CRm, op2);
55
56 /* read coprocessor register into R0; return via DCC */
57 retval = dpm->instr_read_data_r0(dpm,
58 ARMV4_5_MRC(cpnum, op1, 0, CRn, CRm, op2),
59 value);
60
61 /* (void) */ dpm->finish(dpm);
62 return retval;
63 }
64
65 static int dpm_mcr(struct target *target, int cpnum,
66 uint32_t op1, uint32_t op2, uint32_t CRn, uint32_t CRm,
67 uint32_t value)
68 {
69 struct arm *arm = target_to_arm(target);
70 struct arm_dpm *dpm = arm->dpm;
71 int retval;
72
73 retval = dpm->prepare(dpm);
74 if (retval != ERROR_OK)
75 return retval;
76
77 LOG_DEBUG("MCR p%d, %d, r0, c%d, c%d, %d", cpnum, op1, CRn, CRm, op2);
78
79 /* read DCC into r0; then write coprocessor register from R0 */
80 retval = dpm->instr_write_data_r0(dpm,
81 ARMV4_5_MCR(cpnum, op1, 0, CRn, CRm, op2),
82 value);
83
84 /* (void) */ dpm->finish(dpm);
85 return retval;
86 }
87
88 /*
89 * Register access utilities
90 */
91
92 /* Toggles between recorded core mode (USR, SVC, etc) and a temporary one.
93 * Routines *must* restore the original mode before returning!!
94 */
95 static int dpm_modeswitch(struct arm_dpm *dpm, enum armv4_5_mode mode)
96 {
97 int retval;
98 uint32_t cpsr;
99
100 /* restore previous mode */
101 if (mode == ARMV4_5_MODE_ANY)
102 cpsr = buf_get_u32(dpm->arm->cpsr->value, 0, 32);
103
104 /* else force to the specified mode */
105 else
106 cpsr = mode;
107
108 retval = dpm->instr_write_data_r0(dpm, ARMV4_5_MSR_GP(0, 0xf, 0), cpsr);
109
110 if (dpm->instr_cpsr_sync)
111 retval = dpm->instr_cpsr_sync(dpm);
112
113 return retval;
114 }
115
116 /* just read the register -- rely on the core mode being right */
117 static int dpm_read_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
118 {
119 uint32_t value;
120 int retval;
121
122 switch (regnum) {
123 case 0 ... 14:
124 /* return via DCC: "MCR p14, 0, Rnum, c0, c5, 0" */
125 retval = dpm->instr_read_data_dcc(dpm,
126 ARMV4_5_MCR(14, 0, regnum, 0, 5, 0),
127 &value);
128 break;
129 case 15: /* PC */
130 /* "MOV r0, pc"; then return via DCC */
131 retval = dpm->instr_read_data_r0(dpm, 0xe1a0000f, &value);
132
133 /* NOTE: this seems like a slightly awkward place to update
134 * this value ... but if the PC gets written (the only way
135 * to change what we compute), the arch spec says subsequent
136 * reads return values which are "unpredictable". So this
137 * is always right except in those broken-by-intent cases.
138 */
139 switch (dpm->arm->core_state) {
140 case ARMV4_5_STATE_ARM:
141 value -= 8;
142 break;
143 case ARMV4_5_STATE_THUMB:
144 case ARM_STATE_THUMB_EE:
145 value -= 4;
146 break;
147 case ARMV4_5_STATE_JAZELLE:
148 /* core-specific ... ? */
149 LOG_WARNING("Jazelle PC adjustment unknown");
150 break;
151 }
152 break;
153 default:
154 /* 16: "MRS r0, CPSR"; then return via DCC
155 * 17: "MRS r0, SPSR"; then return via DCC
156 */
157 retval = dpm->instr_read_data_r0(dpm,
158 ARMV4_5_MRS(0, regnum & 1),
159 &value);
160 break;
161 }
162
163 if (retval == ERROR_OK) {
164 buf_set_u32(r->value, 0, 32, value);
165 r->valid = true;
166 r->dirty = false;
167 LOG_DEBUG("READ: %s, %8.8x", r->name, (unsigned) value);
168 }
169
170 return retval;
171 }
172
173 /* just write the register -- rely on the core mode being right */
174 static int dpm_write_reg(struct arm_dpm *dpm, struct reg *r, unsigned regnum)
175 {
176 int retval;
177 uint32_t value = buf_get_u32(r->value, 0, 32);
178
179 switch (regnum) {
180 case 0 ... 14:
181 /* load register from DCC: "MCR p14, 0, Rnum, c0, c5, 0" */
182 retval = dpm->instr_write_data_dcc(dpm,
183 ARMV4_5_MRC(14, 0, regnum, 0, 5, 0),
184 value);
185 break;
186 case 15: /* PC */
187 /* read r0 from DCC; then "MOV pc, r0" */
188 retval = dpm->instr_write_data_r0(dpm, 0xe1a0f000, value);
189 break;
190 default:
191 /* 16: read r0 from DCC, then "MSR r0, CPSR_cxsf"
192 * 17: read r0 from DCC, then "MSR r0, SPSR_cxsf"
193 */
194 retval = dpm->instr_write_data_r0(dpm,
195 ARMV4_5_MSR_GP(0, 0xf, regnum & 1),
196 value);
197
198 if (regnum == 16 && dpm->instr_cpsr_sync)
199 retval = dpm->instr_cpsr_sync(dpm);
200
201 break;
202 }
203
204 if (retval == ERROR_OK) {
205 r->dirty = false;
206 LOG_DEBUG("WRITE: %s, %8.8x", r->name, (unsigned) value);
207 }
208
209 return retval;
210 }
211
212 /**
213 * Read basic registers of the the current context: R0 to R15, and CPSR;
214 * sets the core mode (such as USR or IRQ) and state (such as ARM or Thumb).
215 * In normal operation this is called on entry to halting debug state,
216 * possibly after some other operations supporting restore of debug state
217 * or making sure the CPU is fully idle (drain write buffer, etc).
218 */
219 int arm_dpm_read_current_registers(struct arm_dpm *dpm)
220 {
221 struct arm *arm = dpm->arm;
222 uint32_t cpsr;
223 int retval;
224 struct reg *r;
225
226 retval = dpm->prepare(dpm);
227 if (retval != ERROR_OK)
228 return retval;
229
230 /* read R0 first (it's used for scratch), then CPSR */
231 r = arm->core_cache->reg_list + 0;
232 if (!r->valid) {
233 retval = dpm_read_reg(dpm, r, 0);
234 if (retval != ERROR_OK)
235 goto fail;
236 }
237 r->dirty = true;
238
239 retval = dpm->instr_read_data_r0(dpm, ARMV4_5_MRS(0, 0), &cpsr);
240 if (retval != ERROR_OK)
241 goto fail;
242
243 /* update core mode and state, plus shadow mapping for R8..R14 */
244 arm_set_cpsr(arm, cpsr);
245
246 /* REVISIT we can probably avoid reading R1..R14, saving time... */
247 for (unsigned i = 1; i < 16; i++) {
248 r = arm_reg_current(arm, i);
249 if (r->valid)
250 continue;
251
252 retval = dpm_read_reg(dpm, r, i);
253 if (retval != ERROR_OK)
254 goto fail;
255 }
256
257 /* NOTE: SPSR ignored (if it's even relevant). */
258
259 fail:
260 /* (void) */ dpm->finish(dpm);
261 return retval;
262 }
263
264 /**
265 * Writes all modified core registers for all processor modes. In normal
266 * operation this is called on exit from halting debug state.
267 */
268 int arm_dpm_write_dirty_registers(struct arm_dpm *dpm)
269 {
270 struct arm *arm = dpm->arm;
271 struct reg_cache *cache = arm->core_cache;
272 int retval;
273 bool did_write;
274
275 retval = dpm->prepare(dpm);
276 if (retval != ERROR_OK)
277 goto done;
278
279 /* Scan the registers until we find one that's both dirty and
280 * eligible for flushing. Flush that and everything else that
281 * shares the same core mode setting. Typically this won't
282 * actually find anything to do...
283 */
284 do {
285 enum armv4_5_mode mode = ARMV4_5_MODE_ANY;
286
287 did_write = false;
288
289 /* check everything except our scratch register R0 */
290 for (unsigned i = 1; i < cache->num_regs; i++) {
291 struct arm_reg *r;
292 unsigned regnum;
293
294 /* also skip PC, CPSR, and non-dirty */
295 if (i == 15)
296 continue;
297 if (arm->cpsr == cache->reg_list + i)
298 continue;
299 if (!cache->reg_list[i].dirty)
300 continue;
301
302 r = cache->reg_list[i].arch_info;
303 regnum = r->num;
304
305 /* may need to pick and set a mode */
306 if (!did_write) {
307 enum armv4_5_mode tmode;
308
309 did_write = true;
310 mode = tmode = r->mode;
311
312 /* cope with special cases */
313 switch (regnum) {
314 case 8 ... 12:
315 /* r8..r12 "anything but FIQ" case;
316 * we "know" core mode is accurate
317 * since we haven't changed it yet
318 */
319 if (arm->core_mode == ARMV4_5_MODE_FIQ
320 && ARMV4_5_MODE_ANY
321 != mode)
322 tmode = ARMV4_5_MODE_USR;
323 break;
324 case 16:
325 /* SPSR */
326 regnum++;
327 break;
328 }
329
330 /* REVISIT error checks */
331 if (tmode != ARMV4_5_MODE_ANY)
332 retval = dpm_modeswitch(dpm, tmode);
333 }
334 if (r->mode != mode)
335 continue;
336
337 retval = dpm_write_reg(dpm,
338 &cache->reg_list[i],
339 regnum);
340
341 }
342
343 } while (did_write);
344
345 /* Restore original CPSR ... assuming either that we changed it,
346 * or it's dirty. Must write PC to ensure the return address is
347 * defined, and must not write it before CPSR.
348 */
349 retval = dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
350 arm->cpsr->dirty = false;
351
352 retval = dpm_write_reg(dpm, &cache->reg_list[15], 15);
353 cache->reg_list[15].dirty = false;
354
355 /* flush R0 -- it's *very* dirty by now */
356 retval = dpm_write_reg(dpm, &cache->reg_list[0], 0);
357 cache->reg_list[0].dirty = false;
358
359 /* (void) */ dpm->finish(dpm);
360 done:
361 return retval;
362 }
363
364 /* Returns ARMV4_5_MODE_ANY or temporary mode to use while reading the
365 * specified register ... works around flakiness from ARM core calls.
366 * Caller already filtered out SPSR access; mode is never MODE_SYS
367 * or MODE_ANY.
368 */
369 static enum armv4_5_mode dpm_mapmode(struct arm *arm,
370 unsigned num, enum armv4_5_mode mode)
371 {
372 enum armv4_5_mode amode = arm->core_mode;
373
374 /* don't switch if the mode is already correct */
375 if (amode == ARMV4_5_MODE_SYS)
376 amode = ARMV4_5_MODE_USR;
377 if (mode == amode)
378 return ARMV4_5_MODE_ANY;
379
380 switch (num) {
381 /* don't switch for non-shadowed registers (r0..r7, r15/pc, cpsr) */
382 case 0 ... 7:
383 case 15:
384 case 16:
385 break;
386 /* r8..r12 aren't shadowed for anything except FIQ */
387 case 8 ... 12:
388 if (mode == ARMV4_5_MODE_FIQ)
389 return mode;
390 break;
391 /* r13/sp, and r14/lr are always shadowed */
392 case 13:
393 case 14:
394 return mode;
395 default:
396 LOG_WARNING("invalid register #%u", num);
397 break;
398 }
399 return ARMV4_5_MODE_ANY;
400 }
401
402 static int arm_dpm_read_core_reg(struct target *target, struct reg *r,
403 int regnum, enum armv4_5_mode mode)
404 {
405 struct arm_dpm *dpm = target_to_arm(target)->dpm;
406 int retval;
407
408 if (regnum < 0 || regnum > 16)
409 return ERROR_INVALID_ARGUMENTS;
410
411 if (regnum == 16) {
412 if (mode != ARMV4_5_MODE_ANY)
413 regnum = 17;
414 } else
415 mode = dpm_mapmode(dpm->arm, regnum, mode);
416
417 /* REVISIT what happens if we try to read SPSR in a core mode
418 * which has no such register?
419 */
420
421 retval = dpm->prepare(dpm);
422 if (retval != ERROR_OK)
423 return retval;
424
425 if (mode != ARMV4_5_MODE_ANY) {
426 retval = dpm_modeswitch(dpm, mode);
427 if (retval != ERROR_OK)
428 goto fail;
429 }
430
431 retval = dpm_read_reg(dpm, r, regnum);
432 /* always clean up, regardless of error */
433
434 if (mode != ARMV4_5_MODE_ANY)
435 /* (void) */ dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
436
437 fail:
438 /* (void) */ dpm->finish(dpm);
439 return retval;
440 }
441
442 static int arm_dpm_write_core_reg(struct target *target, struct reg *r,
443 int regnum, enum armv4_5_mode mode, uint32_t value)
444 {
445 struct arm_dpm *dpm = target_to_arm(target)->dpm;
446 int retval;
447
448
449 if (regnum < 0 || regnum > 16)
450 return ERROR_INVALID_ARGUMENTS;
451
452 if (regnum == 16) {
453 if (mode != ARMV4_5_MODE_ANY)
454 regnum = 17;
455 } else
456 mode = dpm_mapmode(dpm->arm, regnum, mode);
457
458 /* REVISIT what happens if we try to write SPSR in a core mode
459 * which has no such register?
460 */
461
462 retval = dpm->prepare(dpm);
463 if (retval != ERROR_OK)
464 return retval;
465
466 if (mode != ARMV4_5_MODE_ANY) {
467 retval = dpm_modeswitch(dpm, mode);
468 if (retval != ERROR_OK)
469 goto fail;
470 }
471
472 retval = dpm_write_reg(dpm, r, regnum);
473 /* always clean up, regardless of error */
474
475 if (mode != ARMV4_5_MODE_ANY)
476 /* (void) */ dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
477
478 fail:
479 /* (void) */ dpm->finish(dpm);
480 return retval;
481 }
482
483 static int arm_dpm_full_context(struct target *target)
484 {
485 struct arm *arm = target_to_arm(target);
486 struct arm_dpm *dpm = arm->dpm;
487 struct reg_cache *cache = arm->core_cache;
488 int retval;
489 bool did_read;
490
491 retval = dpm->prepare(dpm);
492 if (retval != ERROR_OK)
493 goto done;
494
495 do {
496 enum armv4_5_mode mode = ARMV4_5_MODE_ANY;
497
498 did_read = false;
499
500 /* We "know" arm_dpm_read_current_registers() was called so
501 * the unmapped registers (R0..R7, PC, AND CPSR) and some
502 * view of R8..R14 are current. We also "know" oddities of
503 * register mapping: special cases for R8..R12 and SPSR.
504 *
505 * Pick some mode with unread registers and read them all.
506 * Repeat until done.
507 */
508 for (unsigned i = 0; i < cache->num_regs; i++) {
509 struct arm_reg *r;
510
511 if (cache->reg_list[i].valid)
512 continue;
513 r = cache->reg_list[i].arch_info;
514
515 /* may need to pick a mode and set CPSR */
516 if (!did_read) {
517 did_read = true;
518 mode = r->mode;
519
520 /* For R8..R12 when we've entered debug
521 * state in FIQ mode... patch mode.
522 */
523 if (mode == ARMV4_5_MODE_ANY)
524 mode = ARMV4_5_MODE_USR;
525
526 /* REVISIT error checks */
527 retval = dpm_modeswitch(dpm, mode);
528 }
529 if (r->mode != mode)
530 continue;
531
532 /* CPSR was read, so "R16" must mean SPSR */
533 retval = dpm_read_reg(dpm,
534 &cache->reg_list[i],
535 (r->num == 16) ? 17 : r->num);
536
537 }
538
539 } while (did_read);
540
541 retval = dpm_modeswitch(dpm, ARMV4_5_MODE_ANY);
542 /* (void) */ dpm->finish(dpm);
543 done:
544 return retval;
545 }
546
547 /**
548 * Hooks up this DPM to its associated target; call only once.
549 * Initially this only covers the register cache.
550 */
551 int arm_dpm_setup(struct arm_dpm *dpm)
552 {
553 struct arm *arm = dpm->arm;
554 struct target *target = arm->target;
555 struct reg_cache *cache;
556
557 arm->dpm = dpm;
558
559 arm->full_context = arm_dpm_full_context;
560 arm->read_core_reg = arm_dpm_read_core_reg;
561 arm->write_core_reg = arm_dpm_write_core_reg;
562
563 cache = armv4_5_build_reg_cache(target, arm);
564 if (!cache)
565 return ERROR_FAIL;
566
567 *register_get_last_cache_p(&target->reg_cache) = cache;
568
569 arm->mrc = dpm_mrc;
570 arm->mcr = dpm_mcr;
571
572 return ERROR_OK;
573 }
574
575 /**
576 * Reinitializes DPM state at the beginning of a new debug session
577 * or after a reset which may have affected the debug module.
578 */
579 int arm_dpm_initialize(struct arm_dpm *dpm)
580 {
581 /* FIXME -- nothing yet */
582 return ERROR_OK;
583 }

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)