/* $OpenBSD: db_sstep.c,v 1.5 2006/05/03 18:12:52 miod Exp $ */ /* * Mach Operating System * Copyright (c) 1993-1991 Carnegie Mellon University * Copyright (c) 1991 OMRON Corporation * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include #include #include #include /* db_get_value() */ #include /* db_breakpoint_t */ #include /* * Support routines for software single step. * * Author: Daniel Stodolsky (danner@cs.cmu.edu) * */ /* * inst_load(ins) * Returns the number of words the instruction loads. byte, * half and word count as 1; double word as 2 */ int inst_load(u_int ins) { /* look at the top six bits, for starters */ switch (ins >> (32 - 6)) { case 0x0: /* xmem byte imm */ case 0x1: /* xmem word imm */ case 0x2: /* unsigned half-word load imm */ case 0x3: /* unsigned byte load imm */ case 0x5: /* signed word load imm */ case 0x6: /* signed half-word load imm */ case 0x7: /* signed byte load imm */ return (1); case 0x4: /* signed double word load imm */ return (2); case 0x3d: /* load/store/xmem scaled/unscaled instruction */ if ((ins & 0x0000c0e0) == 0x00000000) /* is ld/st/xmem */ /* look at bits 15-10 */ switch ((ins & 0x0000fc00) >> 10) { case 0x0: /* xmem byte */ case 0x1: /* xmem word */ case 0x2: /* unsigned half word */ case 0x3: /* unsigned byte load */ case 0x5: /* signed word load */ case 0x6: /* signed half-word load */ case 0x7: /* signed byte load */ return (1); case 0x4: /* signed double word load */ return (2); } break; } return (0); } /* * inst_store * Like inst_load, except for store instructions. */ int inst_store(u_int ins) { /* decode top 6 bits again */ switch (ins >> (32 - 6)) { case 0x0: /* xmem byte imm */ case 0x1: /* xmem word imm */ case 0x9: /* store word imm */ case 0xa: /* store half-word imm */ case 0xb: /* store byte imm */ return (1); case 0x8: /* store double word */ return (2); case 0x3d: /* load/store/xmem scaled/unscaled instruction */ if ((ins & 0x0000c0e0) == 0x00000000) /* is ld/st/xmem */ /* look at bits 15-10 */ switch ((ins & 0x0000fc00) >> 10) { case 0x0: /* xmem byte imm */ case 0x1: /* xmem word imm */ case 0x9: /* store word */ case 0xa: /* store half-word */ case 0xb: /* store byte */ return (1); case 0x8: /* store double word */ return (2); } break; } return (0); } /* * We can not use the MI ddb SOFTWARE_SSTEP facility, since the 88110 will use * hardware single stepping. * Moreover, our software single stepping implementation is tailor-made for the * 88100 and faster than the MI code. */ #ifdef M88100 boolean_t inst_branch_or_call(u_int); db_addr_t branch_taken(u_int, db_addr_t, db_regs_t *); db_breakpoint_t db_not_taken_bkpt = 0; db_breakpoint_t db_taken_bkpt = 0; /* * Returns TRUE is the instruction a branch, jump or call instruction * (br, bb0, bb1, bcnd, jmp, bsr, jsr) */ boolean_t inst_branch_or_call(u_int ins) { /* check high five bits */ switch (ins >> (32 - 5)) { case 0x18: /* br */ case 0x19: /* bsr */ case 0x1a: /* bb0 */ case 0x1b: /* bb1 */ case 0x1d: /* bcnd */ return (TRUE); case 0x1e: /* could be jmp or jsr */ if ((ins & 0xfffff3e0) == 0xf400c000) return (TRUE); } return (FALSE); } /* * branch_taken(instruction, program counter, regs) * * instruction will be a control flow instruction location at address pc. * Branch taken is supposed to return the address to which the instruction * would jump if the branch is taken. */ db_addr_t branch_taken(u_int inst, db_addr_t pc, db_regs_t *regs) { u_int regno; /* * Quick check of the instruction. Note that we know we are only * invoked if inst_branch_or_call() returns TRUE, so we do not * need to repeat the jmp and jsr stricter checks here. */ switch (inst >> (32 - 5)) { case 0x18: /* br */ case 0x19: /* bsr */ /* signed 26 bit pc relative displacement, shift left two bits */ inst = (inst & 0x03ffffff) << 2; /* check if sign extension is needed */ if (inst & 0x08000000) inst |= 0xf0000000; return (pc + inst); case 0x1a: /* bb0 */ case 0x1b: /* bb1 */ case 0x1d: /* bcnd */ /* signed 16 bit pc relative displacement, shift left two bits */ inst = (inst & 0x0000ffff) << 2; /* check if sign extension is needed */ if (inst & 0x00020000) inst |= 0xfffc0000; return (pc + inst); default: /* jmp or jsr */ regno = inst & 0x1f; return (regno == 0 ? 0 : regs->r[regno]); } } #endif /* M88100 */ void db_set_single_step(db_regs_t *regs) { #ifdef M88110 if (CPU_IS88110) { /* * On the 88110, we can use the hardware tracing facility... */ regs->epsr |= PSR_TRACE | PSR_SER; } #endif #ifdef M88100 if (CPU_IS88100) { /* * ... while the 88100 will use two breakpoints. */ db_addr_t pc = PC_REGS(regs); db_addr_t brpc; u_int inst; /* * User was stopped at pc, e.g. the instruction * at pc was not executed. */ db_read_bytes(pc, sizeof(inst), (caddr_t)&inst); /* * Find if this instruction may cause a branch, and set up a * breakpoint at the branch location. */ if (inst_branch_or_call(inst)) { brpc = branch_taken(inst, pc, regs); /* self-branches are hopeless */ if (brpc != pc && brpc != 0) db_taken_bkpt = db_set_temp_breakpoint(brpc); } db_not_taken_bkpt = db_set_temp_breakpoint(pc + 4); } #endif } void db_clear_single_step(regs) db_regs_t *regs; { #ifdef M88110 if (CPU_IS88110) { /* do not remove PSR_SER as we don't enable OoO */ regs->epsr &= ~PSR_TRACE; } #endif #ifdef M88100 if (CPU_IS88100) { if (db_taken_bkpt != 0) { db_delete_temp_breakpoint(db_taken_bkpt); db_taken_bkpt = 0; } if (db_not_taken_bkpt != 0) { db_delete_temp_breakpoint(db_not_taken_bkpt); db_not_taken_bkpt = 0; } } #endif }