/* * Copyright (c) 2012, ARM Limited. All rights reserved. * * Redistribution and use in source and binary forms, with * or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * Redistributions in binary form must reproduce the * above copyright notice, this list of conditions and * the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its * contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. */ /* * The code to save and restore debug context uses the memory-mapped interface. * The registers that are saved are enough to support a self-hosted debugger, * but a different approach should be taken with an external debugger (cp14). */ #include "bakery.h" #include "helpers.h" #include "debug_helpers.h" #include "context.h" #define DIDR_VERSION_SHIFT 16 #define DIDR_VERSION_MASK 0xF #define DIDR_VERSION_7_1 5 #define DIDR_BP_SHIFT 24 #define DIDR_BP_MASK 0xF #define DIDR_WP_SHIFT 28 #define DIDR_WP_MASK 0xF #define CLAIMCLR_CLEAR_ALL 0xff #define DRAR_VALID_MASK 0x00000003 #define DSAR_VALID_MASK 0x00000003 #define DRAR_ADDRESS_MASK 0xFFFFF000 #define DSAR_ADDRESS_MASK 0xFFFFF000 #define OSLSR_OSLM_MASK 0x00000009 #define OSLAR_UNLOCKED 0x00000000 #define OSLAR_LOCKED 0xC5ACCE55 #define LAR_UNLOCKED 0xC5ACCE55 #define LAR_LOCKED 0x00000000 #define OSDLR_UNLOCKED 0x00000000 #define OSDLR_LOCKED 0x00000001 #define DBGREG_BP_VAL 0x0 #define DBGREG_WP_VAL 0x1 #define DBGREG_BP_CTRL 0x2 #define DBGREG_WP_CTRL 0x3 #define DBGREG_BP_XVAL 0x4 typedef volatile struct { /* Registers Save? */ unsigned const didr; /* 0 Read only */ unsigned dscr_i; /* 1 ignore - use dscr_e instead */ unsigned const dummy1[3]; /* 2-4 ignore */ unsigned dtrrx_dtrtx_i; /* 5 ignore */ unsigned wfar; /* 6 ignore - transient information */ unsigned vcr; /* 7 Save */ unsigned const dummy2; /* 8 ignore */ unsigned ecr; /* 9 ignore */ unsigned dsccr; /* 10 ignore */ unsigned dsmcr; /* 11 ignore */ unsigned const dummy3[20]; /* 12-31 ignore */ unsigned dtrrx_e; /* 32 ignore */ unsigned itr_pcsr; /* 33 ignore */ unsigned dscr_e; /* 34 Save */ unsigned dtrtx_e; /* 35 ignore */ unsigned drcr; /* 36 ignore */ unsigned eacr; /* 37 Save - V7.1 only */ unsigned const dummy4[2]; /* 38-39 ignore */ unsigned pcsr; /* 40 ignore */ unsigned cidsr; /* 41 ignore */ unsigned vidsr; /* 42 ignore */ unsigned const dummy5[21]; /* 43-63 ignore */ unsigned bvr[16]; /* 64-79 Save */ unsigned bcr[16]; /* 80-95 Save */ unsigned wvr[16]; /* 96-111 Save */ unsigned wcr[16]; /* 112-127 Save */ unsigned const dummy6[16]; /* 128-143 ignore */ unsigned bxvr[16]; /* 144-159 Save if have Virtualization extensions */ unsigned const dummy7[32]; /* 160-191 ignore */ unsigned oslar; /* 192 If oslsr[0] is 1, unlock before save/restore */ unsigned const oslsr; /* 193 ignore */ unsigned ossrr; /* 194 ignore */ unsigned const dummy8; /* 195 ignore */ unsigned prcr; /* 196 ignore */ unsigned prsr; /* 197 clear SPD on restore */ unsigned const dummy9[762]; /* 198-959 ignore */ unsigned itctrl; /* 960 ignore */ unsigned const dummy10[39]; /* 961-999 ignore */ unsigned claimset; /* 1000 Restore claim bits to here */ unsigned claimclr; /* 1001 Save claim bits from here */ unsigned const dummy11[2]; /* 1002-1003 ignore */ unsigned lar; /* 1004 Unlock before restore */ unsigned const lsr; /* 1005 ignore */ unsigned const authstatus; /* 1006 Read only */ unsigned const dummy12; /* 1007 ignore */ unsigned const devid2; /* 1008 Read only */ unsigned const devid1; /* 1009 Read only */ unsigned const devid; /* 1010 Read only */ unsigned const devtype; /* 1011 Read only */ unsigned const pid[8]; /* 1012-1019 Read only */ unsigned const cid[4]; /* 1020-1023 Read only */ } debug_registers_t; typedef struct { unsigned (*read) (void); void (*write) (unsigned); } rw_ops; typedef struct { rw_ops bvr; rw_ops bcr; rw_ops wvr; rw_ops wcr; rw_ops bxvr; } dbgreg_rw_ops; dbgreg_rw_ops dbgreg_rw_handlers[] = { { {read_dbg_bvr0, write_dbg_bvr0,}, {read_dbg_bcr0, write_dbg_bcr0,}, {read_dbg_wvr0, write_dbg_wvr0,}, {read_dbg_wcr0, write_dbg_wcr0,}, {read_dbg_bxvr0, write_dbg_bxvr0,}, }, { {read_dbg_bvr1, write_dbg_bvr1,}, {read_dbg_bcr1, write_dbg_bcr1,}, {read_dbg_wvr1, write_dbg_wvr1,}, {read_dbg_wcr1, write_dbg_wcr1,}, {read_dbg_bxvr1, write_dbg_bxvr1,}, }, { {read_dbg_bvr2, write_dbg_bvr2,}, {read_dbg_bcr2, write_dbg_bcr2,}, {read_dbg_wvr2, write_dbg_wvr2,}, {read_dbg_wcr2, write_dbg_wcr2,}, {read_dbg_bxvr2, write_dbg_bxvr2,}, }, { {read_dbg_bvr3, write_dbg_bvr3,}, {read_dbg_bcr3, write_dbg_bcr3,}, {read_dbg_wvr3, write_dbg_wvr3,}, {read_dbg_wcr3, write_dbg_wcr3,}, {read_dbg_bxvr3, write_dbg_bxvr3,}, }, { {read_dbg_bvr4, write_dbg_bvr4,}, {read_dbg_bcr4, write_dbg_bcr4,}, {read_dbg_wvr4, write_dbg_wvr4,}, {read_dbg_wcr4, write_dbg_wcr4,}, {read_dbg_bxvr4, write_dbg_bxvr4,}, }, { {read_dbg_bvr5, write_dbg_bvr5,}, {read_dbg_bcr5, write_dbg_bcr5,}, {read_dbg_wvr5, write_dbg_wvr5,}, {read_dbg_wcr5, write_dbg_wcr5,}, {read_dbg_bxvr5, write_dbg_bxvr5,}, }, { {read_dbg_bvr6, write_dbg_bvr6,}, {read_dbg_bcr6, write_dbg_bcr6,}, {read_dbg_wvr6, write_dbg_wvr6,}, {read_dbg_wcr6, write_dbg_wcr6,}, {read_dbg_bxvr6, write_dbg_bxvr6,}, }, { {read_dbg_bvr7, write_dbg_bvr7,}, {read_dbg_bcr7, write_dbg_bcr7,}, {read_dbg_wvr7, write_dbg_wvr7,}, {read_dbg_wcr7, write_dbg_wcr7,}, {read_dbg_bxvr7, write_dbg_bxvr7,}, }, { {read_dbg_bvr8, write_dbg_bvr8,}, {read_dbg_bcr8, write_dbg_bcr8,}, {read_dbg_wvr8, write_dbg_wvr8,}, {read_dbg_wcr8, write_dbg_wcr8,}, {read_dbg_bxvr8, write_dbg_bxvr8,}, }, { {read_dbg_bvr9, write_dbg_bvr9,}, {read_dbg_bcr9, write_dbg_bcr9,}, {read_dbg_wvr9, write_dbg_wvr9,}, {read_dbg_wcr9, write_dbg_wcr9,}, {read_dbg_bxvr9, write_dbg_bxvr9,}, }, { {read_dbg_bvr10, write_dbg_bvr10,}, {read_dbg_bcr10, write_dbg_bcr10,}, {read_dbg_wvr10, write_dbg_wvr10,}, {read_dbg_wcr10, write_dbg_wcr10,}, {read_dbg_bxvr10, write_dbg_bxvr10,}, }, { {read_dbg_bvr11, write_dbg_bvr11,}, {read_dbg_bcr11, write_dbg_bcr11,}, {read_dbg_wvr11, write_dbg_wvr11,}, {read_dbg_wcr11, write_dbg_wcr11,}, {read_dbg_bxvr11, write_dbg_bxvr11,}, }, { {read_dbg_bvr12, write_dbg_bvr12,}, {read_dbg_bcr12, write_dbg_bcr12,}, {read_dbg_wvr12, write_dbg_wvr12,}, {read_dbg_wcr12, write_dbg_wcr12,}, {read_dbg_bxvr12, write_dbg_bxvr12,}, }, { {read_dbg_bvr13, write_dbg_bvr13,}, {read_dbg_bcr13, write_dbg_bcr13,}, {read_dbg_wvr13, write_dbg_wvr13,}, {read_dbg_wcr13, write_dbg_wcr13,}, {read_dbg_bxvr13, write_dbg_bxvr13,}, }, { {read_dbg_bvr14, write_dbg_bvr14,}, {read_dbg_bcr14, write_dbg_bcr14,}, {read_dbg_wvr14, write_dbg_wvr14,}, {read_dbg_wcr14, write_dbg_wcr14,}, {read_dbg_bxvr14, write_dbg_bxvr14,}, }, { {read_dbg_bvr15, write_dbg_bvr15,}, {read_dbg_bcr15, write_dbg_bcr15,}, {read_dbg_wvr15, write_dbg_wvr15,}, {read_dbg_wcr15, write_dbg_wcr15,}, {read_dbg_bxvr15, write_dbg_bxvr15,}, }, }; static void restore_bp_reg(debug_context_t *dbg, unsigned index, unsigned type) { switch (type) { case DBGREG_WP_VAL: dbgreg_rw_handlers[index].wvr.write(dbg->wvr[index]); break; case DBGREG_WP_CTRL: dbgreg_rw_handlers[index].wcr.write(dbg->wcr[index]); break; case DBGREG_BP_XVAL: dbgreg_rw_handlers[index].bxvr.write(dbg->bxvr[index]); break; case DBGREG_BP_VAL: dbgreg_rw_handlers[index].bvr.write(dbg->bvr[index]); break; case DBGREG_BP_CTRL: dbgreg_rw_handlers[index].bcr.write(dbg->bcr[index]); break; default: break; } return; } static void save_bp_reg(debug_context_t *dbg, unsigned index, unsigned type) { switch (type) { case DBGREG_WP_VAL: dbg->wvr[index] = dbgreg_rw_handlers[index].wvr.read(); break; case DBGREG_WP_CTRL: dbg->wcr[index] = dbgreg_rw_handlers[index].wcr.read(); break; case DBGREG_BP_XVAL: dbg->bxvr[index] = dbgreg_rw_handlers[index].bxvr.read(); break; case DBGREG_BP_VAL: dbg->bvr[index] = dbgreg_rw_handlers[index].bvr.read(); break; case DBGREG_BP_CTRL: dbg->bcr[index] = dbgreg_rw_handlers[index].bcr.read(); break; default: break; } return; } static void sr_bp_context(debug_context_t *dbg, unsigned bp_type, unsigned op) { unsigned num_bps, num_ctx_cmps, num_wps, didr; unsigned index = 0, max_index = 0; didr = read_dbg_didr(); num_bps = (didr >> 24) & 0xf; num_ctx_cmps = (didr >> 20) & 0xf; num_wps = (didr >> 28) & 0xf; switch (bp_type) { case DBGREG_WP_VAL: case DBGREG_WP_CTRL: max_index = num_wps; break; case DBGREG_BP_XVAL: index = num_bps - num_ctx_cmps; case DBGREG_BP_VAL: case DBGREG_BP_CTRL: max_index = num_bps; break; default: break; } for (; index <= max_index; index++) if (op) save_bp_reg(dbg, index, bp_type); else restore_bp_reg(dbg, index, bp_type); return; } static void save_v71_debug_cp14(unsigned *context) { debug_context_t *dbg = (void *) context; unsigned virtext_present; /* * Unlock the Double lock. */ if (read_dbg_osdlr() == 0x1) write_dbg_osdlr(OSDLR_UNLOCKED); virtext_present = (read_dbg_devid() >> 16) & 0xf; /* * Prevent updates to the debug registers during a S&R operation */ write_dbg_oslar(OSLAR_LOCKED); dbg->dtrrx_e = read_dbg_dtrrxext(); dbg->dtrtx_e = read_dbg_dtrtxext(); dbg->dscr_e = read_dbg_dscrext(); dbg->wfar = read_dbg_wfar(); dbg->vcr = read_dbg_vcr(); dbg->claimclr = read_dbg_claimclr(); if (virtext_present) sr_bp_context(dbg, DBGREG_BP_XVAL, 1); sr_bp_context(dbg, DBGREG_BP_VAL, 1); sr_bp_context(dbg, DBGREG_BP_CTRL, 1); sr_bp_context(dbg, DBGREG_WP_VAL, 1); sr_bp_context(dbg, DBGREG_WP_CTRL, 1); write_dbg_osdlr(OSDLR_LOCKED); return; } static void restore_v71_debug_cp14(unsigned *context) { debug_context_t *dbg = (void *) context; unsigned virtext_present; /* * Unlock the Double lock. */ if (read_dbg_osdlr() == 0x1) write_dbg_osdlr(OSDLR_UNLOCKED); virtext_present = (read_dbg_devid() >> 16) & 0xf; /* * Prevent updates to the debug registers during a S&R operation */ write_dbg_oslar(OSLAR_LOCKED); write_dbg_dtrrxext(dbg->dtrrx_e); write_dbg_dtrtxext(dbg->dtrtx_e); write_dbg_dscrext(dbg->dscr_e); write_dbg_wfar(dbg->wfar); write_dbg_vcr(dbg->vcr); write_dbg_claimset(dbg->claimclr); if (virtext_present) sr_bp_context(dbg, DBGREG_BP_XVAL, 0); sr_bp_context(dbg, DBGREG_BP_VAL, 0); sr_bp_context(dbg, DBGREG_BP_CTRL, 0); sr_bp_context(dbg, DBGREG_WP_VAL, 0); sr_bp_context(dbg, DBGREG_WP_CTRL, 0); isb(); /* * Unlock access to the debug registers */ write_dbg_oslar(OSLAR_UNLOCKED); return; } debug_registers_t *read_debug_address(void) { unsigned drar, dsar; drar = read_dbg_drar(); dsar = read_dbg_dsar(); if (!(drar & DRAR_VALID_MASK) || !(dsar & DSAR_VALID_MASK)) { return 0; /* No memory-mapped debug on this processor */ } return (debug_registers_t *) ((drar & DRAR_ADDRESS_MASK) + (dsar & DSAR_ADDRESS_MASK)); } /* * We assume that before save (and after restore): * - OSLAR is NOT locked, or the debugger would not work properly * - LAR is locked, because the ARM ARM says it must be * - OSDLR is NOT locked, or the debugger would not work properly */ static void save_v7_debug_mmapped(unsigned *context) { debug_registers_t *dbg = (void *)read_debug_address(); debug_context_t *ctx = (void *)context; unsigned v71, num_bps, num_wps, i; unsigned didr, virtext_present = (read_id_pfr1() >> 12) & 0xf; if (!dbg) { return; } didr = dbg->didr; /* * Work out what version of debug we have */ v71 = (((didr >> DIDR_VERSION_SHIFT) & DIDR_VERSION_MASK) == DIDR_VERSION_7_1); /* * Save all context to memory */ ctx->vcr = dbg->vcr; ctx->dscr_e = dbg->dscr_e; ctx->claim = dbg->claimclr; if (v71) { ctx->eacr = dbg->eacr; } num_bps = 1 + ((didr >> DIDR_BP_SHIFT) & DIDR_BP_MASK); for (i = 0; i < num_bps; ++i) { ctx->bvr[i] = dbg->bvr[i]; ctx->bcr[i] = dbg->bcr[i]; if (virtext_present) ctx->bxvr[i] = dbg->bxvr[i]; } num_wps = 1 + ((didr >> DIDR_WP_SHIFT) & DIDR_WP_MASK); for (i = 0; i < num_wps; ++i) { ctx->wvr[i] = dbg->wvr[i]; ctx->wcr[i] = dbg->wcr[i]; } /* * If Debug V7.1, we must set osdlr (by cp14 interface) before power down. * Once we have done this, debug becomes inaccessible. */ if (v71) { write_dbg_osdlr(OSDLR_LOCKED); } } static void restore_v7_debug_mmapped(unsigned *context) { debug_registers_t *dbg = (void *)read_debug_address(); debug_context_t *ctx = (void *)context; unsigned v71, num_bps, num_wps, i; unsigned didr, virtext_present = (read_id_pfr1() >> 12) & 0xf;; if (!dbg) { return; } didr = dbg->didr; /* * Work out what version of debug we have */ v71 = (((didr >> DIDR_VERSION_SHIFT) & DIDR_VERSION_MASK) == DIDR_VERSION_7_1); /* Enable write access to registers */ dbg->lar = LAR_UNLOCKED; /* * If Debug V7.1, we must unset osdlr (by cp14 interface) before restoring. * (If the CPU has not actually power-cycled, osdlr may not be reset). */ if (v71) { write_dbg_osdlr(OSDLR_UNLOCKED); } /* * Restore all context from memory */ dbg->vcr = ctx->vcr; dbg->claimclr = CLAIMCLR_CLEAR_ALL; dbg->claimset = ctx->claim; if (v71) { dbg->eacr = ctx->eacr; } num_bps = 1 + ((didr >> DIDR_BP_SHIFT) & DIDR_BP_MASK); for (i = 0; i < num_bps; ++i) { dbg->bvr[i] = ctx->bvr[i]; dbg->bcr[i] = ctx->bcr[i]; if (virtext_present) dbg->bxvr[i] = ctx->bxvr[i]; } num_wps = 1 + ((didr >> DIDR_WP_SHIFT) & DIDR_WP_MASK); for (i = 0; i < num_wps; ++i) { dbg->wvr[i] = ctx->wvr[i]; dbg->wcr[i] = ctx->wcr[i]; } /* Clear PRSR.SPD by reading PRSR */ if (!v71) { (dbg->prsr); } /* Re-enable debug */ dbg->dscr_e = ctx->dscr_e; /* Disable write access to registers */ dbg->lar = LAR_LOCKED; } void save_v7_debug(unsigned *context) { unsigned v71 = 0, didr = read_dbg_didr(); v71 = (((didr >> DIDR_VERSION_SHIFT) & DIDR_VERSION_MASK) == DIDR_VERSION_7_1); /* * TODO: Code for saving the v7.0 Debug context through the * cp14 interface has not been implemented as yet. */ if (v71) save_v71_debug_cp14(context); else save_v7_debug_mmapped(context); return; } void restore_v7_debug(unsigned *context) { unsigned v71 = 0, didr = read_dbg_didr(); v71 = (((didr >> DIDR_VERSION_SHIFT) & DIDR_VERSION_MASK) == DIDR_VERSION_7_1); /* * TODO: Code for restoring the v7.0 Debug context through the * cp14 interface has not been implemented as yet. */ if (v71) restore_v71_debug_cp14(context); else restore_v7_debug_mmapped(context); return; }