/*- * Copyright (c) 2001 Jake Burkholder. * All rights reserved. * Copyright 2001 by Thomas Moestl . 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. * * $FreeBSD: src/sys/sparc64/sparc64/db_hwwatch.c,v 1.3 2002/03/21 00:06:55 alfred Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); void db_md_list_watchpoints(void); static void db_watch_print(vm_offset_t wp, int bm); static int debugger_on_watchpoint = 1; SYSCTL_INT(_debug, OID_AUTO, debugger_on_watchpoint, CTLFLAG_RW, &debugger_on_watchpoint, 0, ""); static int watchpoint_print = 1; SYSCTL_INT(_debug, OID_AUTO, watchpoint_print, CTLFLAG_RW, &watchpoint_print, 0, ""); int watch_phys_set_mask(vm_offset_t pa, u_long mask) { u_long lsucr; stxa_sync(AA_DMMU_PWPR, ASI_DMMU, pa & (((2UL << 38) - 1) << 3)); lsucr = ldxa(0, ASI_LSU_CTL_REG); lsucr = ((lsucr | LSU_PW) & ~LSU_PM_MASK) | (mask << LSU_PM_SHIFT); stxa_sync(0, ASI_LSU_CTL_REG, lsucr); return (0); } int watch_phys_set(vm_offset_t pa, int sz) { u_long off; off = (u_long)pa & 7; /* Test for misaligned watch points. */ if (off + sz > 8) return (-1); return (watch_phys_set_mask(pa, ((1 << sz) - 1) << off)); } vm_offset_t watch_phys_get(int *bm) { u_long pa; u_long lsucr; if (!watch_phys_active()) return (0); pa = ldxa(AA_DMMU_PWPR, ASI_DMMU); lsucr = ldxa(0, ASI_LSU_CTL_REG); *bm = (lsucr & LSU_PM_MASK) >> LSU_PM_SHIFT; return ((vm_offset_t)pa); } void watch_phys_clear() { stxa_sync(0, ASI_LSU_CTL_REG, ldxa(0, ASI_LSU_CTL_REG) & ~LSU_PW); } int watch_phys_active() { return (ldxa(0, ASI_LSU_CTL_REG) & LSU_PW); } int watch_virt_set_mask(vm_offset_t va, u_long mask) { u_long lsucr; stxa_sync(AA_DMMU_VWPR, ASI_DMMU, va & (((2UL << 41) - 1) << 3)); lsucr = ldxa(0, ASI_LSU_CTL_REG); lsucr = ((lsucr | LSU_VW) & ~LSU_VM_MASK) | (mask << LSU_VM_SHIFT); stxa_sync(0, ASI_LSU_CTL_REG, lsucr); return (0); } int watch_virt_set(vm_offset_t va, int sz) { u_long off; off = (u_long)va & 7; /* Test for misaligned watch points. */ if (off + sz > 8) return (-1); return (watch_virt_set_mask(va, ((1 << sz) - 1) << off)); } vm_offset_t watch_virt_get(int *bm) { u_long va; u_long lsucr; if (!watch_virt_active()) return (0); va = ldxa(AA_DMMU_VWPR, ASI_DMMU); lsucr = ldxa(0, ASI_LSU_CTL_REG); *bm = (lsucr & LSU_VM_MASK) >> LSU_VM_SHIFT; return ((vm_offset_t)va); } void watch_virt_clear() { stxa_sync(0, ASI_LSU_CTL_REG, ldxa(0, ASI_LSU_CTL_REG) & ~LSU_VW); } int watch_virt_active() { return (ldxa(0, ASI_LSU_CTL_REG) & LSU_VW); } static u_long emul_fetch_reg(struct trapframe *tf, int reg) { struct frame *frm; if (reg == IREG_G0) return (0); else if (reg < IREG_O0) /* global */ return tf->tf_global[reg]; else if (reg < IREG_L0) /* out */ return tf->tf_out[reg - IREG_O0]; else { /* local, in */ /* * The in registers are immediately after the locals in * the frame. */ frm = (struct frame *)(tf->tf_out[6] + SPOFF); return (frm->fr_local[reg - IREG_L0]); } } static void emul_store_reg(struct trapframe *tf, int reg, u_long val) { struct frame *frm; if (reg == IREG_G0) return; if (reg < IREG_O0) /* global */ tf->tf_global[reg] = val; else if (reg < IREG_L0) /* out */ tf->tf_out[reg - IREG_O0] = val; else { /* * The in registers are immediately after the locals in * the frame. */ frm = (struct frame *)(tf->tf_out[6] + SPOFF); frm->fr_local[reg - IREG_L0] = val; } } static vm_offset_t f3_memop_addr(struct trapframe *tf, u_int insn) { if (IF_F3_I(insn) != 0) return emul_fetch_reg(tf, IF_F3_RS1(insn)) + IF_SIMM(insn, 13); else return emul_fetch_reg(tf, IF_F3_RS1(insn)) + emul_fetch_reg(tf, IF_F3_RS2(insn)); } int db_watch_trap(struct trapframe *tf) { vm_offset_t addr; vm_offset_t va; uint32_t insn; uint64_t val; int mask; addr = 0; mask = 0; if ((tf->tf_type & ~T_KERNEL) == T_PA_WATCHPOINT) { addr = watch_phys_get(&mask); watch_phys_clear(); } else { addr = watch_virt_get(&mask); watch_virt_clear(); } TR3("db_watch_trap: %s addr=%#lx tpc=%#lx", trap_msg[tf->tf_type & ~T_KERNEL], addr, tf->tf_tpc); if (watchpoint_print) printf("db_watch_trap: %s addr=%#lx tpc=%#lx\n", trap_msg[tf->tf_type & ~T_KERNEL], addr, tf->tf_tpc); if (debugger_on_watchpoint && kdb_trap(tf) == 0) return (1); insn = *(uint32_t *)tf->tf_tpc; flushw(); switch (IF_OP(insn)) { case IOP_LDST: va = f3_memop_addr(tf, insn); switch (IF_F3_OP3(insn)) { case INS3_LDUB: val = *(uint8_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDUH: val = *(uint16_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDUW: val = *(uint32_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDX: val = *(uint64_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDSB: val = *(int8_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDSH: val = *(int16_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_LDSW: val = *(int32_t *)va; emul_store_reg(tf, IF_F3_RD(insn), val); break; case INS3_STB: val = emul_fetch_reg(tf, IF_F3_RD(insn)); *(uint8_t *)va = val; break; case INS3_STH: val = emul_fetch_reg(tf, IF_F3_RD(insn)); *(uint16_t *)va = val; break; case INS3_STW: val = emul_fetch_reg(tf, IF_F3_RD(insn)); *(uint32_t *)va = val; break; case INS3_STX: val = emul_fetch_reg(tf, IF_F3_RD(insn)); *(uint64_t *)va = val; break; default: panic("db_watch_trap: unimplemented instruction %#x", insn); break; } TF_DONE(tf); break; default: panic("db_watch_trap: instruction not load or store"); break; } if ((tf->tf_type & ~T_KERNEL) == T_PA_WATCHPOINT) watch_phys_set_mask(addr, mask); else watch_virt_set_mask(addr, mask); return (0); } int db_md_set_watchpoint(db_expr_t addr, db_expr_t size) { int dummy; if (watch_virt_active()) { db_printf("Overwriting previously active watch point at " "0x%lx\n", watch_virt_get(&dummy)); } return (watch_virt_set(addr, size)); } int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size) { watch_virt_clear(); return (0); } void db_watch_print(vm_offset_t wp, int bm) { int i; db_printf("\tat 0x%lx, active bytes: ", (u_long)wp); for (i = 0; i < 8; i++) { if ((bm & (1 << i)) != 0) db_printf("%d ", i); } if (bm == 0) db_printf("none"); db_printf("\n"); } void db_md_list_watchpoints(void) { vm_offset_t wp; int bm; db_printf("Physical address watchpoint:\n"); if (watch_phys_active()) { wp = watch_phys_get(&bm); db_watch_print(wp, bm); } else db_printf("\tnot active.\n"); db_printf("Virtual address watchpoint:\n"); if (watch_virt_active()) { wp = watch_virt_get(&bm); db_watch_print(wp, bm); } else db_printf("\tnot active.\n"); }