Index: head/sys/arm/arm/debug_monitor.c =================================================================== --- head/sys/arm/arm/debug_monitor.c (revision 294986) +++ head/sys/arm/arm/debug_monitor.c (revision 294987) @@ -1,943 +1,1070 @@ /* * Copyright (c) 2015 Juniper Networks Inc. * All rights reserved. * * Developed by Semihalf. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include #include #include #include +#include #include +#include #include #include #include #include #include #include +#include #include #include #include enum dbg_t { DBG_TYPE_BREAKPOINT = 0, DBG_TYPE_WATCHPOINT = 1, }; struct dbg_wb_conf { enum dbg_t type; enum dbg_access_t access; db_addr_t address; db_expr_t size; u_int slot; }; static int dbg_reset_state(void); static int dbg_setup_breakpoint(db_expr_t, db_expr_t, u_int); static int dbg_remove_breakpoint(u_int); static u_int dbg_find_slot(enum dbg_t, db_expr_t); static boolean_t dbg_check_slot_free(enum dbg_t, u_int); static int dbg_remove_xpoint(struct dbg_wb_conf *); static int dbg_setup_xpoint(struct dbg_wb_conf *); static boolean_t dbg_capable; /* Indicates that machine is capable of using HW watchpoints/breakpoints */ static boolean_t dbg_ready[MAXCPU]; /* Debug arch. reset performed on this CPU */ static uint32_t dbg_model; /* Debug Arch. Model */ static boolean_t dbg_ossr; /* OS Save and Restore implemented */ static uint32_t dbg_watchpoint_num; static uint32_t dbg_breakpoint_num; -static int dbg_ref_count_mme[MAXCPU]; /* Times monitor mode was enabled */ +static int dbg_ref_count_mme; /* Times monitor mode was enabled */ /* ID_DFR0 - Debug Feature Register 0 */ #define ID_DFR0_CP_DEBUG_M_SHIFT 0 #define ID_DFR0_CP_DEBUG_M_MASK (0xF << ID_DFR0_CP_DEBUG_M_SHIFT) #define ID_DFR0_CP_DEBUG_M_NS (0x0) /* Not supported */ #define ID_DFR0_CP_DEBUG_M_V6 (0x2) /* v6 Debug arch. CP14 access */ #define ID_DFR0_CP_DEBUG_M_V6_1 (0x3) /* v6.1 Debug arch. CP14 access */ #define ID_DFR0_CP_DEBUG_M_V7 (0x4) /* v7 Debug arch. CP14 access */ #define ID_DFR0_CP_DEBUG_M_V7_1 (0x5) /* v7.1 Debug arch. CP14 access */ /* DBGDIDR - Debug ID Register */ #define DBGDIDR_WRPS_SHIFT 28 #define DBGDIDR_WRPS_MASK (0xF << DBGDIDR_WRPS_SHIFT) #define DBGDIDR_WRPS_NUM(reg) \ ((((reg) & DBGDIDR_WRPS_MASK) >> DBGDIDR_WRPS_SHIFT) + 1) #define DBGDIDR_BRPS_SHIFT 24 #define DBGDIDR_BRPS_MASK (0xF << DBGDIDR_BRPS_SHIFT) #define DBGDIDR_BRPS_NUM(reg) \ ((((reg) & DBGDIDR_BRPS_MASK) >> DBGDIDR_BRPS_SHIFT) + 1) /* DBGPRSR - Device Powerdown and Reset Status Register */ #define DBGPRSR_PU (1 << 0) /* Powerup status */ /* DBGOSLSR - OS Lock Status Register */ #define DBGOSLSR_OSLM0 (1 << 0) /* DBGOSDLR - OS Double Lock Register */ #define DBGPRSR_DLK (1 << 0) /* OS Double Lock set */ /* DBGDSCR - Debug Status and Control Register */ #define DBGSCR_MDBG_EN (1 << 15) /* Monitor debug-mode enable */ /* DBGWVR - Watchpoint Value Register */ #define DBGWVR_ADDR_MASK (~0x3U) /* Watchpoints/breakpoints control register bitfields */ #define DBG_WB_CTRL_LEN_1 (0x1 << 5) #define DBG_WB_CTRL_LEN_2 (0x3 << 5) #define DBG_WB_CTRL_LEN_4 (0xf << 5) #define DBG_WB_CTRL_LEN_8 (0xff << 5) #define DBG_WB_CTRL_LEN_MASK(x) ((x) & (0xff << 5)) #define DBG_WB_CTRL_EXEC (0x0 << 3) #define DBG_WB_CTRL_LOAD (0x1 << 3) #define DBG_WB_CTRL_STORE (0x2 << 3) #define DBG_WB_CTRL_ACCESS_MASK(x) ((x) & (0x3 << 3)) /* Common for breakpoint and watchpoint */ #define DBG_WB_CTRL_PL1 (0x1 << 1) #define DBG_WB_CTRL_PL0 (0x2 << 1) #define DBG_WB_CTRL_PLX_MASK(x) ((x) & (0x3 << 1)) #define DBG_WB_CTRL_E (0x1 << 0) /* * Watchpoint/breakpoint helpers */ #define DBG_BKPT_BT_SLOT 0 /* Slot for branch taken */ #define DBG_BKPT_BNT_SLOT 1 /* Slot for branch not taken */ #define OP2_SHIFT 4 /* Opc2 numbers for coprocessor instructions */ #define DBG_WB_BVR 4 #define DBG_WB_BCR 5 #define DBG_WB_WVR 6 #define DBG_WB_WCR 7 #define DBG_REG_BASE_BVR (DBG_WB_BVR << OP2_SHIFT) #define DBG_REG_BASE_BCR (DBG_WB_BCR << OP2_SHIFT) #define DBG_REG_BASE_WVR (DBG_WB_WVR << OP2_SHIFT) #define DBG_REG_BASE_WCR (DBG_WB_WCR << OP2_SHIFT) #define DBG_WB_READ(cn, cm, op2, val) do { \ __asm __volatile("mrc p14, 0, %0, " #cn "," #cm "," #op2 : "=r" (val)); \ } while (0) #define DBG_WB_WRITE(cn, cm, op2, val) do { \ __asm __volatile("mcr p14, 0, %0, " #cn "," #cm "," #op2 :: "r" (val)); \ } while (0) #define READ_WB_REG_CASE(op2, m, val) \ case (((op2) << OP2_SHIFT) + m): \ DBG_WB_READ(c0, c ## m, op2, val); \ break #define WRITE_WB_REG_CASE(op2, m, val) \ case (((op2) << OP2_SHIFT) + m): \ DBG_WB_WRITE(c0, c ## m, op2, val); \ break #define SWITCH_CASES_READ_WB_REG(op2, val) \ READ_WB_REG_CASE(op2, 0, val); \ READ_WB_REG_CASE(op2, 1, val); \ READ_WB_REG_CASE(op2, 2, val); \ READ_WB_REG_CASE(op2, 3, val); \ READ_WB_REG_CASE(op2, 4, val); \ READ_WB_REG_CASE(op2, 5, val); \ READ_WB_REG_CASE(op2, 6, val); \ READ_WB_REG_CASE(op2, 7, val); \ READ_WB_REG_CASE(op2, 8, val); \ READ_WB_REG_CASE(op2, 9, val); \ READ_WB_REG_CASE(op2, 10, val); \ READ_WB_REG_CASE(op2, 11, val); \ READ_WB_REG_CASE(op2, 12, val); \ READ_WB_REG_CASE(op2, 13, val); \ READ_WB_REG_CASE(op2, 14, val); \ READ_WB_REG_CASE(op2, 15, val) #define SWITCH_CASES_WRITE_WB_REG(op2, val) \ WRITE_WB_REG_CASE(op2, 0, val); \ WRITE_WB_REG_CASE(op2, 1, val); \ WRITE_WB_REG_CASE(op2, 2, val); \ WRITE_WB_REG_CASE(op2, 3, val); \ WRITE_WB_REG_CASE(op2, 4, val); \ WRITE_WB_REG_CASE(op2, 5, val); \ WRITE_WB_REG_CASE(op2, 6, val); \ WRITE_WB_REG_CASE(op2, 7, val); \ WRITE_WB_REG_CASE(op2, 8, val); \ WRITE_WB_REG_CASE(op2, 9, val); \ WRITE_WB_REG_CASE(op2, 10, val); \ WRITE_WB_REG_CASE(op2, 11, val); \ WRITE_WB_REG_CASE(op2, 12, val); \ WRITE_WB_REG_CASE(op2, 13, val); \ WRITE_WB_REG_CASE(op2, 14, val); \ WRITE_WB_REG_CASE(op2, 15, val) static uint32_t dbg_wb_read_reg(int reg, int n) { uint32_t val; val = 0; switch (reg + n) { SWITCH_CASES_READ_WB_REG(DBG_WB_WVR, val); SWITCH_CASES_READ_WB_REG(DBG_WB_WCR, val); SWITCH_CASES_READ_WB_REG(DBG_WB_BVR, val); SWITCH_CASES_READ_WB_REG(DBG_WB_BCR, val); default: db_printf( "trying to read from CP14 reg. using wrong opc2 %d\n", reg >> OP2_SHIFT); } return (val); } static void dbg_wb_write_reg(int reg, int n, uint32_t val) { switch (reg + n) { SWITCH_CASES_WRITE_WB_REG(DBG_WB_WVR, val); SWITCH_CASES_WRITE_WB_REG(DBG_WB_WCR, val); SWITCH_CASES_WRITE_WB_REG(DBG_WB_BVR, val); SWITCH_CASES_WRITE_WB_REG(DBG_WB_BCR, val); default: db_printf( "trying to write to CP14 reg. using wrong opc2 %d\n", reg >> OP2_SHIFT); } isb(); } boolean_t kdb_cpu_pc_is_singlestep(db_addr_t pc) { if (dbg_find_slot(DBG_TYPE_BREAKPOINT, pc) != ~0U) return (TRUE); return (FALSE); } void kdb_cpu_set_singlestep(void) { db_expr_t inst; db_addr_t pc, brpc; uint32_t wcr; u_int i; /* * Disable watchpoints, e.g. stepping over watched instruction will * trigger break exception instead of single-step exception and locks * CPU on that instruction for ever. */ for (i = 0; i < dbg_watchpoint_num; i++) { wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i); if ((wcr & DBG_WB_CTRL_E) != 0) { dbg_wb_write_reg(DBG_REG_BASE_WCR, i, (wcr & ~DBG_WB_CTRL_E)); } } pc = PC_REGS(); inst = db_get_value(pc, sizeof(pc), FALSE); if (inst_branch(inst) || inst_call(inst) || inst_return(inst)) { brpc = branch_taken(inst, pc); dbg_setup_breakpoint(brpc, INSN_SIZE, DBG_BKPT_BT_SLOT); } pc = next_instr_address(pc, 0); dbg_setup_breakpoint(pc, INSN_SIZE, DBG_BKPT_BNT_SLOT); } void kdb_cpu_clear_singlestep(void) { uint32_t wvr, wcr; u_int i; dbg_remove_breakpoint(DBG_BKPT_BT_SLOT); dbg_remove_breakpoint(DBG_BKPT_BNT_SLOT); /* Restore all watchpoints */ for (i = 0; i < dbg_watchpoint_num; i++) { wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i); wvr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i); /* Watchpoint considered not empty if address value is not 0 */ if ((wvr & DBGWVR_ADDR_MASK) != 0) { dbg_wb_write_reg(DBG_REG_BASE_WCR, i, (wcr | DBG_WB_CTRL_E)); } } } int dbg_setup_watchpoint(db_expr_t addr, db_expr_t size, enum dbg_access_t access) { struct dbg_wb_conf conf; if (access == HW_BREAKPOINT_X) { db_printf("Invalid access type for watchpoint: %d\n", access); return (EINVAL); } conf.address = addr; conf.size = size; conf.access = access; conf.type = DBG_TYPE_WATCHPOINT; return (dbg_setup_xpoint(&conf)); } int dbg_remove_watchpoint(db_expr_t addr, db_expr_t size __unused) { struct dbg_wb_conf conf; conf.address = addr; conf.type = DBG_TYPE_WATCHPOINT; return (dbg_remove_xpoint(&conf)); } static int dbg_setup_breakpoint(db_expr_t addr, db_expr_t size, u_int slot) { struct dbg_wb_conf conf; conf.address = addr; conf.size = size; conf.access = HW_BREAKPOINT_X; conf.type = DBG_TYPE_BREAKPOINT; conf.slot = slot; return (dbg_setup_xpoint(&conf)); } static int dbg_remove_breakpoint(u_int slot) { struct dbg_wb_conf conf; /* Slot already cleared. Don't recurse */ if (dbg_check_slot_free(DBG_TYPE_BREAKPOINT, slot)) return (0); conf.slot = slot; conf.type = DBG_TYPE_BREAKPOINT; return (dbg_remove_xpoint(&conf)); } static const char * dbg_watchtype_str(uint32_t type) { switch (type) { case DBG_WB_CTRL_EXEC: return ("execute"); case DBG_WB_CTRL_STORE: return ("write"); case DBG_WB_CTRL_LOAD: return ("read"); case DBG_WB_CTRL_LOAD | DBG_WB_CTRL_STORE: return ("read/write"); default: return ("invalid"); } } static int dbg_watchtype_len(uint32_t len) { switch (len) { case DBG_WB_CTRL_LEN_1: return (1); case DBG_WB_CTRL_LEN_2: return (2); case DBG_WB_CTRL_LEN_4: return (4); case DBG_WB_CTRL_LEN_8: return (8); default: return (0); } } void dbg_show_watchpoint(void) { uint32_t wcr, len, type; uint32_t addr; boolean_t is_enabled; int i; if (!dbg_capable) { db_printf("Architecture does not support HW " "breakpoints/watchpoints\n"); return; } db_printf("\nhardware watchpoints:\n"); db_printf(" watch status type len address symbol\n"); db_printf(" ----- -------- ---------- --- ---------- ------------------\n"); for (i = 0; i < dbg_watchpoint_num; i++) { wcr = dbg_wb_read_reg(DBG_REG_BASE_WCR, i); if ((wcr & DBG_WB_CTRL_E) != 0) is_enabled = TRUE; else is_enabled = FALSE; type = DBG_WB_CTRL_ACCESS_MASK(wcr); len = DBG_WB_CTRL_LEN_MASK(wcr); addr = dbg_wb_read_reg(DBG_REG_BASE_WVR, i) & DBGWVR_ADDR_MASK; db_printf(" %-5d %-8s %10s %3d 0x%08x ", i, is_enabled ? "enabled" : "disabled", is_enabled ? dbg_watchtype_str(type) : "", is_enabled ? dbg_watchtype_len(len) : 0, addr); db_printsym((db_addr_t)addr, DB_STGY_ANY); db_printf("\n"); } } static boolean_t dbg_check_slot_free(enum dbg_t type, u_int slot) { uint32_t cr, vr; uint32_t max; switch(type) { case DBG_TYPE_BREAKPOINT: max = dbg_breakpoint_num; cr = DBG_REG_BASE_BCR; vr = DBG_REG_BASE_BVR; break; case DBG_TYPE_WATCHPOINT: max = dbg_watchpoint_num; cr = DBG_REG_BASE_WCR; vr = DBG_REG_BASE_WVR; break; default: db_printf("%s: Unsupported event type %d\n", __func__, type); return (FALSE); } if (slot >= max) { db_printf("%s: Invalid slot number %d, max %d\n", __func__, slot, max - 1); return (FALSE); } if ((dbg_wb_read_reg(cr, slot) & DBG_WB_CTRL_E) == 0 && (dbg_wb_read_reg(vr, slot) & DBGWVR_ADDR_MASK) == 0) return (TRUE); return (FALSE); } static u_int dbg_find_free_slot(enum dbg_t type) { u_int max, i; switch(type) { case DBG_TYPE_BREAKPOINT: max = dbg_breakpoint_num; break; case DBG_TYPE_WATCHPOINT: max = dbg_watchpoint_num; break; default: db_printf("Unsupported debug type\n"); return (~0U); } for (i = 0; i < max; i++) { if (dbg_check_slot_free(type, i)) return (i); } return (~0U); } static u_int dbg_find_slot(enum dbg_t type, db_expr_t addr) { uint32_t reg_addr, reg_ctrl; u_int max, i; switch(type) { case DBG_TYPE_BREAKPOINT: max = dbg_breakpoint_num; reg_addr = DBG_REG_BASE_BVR; reg_ctrl = DBG_REG_BASE_BCR; break; case DBG_TYPE_WATCHPOINT: max = dbg_watchpoint_num; reg_addr = DBG_REG_BASE_WVR; reg_ctrl = DBG_REG_BASE_WCR; break; default: db_printf("Unsupported debug type\n"); return (~0U); } for (i = 0; i < max; i++) { if ((dbg_wb_read_reg(reg_addr, i) == addr) && ((dbg_wb_read_reg(reg_ctrl, i) & DBG_WB_CTRL_E) != 0)) return (i); } return (~0U); } static __inline boolean_t dbg_monitor_is_enabled(void) { return ((cp14_dbgdscrint_get() & DBGSCR_MDBG_EN) != 0); } static int dbg_enable_monitor(void) { uint32_t dbg_dscr; - /* Already enabled? Just increment reference counter and return */ - if (dbg_monitor_is_enabled()) { - dbg_ref_count_mme[PCPU_GET(cpuid)]++; + /* Already enabled? Just return */ + if (dbg_monitor_is_enabled()) return (0); - } dbg_dscr = cp14_dbgdscrint_get(); switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6: case ID_DFR0_CP_DEBUG_M_V6_1: /* fall through */ cp14_dbgdscr_v6_set(dbg_dscr | DBGSCR_MDBG_EN); break; case ID_DFR0_CP_DEBUG_M_V7: /* fall through */ case ID_DFR0_CP_DEBUG_M_V7_1: cp14_dbgdscr_v7_set(dbg_dscr | DBGSCR_MDBG_EN); break; default: break; } isb(); /* Verify that Monitor mode is set */ - if (dbg_monitor_is_enabled()) { - dbg_ref_count_mme[PCPU_GET(cpuid)]++; + if (dbg_monitor_is_enabled()) return (0); - } return (ENXIO); } static int dbg_disable_monitor(void) { uint32_t dbg_dscr; if (!dbg_monitor_is_enabled()) return (0); - if (--dbg_ref_count_mme[PCPU_GET(cpuid)] > 0) - return (0); - dbg_dscr = cp14_dbgdscrint_get(); switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6: case ID_DFR0_CP_DEBUG_M_V6_1: /* fall through */ dbg_dscr &= ~DBGSCR_MDBG_EN; cp14_dbgdscr_v6_set(dbg_dscr); break; case ID_DFR0_CP_DEBUG_M_V7: /* fall through */ case ID_DFR0_CP_DEBUG_M_V7_1: dbg_dscr &= ~DBGSCR_MDBG_EN; cp14_dbgdscr_v7_set(dbg_dscr); break; default: return (ENXIO); } isb(); return (0); } static int dbg_setup_xpoint(struct dbg_wb_conf *conf) { + struct pcpu *pcpu; + struct dbreg *d; const char *typestr; uint32_t cr_size, cr_priv, cr_access; uint32_t reg_ctrl, reg_addr, ctrl, addr; boolean_t is_bkpt; - u_int cpuid; + u_int cpuid, cpu; u_int i; int err; if (!dbg_capable) return (ENXIO); is_bkpt = (conf->type == DBG_TYPE_BREAKPOINT); typestr = is_bkpt ? "breakpoint" : "watchpoint"; cpuid = PCPU_GET(cpuid); if (!dbg_ready[cpuid]) { err = dbg_reset_state(); if (err != 0) return (err); dbg_ready[cpuid] = TRUE; } if (is_bkpt) { if (dbg_breakpoint_num == 0) { db_printf("Breakpoints not supported on this architecture\n"); return (ENXIO); } i = conf->slot; if (!dbg_check_slot_free(DBG_TYPE_BREAKPOINT, i)) { /* * This should never happen. If it does it means that * there is an erroneus scenario somewhere. Still, it can * be done but let's inform the user. */ db_printf("ERROR: Breakpoint already set. Replacing...\n"); } } else { i = dbg_find_free_slot(DBG_TYPE_WATCHPOINT); if (i == ~0U) { db_printf("Can not find slot for %s, max %d slots supported\n", typestr, dbg_watchpoint_num); return (ENXIO); } } /* Kernel access only */ cr_priv = DBG_WB_CTRL_PL1; switch(conf->size) { case 1: cr_size = DBG_WB_CTRL_LEN_1; break; case 2: cr_size = DBG_WB_CTRL_LEN_2; break; case 4: cr_size = DBG_WB_CTRL_LEN_4; break; case 8: cr_size = DBG_WB_CTRL_LEN_8; break; default: db_printf("Unsupported address size for %s\n", typestr); return (EINVAL); } if (is_bkpt) { cr_access = DBG_WB_CTRL_EXEC; reg_ctrl = DBG_REG_BASE_BCR; reg_addr = DBG_REG_BASE_BVR; /* Always unlinked BKPT */ ctrl = (cr_size | cr_access | cr_priv | DBG_WB_CTRL_E); } else { switch(conf->access) { case HW_WATCHPOINT_R: cr_access = DBG_WB_CTRL_LOAD; break; case HW_WATCHPOINT_W: cr_access = DBG_WB_CTRL_STORE; break; case HW_WATCHPOINT_RW: cr_access = DBG_WB_CTRL_LOAD | DBG_WB_CTRL_STORE; break; default: db_printf("Unsupported exception level for %s\n", typestr); return (EINVAL); } reg_ctrl = DBG_REG_BASE_WCR; reg_addr = DBG_REG_BASE_WVR; ctrl = (cr_size | cr_access | cr_priv | DBG_WB_CTRL_E); } addr = conf->address; dbg_wb_write_reg(reg_addr, i, addr); dbg_wb_write_reg(reg_ctrl, i, ctrl); - return (dbg_enable_monitor()); + err = dbg_enable_monitor(); + if (err != 0) + return (err); + + /* Increment monitor enable counter */ + dbg_ref_count_mme++; + + /* + * Save watchpoint settings for all CPUs. + * We don't need to do the same with breakpoints since HW breakpoints + * are only used to perform single stepping. + */ + if (!is_bkpt) { + CPU_FOREACH(cpu) { + pcpu = pcpu_find(cpu); + /* Fill out the settings for watchpoint */ + d = (struct dbreg *)pcpu->pc_dbreg; + d->dbg_wvr[i] = addr; + d->dbg_wcr[i] = ctrl; + /* Skip update command for the current CPU */ + if (cpu != cpuid) + pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; + } + } + /* Ensure all data is written before waking other CPUs */ + atomic_thread_fence_rel(); + + return (0); } static int dbg_remove_xpoint(struct dbg_wb_conf *conf) { + struct pcpu *pcpu; + struct dbreg *d; uint32_t reg_ctrl, reg_addr, addr; - u_int cpuid; + boolean_t is_bkpt; + u_int cpuid, cpu; u_int i; int err; if (!dbg_capable) return (ENXIO); + is_bkpt = (conf->type == DBG_TYPE_BREAKPOINT); + cpuid = PCPU_GET(cpuid); if (!dbg_ready[cpuid]) { err = dbg_reset_state(); if (err != 0) return (err); dbg_ready[cpuid] = TRUE; } addr = conf->address; - if (conf->type == DBG_TYPE_BREAKPOINT) { + if (is_bkpt) { i = conf->slot; reg_ctrl = DBG_REG_BASE_BCR; reg_addr = DBG_REG_BASE_BVR; } else { i = dbg_find_slot(DBG_TYPE_WATCHPOINT, addr); if (i == ~0U) { db_printf("Can not find watchpoint for address 0%x\n", addr); return (EINVAL); } reg_ctrl = DBG_REG_BASE_WCR; reg_addr = DBG_REG_BASE_WVR; } dbg_wb_write_reg(reg_ctrl, i, 0); dbg_wb_write_reg(reg_addr, i, 0); - return (dbg_disable_monitor()); + /* Decrement monitor enable counter */ + dbg_ref_count_mme--; + if (dbg_ref_count_mme < 0) + dbg_ref_count_mme = 0; + + atomic_thread_fence_rel(); + + if (dbg_ref_count_mme == 0) { + err = dbg_disable_monitor(); + if (err != 0) + return (err); + } + + /* + * Save watchpoint settings for all CPUs. + * We don't need to do the same with breakpoints since HW breakpoints + * are only used to perform single stepping. + */ + if (!is_bkpt) { + CPU_FOREACH(cpu) { + pcpu = pcpu_find(cpu); + /* Fill out the settings for watchpoint */ + d = (struct dbreg *)pcpu->pc_dbreg; + d->dbg_wvr[i] = 0; + d->dbg_wcr[i] = 0; + /* Skip update command for the current CPU */ + if (cpu != cpuid) + pcpu->pc_dbreg_cmd = PC_DBREG_CMD_LOAD; + } + /* Ensure all data is written before waking other CPUs */ + atomic_thread_fence_rel(); + } + + return (0); } static __inline uint32_t dbg_get_debug_model(void) { uint32_t dbg_m; dbg_m = ((cpuinfo.id_dfr0 & ID_DFR0_CP_DEBUG_M_MASK) >> ID_DFR0_CP_DEBUG_M_SHIFT); return (dbg_m); } static __inline boolean_t dbg_get_ossr(void) { switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6_1: if ((cp14_dbgoslsr_get() & DBGOSLSR_OSLM0) != 0) return (TRUE); return (FALSE); case ID_DFR0_CP_DEBUG_M_V7_1: return (TRUE); default: return (FALSE); } } static __inline boolean_t dbg_arch_supported(void) { switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6: case ID_DFR0_CP_DEBUG_M_V6_1: case ID_DFR0_CP_DEBUG_M_V7: case ID_DFR0_CP_DEBUG_M_V7_1: /* fall through */ return (TRUE); default: /* We only support valid v6.x/v7.x modes through CP14 */ return (FALSE); } } static __inline uint32_t dbg_get_wrp_num(void) { uint32_t dbg_didr; dbg_didr = cp14_dbgdidr_get(); return (DBGDIDR_WRPS_NUM(dbg_didr)); } static __inline uint32_t dgb_get_brp_num(void) { uint32_t dbg_didr; dbg_didr = cp14_dbgdidr_get(); return (DBGDIDR_BRPS_NUM(dbg_didr)); } static int dbg_reset_state(void) { u_int cpuid; size_t i; int err; cpuid = PCPU_GET(cpuid); err = 0; switch (dbg_model) { case ID_DFR0_CP_DEBUG_M_V6: /* v6 Debug logic reset upon power-up */ return (0); case ID_DFR0_CP_DEBUG_M_V6_1: /* Is core power domain powered up? */ if ((cp14_dbgprsr_get() & DBGPRSR_PU) == 0) err = ENXIO; if (err != 0) break; if (dbg_ossr) goto vectr_clr; break; case ID_DFR0_CP_DEBUG_M_V7: break; case ID_DFR0_CP_DEBUG_M_V7_1: /* Is double lock set? */ if ((cp14_dbgosdlr_get() & DBGPRSR_DLK) != 0) err = ENXIO; break; default: break; } if (err != 0) { db_printf("Debug facility locked (CPU%d)\n", cpuid); return (err); } /* * DBGOSLAR is always implemented for v7.1 Debug Arch. however is * optional for v7 (depends on OS save and restore support). */ if (((dbg_model & ID_DFR0_CP_DEBUG_M_V7_1) != 0) || dbg_ossr) { /* * Clear OS lock. * Writing any other value than 0xC5ACCESS will unlock. */ cp14_dbgoslar_set(0); isb(); } vectr_clr: /* * After reset we must ensure that DBGVCR has a defined value. * Disable all vector catch events. Safe to use - required in all * implementations. */ cp14_dbgvcr_set(0); isb(); /* * We have limited number of {watch,break}points, each consists of * two registers: * - wcr/bcr regsiter configurates corresponding {watch,break}point * behaviour * - wvr/bvr register keeps address we are hunting for * * Reset all breakpoints and watchpoints. */ for (i = 0; i < dbg_watchpoint_num; ++i) { dbg_wb_write_reg(DBG_REG_BASE_WCR, i, 0); dbg_wb_write_reg(DBG_REG_BASE_WVR, i, 0); } for (i = 0; i < dbg_breakpoint_num; ++i) { dbg_wb_write_reg(DBG_REG_BASE_BCR, i, 0); dbg_wb_write_reg(DBG_REG_BASE_BVR, i, 0); } return (0); } void dbg_monitor_init(void) { int err; /* Fetch ARM Debug Architecture model */ dbg_model = dbg_get_debug_model(); if (!dbg_arch_supported()) { db_printf("ARM Debug Architecture not supported\n"); return; } if (bootverbose) { db_printf("ARM Debug Architecture %s\n", (dbg_model == ID_DFR0_CP_DEBUG_M_V6) ? "v6" : (dbg_model == ID_DFR0_CP_DEBUG_M_V6_1) ? "v6.1" : (dbg_model == ID_DFR0_CP_DEBUG_M_V7) ? "v7" : (dbg_model == ID_DFR0_CP_DEBUG_M_V7_1) ? "v7.1" : "unknown"); } /* Do we have OS Save and Restore mechanism? */ dbg_ossr = dbg_get_ossr(); /* Find out many breakpoints and watchpoints we can use */ dbg_watchpoint_num = dbg_get_wrp_num(); dbg_breakpoint_num = dgb_get_brp_num(); if (bootverbose) { db_printf("%d watchpoints and %d breakpoints supported\n", dbg_watchpoint_num, dbg_breakpoint_num); } err = dbg_reset_state(); if (err == 0) { dbg_capable = TRUE; return; } db_printf("HW Breakpoints/Watchpoints not enabled on CPU%d\n", PCPU_GET(cpuid)); +} + +CTASSERT(sizeof(struct dbreg) == sizeof(((struct pcpu *)NULL)->pc_dbreg)); + +void +dbg_resume_dbreg(void) +{ + struct dbreg *d; + u_int cpuid; + u_int i; + int err; + + /* + * This flag is set on the primary CPU + * and its meaning is valid for other CPUs too. + */ + if (!dbg_capable) + return; + + atomic_thread_fence_acq(); + + switch (PCPU_GET(dbreg_cmd)) { + case PC_DBREG_CMD_LOAD: + d = (struct dbreg *)PCPU_PTR(dbreg); + cpuid = PCPU_GET(cpuid); + + /* Reset Debug Architecture State if not done earlier */ + if (!dbg_ready[cpuid]) { + err = dbg_reset_state(); + if (err != 0) { + /* + * Something is very wrong. + * WPs/BPs will not work correctly in this CPU. + */ + panic("%s: Failed to reset Debug Architecture " + "state on CPU%d", __func__, cpuid); + } + dbg_ready[cpuid] = TRUE; + } + + /* Restore watchpoints */ + for (i = 0; i < dbg_watchpoint_num; i++) { + dbg_wb_write_reg(DBG_REG_BASE_WVR, i, d->dbg_wvr[i]); + dbg_wb_write_reg(DBG_REG_BASE_WCR, i, d->dbg_wcr[i]); + } + + if ((dbg_ref_count_mme > 0) && !dbg_monitor_is_enabled()) { + err = dbg_enable_monitor(); + if (err != 0) { + panic("%s: Failed to enable Debug Monitor " + "on CPU%d", __func__, cpuid); + } + } + if ((dbg_ref_count_mme == 0) && dbg_monitor_is_enabled()) { + err = dbg_disable_monitor(); + if (err != 0) { + panic("%s: Failed to disable Debug Monitor " + "on CPU%d", __func__, cpuid); + } + } + + PCPU_SET(dbreg_cmd, PC_DBREG_CMD_NONE); + break; + } } Index: head/sys/arm/arm/mp_machdep.c =================================================================== --- head/sys/arm/arm/mp_machdep.c (revision 294986) +++ head/sys/arm/arm/mp_machdep.c (revision 294987) @@ -1,549 +1,556 @@ /*- * Copyright (c) 2011 Semihalf. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #ifdef VFP #include #endif #ifdef CPU_MV_PJ4B #include #include #endif #include "opt_smp.h" extern struct pcpu __pcpu[]; /* used to hold the AP's until we are ready to release them */ struct mtx ap_boot_mtx; struct pcb stoppcbs[MAXCPU]; /* # of Applications processors */ volatile int mp_naps; /* Set to 1 once we're ready to let the APs out of the pen. */ volatile int aps_ready = 0; #ifndef ARM_INTRNG static int ipi_handler(void *arg); #endif void set_stackptrs(int cpu); /* Temporary variables for init_secondary() */ void *dpcpu[MAXCPU - 1]; /* Determine if we running MP machine */ int cpu_mp_probe(void) { CPU_SETOF(0, &all_cpus); return (platform_mp_probe()); } /* Start Application Processor via platform specific function */ static int check_ap(void) { uint32_t ms; for (ms = 0; ms < 2000; ++ms) { if ((mp_naps + 1) == mp_ncpus) return (0); /* success */ else DELAY(1000); } return (-2); } extern unsigned char _end[]; /* Initialize and fire up non-boot processors */ void cpu_mp_start(void) { int error, i; mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); /* Reserve memory for application processors */ for(i = 0; i < (mp_ncpus - 1); i++) dpcpu[i] = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, M_WAITOK | M_ZERO); cpu_idcache_wbinv_all(); cpu_l2cache_wbinv_all(); cpu_idcache_wbinv_all(); /* Initialize boot code and start up processors */ platform_mp_start_ap(); /* Check if ap's started properly */ error = check_ap(); if (error) printf("WARNING: Some AP's failed to start\n"); else for (i = 1; i < mp_ncpus; i++) CPU_SET(i, &all_cpus); } /* Introduce rest of cores to the world */ void cpu_mp_announce(void) { } extern vm_paddr_t pmap_pa; void init_secondary(int cpu) { struct pcpu *pc; uint32_t loop_counter; #ifndef ARM_INTRNG int start = 0, end = 0; #endif #ifdef ARM_NEW_PMAP uint32_t actlr_mask, actlr_set; pmap_set_tex(); cpuinfo_get_actlr_modifier(&actlr_mask, &actlr_set); reinit_mmu(pmap_kern_ttb, actlr_mask, actlr_set); cpu_setup(); /* Provide stack pointers for other processor modes. */ set_stackptrs(cpu); enable_interrupts(PSR_A); #else /* ARM_NEW_PMAP */ cpu_setup(); setttb(pmap_pa); cpu_tlb_flushID(); #endif /* ARM_NEW_PMAP */ pc = &__pcpu[cpu]; /* * pcpu_init() updates queue, so it should not be executed in parallel * on several cores */ while(mp_naps < (cpu - 1)) ; pcpu_init(pc, cpu, sizeof(struct pcpu)); dpcpu_init(dpcpu[cpu - 1], cpu); #ifndef ARM_NEW_PMAP /* Provide stack pointers for other processor modes. */ set_stackptrs(cpu); #endif /* Signal our startup to BSP */ atomic_add_rel_32(&mp_naps, 1); /* Spin until the BSP releases the APs */ while (!atomic_load_acq_int(&aps_ready)) { #if __ARM_ARCH >= 7 __asm __volatile("wfe"); #endif } /* Initialize curthread */ KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread")); pc->pc_curthread = pc->pc_idlethread; pc->pc_curpcb = pc->pc_idlethread->td_pcb; set_curthread(pc->pc_idlethread); #ifdef VFP vfp_init(); #endif mtx_lock_spin(&ap_boot_mtx); atomic_add_rel_32(&smp_cpus, 1); if (smp_cpus == mp_ncpus) { /* enable IPI's, tlb shootdown, freezes etc */ atomic_store_rel_int(&smp_started, 1); } mtx_unlock_spin(&ap_boot_mtx); #ifndef ARM_INTRNG /* Enable ipi */ #ifdef IPI_IRQ_START start = IPI_IRQ_START; #ifdef IPI_IRQ_END end = IPI_IRQ_END; #else end = IPI_IRQ_START; #endif #endif for (int i = start; i <= end; i++) arm_unmask_irq(i); #endif /* INTRNG */ enable_interrupts(PSR_I); loop_counter = 0; while (smp_started == 0) { DELAY(100); loop_counter++; if (loop_counter == 1000) CTR0(KTR_SMP, "AP still wait for smp_started"); } /* Start per-CPU event timers. */ cpu_initclocks_ap(); CTR0(KTR_SMP, "go into scheduler"); platform_mp_init_secondary(); /* Enter the scheduler */ sched_throw(NULL); panic("scheduler returned us to %s", __func__); /* NOTREACHED */ } #ifdef ARM_INTRNG static void ipi_rendezvous(void *dummy __unused) { CTR0(KTR_SMP, "IPI_RENDEZVOUS"); smp_rendezvous_action(); } static void ipi_ast(void *dummy __unused) { CTR0(KTR_SMP, "IPI_AST"); } static void ipi_stop(void *dummy __unused) { u_int cpu; /* * IPI_STOP_HARD is mapped to IPI_STOP. */ CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD"); cpu = PCPU_GET(cpuid); savectx(&stoppcbs[cpu]); /* * CPUs are stopped when entering the debugger and at * system shutdown, both events which can precede a * panic dump. For the dump to be correct, all caches * must be flushed and invalidated, but on ARM there's * no way to broadcast a wbinv_all to other cores. * Instead, we have each core do the local wbinv_all as * part of stopping the core. The core requesting the * stop will do the l2 cache flush after all other cores * have done their l1 flushes and stopped. */ cpu_idcache_wbinv_all(); /* Indicate we are stopped */ CPU_SET_ATOMIC(cpu, &stopped_cpus); /* Wait for restart */ while (!CPU_ISSET(cpu, &started_cpus)) cpu_spinwait(); CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); +#ifdef DDB + dbg_resume_dbreg(); +#endif CTR0(KTR_SMP, "IPI_STOP (restart)"); } static void ipi_preempt(void *arg) { struct trapframe *oldframe; struct thread *td; critical_enter(); td = curthread; td->td_intr_nesting_level++; oldframe = td->td_intr_frame; td->td_intr_frame = (struct trapframe *)arg; CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); sched_preempt(td); td->td_intr_frame = oldframe; td->td_intr_nesting_level--; critical_exit(); } static void ipi_hardclock(void *arg) { struct trapframe *oldframe; struct thread *td; critical_enter(); td = curthread; td->td_intr_nesting_level++; oldframe = td->td_intr_frame; td->td_intr_frame = (struct trapframe *)arg; CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); hardclockintr(); td->td_intr_frame = oldframe; td->td_intr_nesting_level--; critical_exit(); } static void ipi_tlb(void *dummy __unused) { CTR1(KTR_SMP, "%s: IPI_TLB", __func__); cpufuncs.cf_tlb_flushID(); } #else static int ipi_handler(void *arg) { u_int cpu, ipi; cpu = PCPU_GET(cpuid); ipi = pic_ipi_read((int)arg); while ((ipi != 0x3ff)) { switch (ipi) { case IPI_RENDEZVOUS: CTR0(KTR_SMP, "IPI_RENDEZVOUS"); smp_rendezvous_action(); break; case IPI_AST: CTR0(KTR_SMP, "IPI_AST"); break; case IPI_STOP: /* * IPI_STOP_HARD is mapped to IPI_STOP so it is not * necessary to add it in the switch. */ CTR0(KTR_SMP, "IPI_STOP or IPI_STOP_HARD"); savectx(&stoppcbs[cpu]); /* * CPUs are stopped when entering the debugger and at * system shutdown, both events which can precede a * panic dump. For the dump to be correct, all caches * must be flushed and invalidated, but on ARM there's * no way to broadcast a wbinv_all to other cores. * Instead, we have each core do the local wbinv_all as * part of stopping the core. The core requesting the * stop will do the l2 cache flush after all other cores * have done their l1 flushes and stopped. */ cpu_idcache_wbinv_all(); /* Indicate we are stopped */ CPU_SET_ATOMIC(cpu, &stopped_cpus); /* Wait for restart */ while (!CPU_ISSET(cpu, &started_cpus)) cpu_spinwait(); CPU_CLR_ATOMIC(cpu, &started_cpus); CPU_CLR_ATOMIC(cpu, &stopped_cpus); +#ifdef DDB + dbg_resume_dbreg(); +#endif CTR0(KTR_SMP, "IPI_STOP (restart)"); break; case IPI_PREEMPT: CTR1(KTR_SMP, "%s: IPI_PREEMPT", __func__); sched_preempt(curthread); break; case IPI_HARDCLOCK: CTR1(KTR_SMP, "%s: IPI_HARDCLOCK", __func__); hardclockintr(); break; case IPI_TLB: CTR1(KTR_SMP, "%s: IPI_TLB", __func__); cpufuncs.cf_tlb_flushID(); break; default: panic("Unknown IPI 0x%0x on cpu %d", ipi, curcpu); } pic_ipi_clear(ipi); ipi = pic_ipi_read(-1); } return (FILTER_HANDLED); } #endif static void release_aps(void *dummy __unused) { uint32_t loop_counter; #ifndef ARM_INTRNG int start = 0, end = 0; #endif if (mp_ncpus == 1) return; #ifdef ARM_INTRNG intr_ipi_set_handler(IPI_RENDEZVOUS, "rendezvous", ipi_rendezvous, NULL, 0); intr_ipi_set_handler(IPI_AST, "ast", ipi_ast, NULL, 0); intr_ipi_set_handler(IPI_STOP, "stop", ipi_stop, NULL, 0); intr_ipi_set_handler(IPI_PREEMPT, "preempt", ipi_preempt, NULL, 0); intr_ipi_set_handler(IPI_HARDCLOCK, "hardclock", ipi_hardclock, NULL, 0); intr_ipi_set_handler(IPI_TLB, "tlb", ipi_tlb, NULL, 0); #else #ifdef IPI_IRQ_START start = IPI_IRQ_START; #ifdef IPI_IRQ_END end = IPI_IRQ_END; #else end = IPI_IRQ_START; #endif #endif for (int i = start; i <= end; i++) { /* * IPI handler */ /* * Use 0xdeadbeef as the argument value for irq 0, * if we used 0, the intr code will give the trap frame * pointer instead. */ arm_setup_irqhandler("ipi", ipi_handler, NULL, (void *)i, i, INTR_TYPE_MISC | INTR_EXCL, NULL); /* Enable ipi */ arm_unmask_irq(i); } #endif atomic_store_rel_int(&aps_ready, 1); /* Wake the other threads up */ #if __ARM_ARCH >= 7 armv7_sev(); #endif printf("Release APs\n"); for (loop_counter = 0; loop_counter < 2000; loop_counter++) { if (smp_started) return; DELAY(1000); } printf("AP's not started\n"); } SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, release_aps, NULL); struct cpu_group * cpu_topo(void) { return (smp_topo_1level(CG_SHARE_L2, mp_ncpus, 0)); } void cpu_mp_setmaxid(void) { platform_mp_setmaxid(); } /* Sending IPI */ void ipi_all_but_self(u_int ipi) { cpuset_t other_cpus; other_cpus = all_cpus; CPU_CLR(PCPU_GET(cpuid), &other_cpus); CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); platform_ipi_send(other_cpus, ipi); } void ipi_cpu(int cpu, u_int ipi) { cpuset_t cpus; CPU_ZERO(&cpus); CPU_SET(cpu, &cpus); CTR3(KTR_SMP, "%s: cpu: %d, ipi: %x", __func__, cpu, ipi); platform_ipi_send(cpus, ipi); } void ipi_selected(cpuset_t cpus, u_int ipi) { CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi); platform_ipi_send(cpus, ipi); } void tlb_broadcast(int ipi) { if (smp_started) ipi_all_but_self(ipi); } Index: head/sys/arm/include/debug_monitor.h =================================================================== --- head/sys/arm/include/debug_monitor.h (revision 294986) +++ head/sys/arm/include/debug_monitor.h (revision 294987) @@ -1,80 +1,86 @@ /*- * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * This software was developed by Semihalf under * the sponsorship of the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _MACHINE_DEBUG_MONITOR_H_ #define _MACHINE_DEBUG_MONITOR_H_ #ifdef DDB #include enum dbg_access_t { HW_BREAKPOINT_X = 0, HW_WATCHPOINT_R = 1, HW_WATCHPOINT_W = 2, HW_WATCHPOINT_RW = HW_WATCHPOINT_R | HW_WATCHPOINT_W, }; #if __ARM_ARCH >= 6 void dbg_monitor_init(void); void dbg_show_watchpoint(void); int dbg_setup_watchpoint(db_expr_t, db_expr_t, enum dbg_access_t); int dbg_remove_watchpoint(db_expr_t, db_expr_t); +void dbg_resume_dbreg(void); #else /* __ARM_ARCH >= 6 */ static __inline void dbg_show_watchpoint(void) { } static __inline int dbg_setup_watchpoint(db_expr_t addr __unused, db_expr_t size __unused, enum dbg_access_t access __unused) { return (ENXIO); } static __inline int dbg_remove_watchpoint(db_expr_t addr __unused, db_expr_t size __unused) { return (ENXIO); } static __inline void dbg_monitor_init(void) +{ +} + +static __inline void +dbg_resume_dbreg(void) { } #endif /* __ARM_ARCH < 6 */ #else /* DDB */ static __inline void dbg_monitor_init(void) { } #endif #endif /* _MACHINE_DEBUG_MONITOR_H_ */ Index: head/sys/arm/include/pcpu.h =================================================================== --- head/sys/arm/include/pcpu.h (revision 294986) +++ head/sys/arm/include/pcpu.h (revision 294987) @@ -1,128 +1,133 @@ /*- * Copyright (c) 1999 Luoqi Chen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: FreeBSD: src/sys/i386/include/globaldata.h,v 1.27 2001/04/27 * $FreeBSD$ */ #ifndef _MACHINE_PCPU_H_ #define _MACHINE_PCPU_H_ #ifdef _KERNEL #include #include #define ALT_STACK_SIZE 128 struct vmspace; #endif /* _KERNEL */ #if __ARM_ARCH >= 6 #define PCPU_MD_FIELDS \ unsigned int pc_vfpsid; \ unsigned int pc_vfpmvfr0; \ unsigned int pc_vfpmvfr1; \ struct pmap *pc_curpmap; \ vm_offset_t pc_qmap_addr; \ void *pc_qmap_pte; \ - char __pad[133] + unsigned int pc_dbreg[32]; \ + int pc_dbreg_cmd; \ + char __pad[1] #else #define PCPU_MD_FIELDS \ vm_offset_t qmap_addr; \ void *pc_qmap_pte; \ char __pad[149] #endif #ifdef _KERNEL + +#define PC_DBREG_CMD_NONE 0 +#define PC_DBREG_CMD_LOAD 1 struct pcb; struct pcpu; extern struct pcpu *pcpup; #if __ARM_ARCH >= 6 #define CPU_MASK (0xf) #ifndef SMP #define get_pcpu() (pcpup) #else #define get_pcpu() __extension__ ({ \ int id; \ __asm __volatile("mrc p15, 0, %0, c0, c0, 5" : "=r" (id)); \ (pcpup + (id & CPU_MASK)); \ }) #endif static inline struct thread * get_curthread(void) { void *ret; __asm __volatile("mrc p15, 0, %0, c13, c0, 4" : "=r" (ret)); return (ret); } static inline void set_curthread(struct thread *td) { __asm __volatile("mcr p15, 0, %0, c13, c0, 4" : : "r" (td)); } static inline void * get_tls(void) { void *tls; __asm __volatile("mrc p15, 0, %0, c13, c0, 3" : "=r" (tls)); return (tls); } static inline void set_tls(void *tls) { __asm __volatile("mcr p15, 0, %0, c13, c0, 3" : : "r" (tls)); } #define curthread get_curthread() #else #define get_pcpu() pcpup #endif #define PCPU_GET(member) (get_pcpu()->pc_ ## member) #define PCPU_ADD(member, value) (get_pcpu()->pc_ ## member += (value)) #define PCPU_INC(member) PCPU_ADD(member, 1) #define PCPU_PTR(member) (&get_pcpu()->pc_ ## member) #define PCPU_SET(member,value) (get_pcpu()->pc_ ## member = (value)) void pcpu0_init(void); #endif /* _KERNEL */ #endif /* !_MACHINE_PCPU_H_ */ Index: head/sys/arm/include/reg.h =================================================================== --- head/sys/arm/include/reg.h (revision 294986) +++ head/sys/arm/include/reg.h (revision 294987) @@ -1,34 +1,36 @@ /* $NetBSD: reg.h,v 1.2 2001/02/23 21:23:52 reinoud Exp $ */ /* $FreeBSD$ */ #ifndef MACHINE_REG_H #define MACHINE_REG_H #include struct reg { unsigned int r[13]; unsigned int r_sp; unsigned int r_lr; unsigned int r_pc; unsigned int r_cpsr; }; struct fpreg { unsigned int fpr_fpsr; fp_reg_t fpr[8]; }; struct dbreg { - unsigned int dr[8]; /* debug registers */ +#define ARM_WR_MAX 16 /* Maximum number of watchpoint registers */ + unsigned int dbg_wcr[ARM_WR_MAX]; /* Watchpoint Control Registers */ + unsigned int dbg_wvr[ARM_WR_MAX]; /* Watchpoint Value Registers */ }; #ifdef _KERNEL int fill_regs(struct thread *, struct reg *); int set_regs(struct thread *, struct reg *); int fill_fpregs(struct thread *, struct fpreg *); int set_fpregs(struct thread *, struct fpreg *); int fill_dbregs(struct thread *, struct dbreg *); int set_dbregs(struct thread *, struct dbreg *); #endif #endif /* !MACHINE_REG_H */