📄 kgdb_stub.c
字号:
char *src; int runlen; int encode; do { src = buffer; put_debug_char('$'); checksum = 0; /* Continue while we still have chars left */ while (*src) { /* Check for runs up to 99 chars long */ for (runlen = 1; runlen < 99; runlen++) { if (src[0] != src[runlen]) break; } if (runlen > 3) { /* Got a useful amount, send encoding */ encode = runlen + ' ' - 4; put_debug_char(*src); checksum += *src; put_debug_char('*'); checksum += '*'; put_debug_char(encode); checksum += encode; src += runlen; } else { /* Otherwise just send the current char */ put_debug_char(*src); checksum += *src; src += 1; } } /* '#' Separator, put high and low components of checksum */ put_debug_char('#'); put_debug_char(highhex(checksum)); put_debug_char(lowhex(checksum)); } while ((get_debug_char()) != '+'); /* While no ack */}/* A bus error has occurred - perform a longjmp to return execution and allow handling of the error */static void kgdb_handle_bus_error(void){ longjmp(rem_com_env, 1);}/* Translate SH-3/4 exception numbers to unix-like signal values */static int compute_signal(const int excep_code){ int sigval; switch (excep_code) { case INVALID_INSN_VEC: case INVALID_SLOT_VEC: sigval = SIGILL; break; case ADDRESS_ERROR_LOAD_VEC: case ADDRESS_ERROR_STORE_VEC: sigval = SIGSEGV; break; case SERIAL_BREAK_VEC: case NMI_VEC: sigval = SIGINT; break; case USER_BREAK_VEC: case TRAP_VEC: sigval = SIGTRAP; break; default: sigval = SIGBUS; /* "software generated" */ break; } return (sigval);}/* Make a local copy of the registers passed into the handler (bletch) */static void kgdb_regs_to_gdb_regs(const struct kgdb_regs *regs, int *gdb_regs){ gdb_regs[R0] = regs->regs[R0]; gdb_regs[R1] = regs->regs[R1]; gdb_regs[R2] = regs->regs[R2]; gdb_regs[R3] = regs->regs[R3]; gdb_regs[R4] = regs->regs[R4]; gdb_regs[R5] = regs->regs[R5]; gdb_regs[R6] = regs->regs[R6]; gdb_regs[R7] = regs->regs[R7]; gdb_regs[R8] = regs->regs[R8]; gdb_regs[R9] = regs->regs[R9]; gdb_regs[R10] = regs->regs[R10]; gdb_regs[R11] = regs->regs[R11]; gdb_regs[R12] = regs->regs[R12]; gdb_regs[R13] = regs->regs[R13]; gdb_regs[R14] = regs->regs[R14]; gdb_regs[R15] = regs->regs[R15]; gdb_regs[PC] = regs->pc; gdb_regs[PR] = regs->pr; gdb_regs[GBR] = regs->gbr; gdb_regs[MACH] = regs->mach; gdb_regs[MACL] = regs->macl; gdb_regs[SR] = regs->sr; gdb_regs[VBR] = regs->vbr;}/* Copy local gdb registers back to kgdb regs, for later copy to kernel */static void gdb_regs_to_kgdb_regs(const int *gdb_regs, struct kgdb_regs *regs){ regs->regs[R0] = gdb_regs[R0]; regs->regs[R1] = gdb_regs[R1]; regs->regs[R2] = gdb_regs[R2]; regs->regs[R3] = gdb_regs[R3]; regs->regs[R4] = gdb_regs[R4]; regs->regs[R5] = gdb_regs[R5]; regs->regs[R6] = gdb_regs[R6]; regs->regs[R7] = gdb_regs[R7]; regs->regs[R8] = gdb_regs[R8]; regs->regs[R9] = gdb_regs[R9]; regs->regs[R10] = gdb_regs[R10]; regs->regs[R11] = gdb_regs[R11]; regs->regs[R12] = gdb_regs[R12]; regs->regs[R13] = gdb_regs[R13]; regs->regs[R14] = gdb_regs[R14]; regs->regs[R15] = gdb_regs[R15]; regs->pc = gdb_regs[PC]; regs->pr = gdb_regs[PR]; regs->gbr = gdb_regs[GBR]; regs->mach = gdb_regs[MACH]; regs->macl = gdb_regs[MACL]; regs->sr = gdb_regs[SR]; regs->vbr = gdb_regs[VBR];}#ifdef CONFIG_KGDB_THREAD/* Make a local copy of registers from the specified thread */asmlinkage void ret_from_fork(void);static void thread_regs_to_gdb_regs(const struct task_struct *thread, int *gdb_regs){ int regno; int *tregs; /* Initialize to zero */ for (regno = 0; regno < MAXREG; regno++) gdb_regs[regno] = 0; /* Just making sure... */ if (thread == NULL) return; /* A new fork has pt_regs on the stack from a fork() call */ if (thread->thread.pc == (unsigned long)ret_from_fork) { int vbr_val; struct pt_regs *kregs; kregs = (struct pt_regs*)thread->thread.sp; gdb_regs[R0] = kregs->regs[R0]; gdb_regs[R1] = kregs->regs[R1]; gdb_regs[R2] = kregs->regs[R2]; gdb_regs[R3] = kregs->regs[R3]; gdb_regs[R4] = kregs->regs[R4]; gdb_regs[R5] = kregs->regs[R5]; gdb_regs[R6] = kregs->regs[R6]; gdb_regs[R7] = kregs->regs[R7]; gdb_regs[R8] = kregs->regs[R8]; gdb_regs[R9] = kregs->regs[R9]; gdb_regs[R10] = kregs->regs[R10]; gdb_regs[R11] = kregs->regs[R11]; gdb_regs[R12] = kregs->regs[R12]; gdb_regs[R13] = kregs->regs[R13]; gdb_regs[R14] = kregs->regs[R14]; gdb_regs[R15] = kregs->regs[R15]; gdb_regs[PC] = kregs->pc; gdb_regs[PR] = kregs->pr; gdb_regs[GBR] = kregs->gbr; gdb_regs[MACH] = kregs->mach; gdb_regs[MACL] = kregs->macl; gdb_regs[SR] = kregs->sr; asm("stc vbr, %0":"=r"(vbr_val)); gdb_regs[VBR] = vbr_val; return; } /* Otherwise, we have only some registers from switch_to() */ tregs = (int *)thread->thread.sp; gdb_regs[R15] = (int)tregs; gdb_regs[R14] = *tregs++; gdb_regs[R13] = *tregs++; gdb_regs[R12] = *tregs++; gdb_regs[R11] = *tregs++; gdb_regs[R10] = *tregs++; gdb_regs[R9] = *tregs++; gdb_regs[R8] = *tregs++; gdb_regs[PR] = *tregs++; gdb_regs[GBR] = *tregs++; gdb_regs[PC] = thread->thread.pc;}#endif /* CONFIG_KGDB_THREAD *//* Calculate the new address for after a step */static short *get_step_address(void){ short op = *(short *) trap_registers.pc; long addr; /* BT */ if (OPCODE_BT(op)) { if (trap_registers.sr & SR_T_BIT_MASK) addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); else addr = trap_registers.pc + 2; } /* BTS */ else if (OPCODE_BTS(op)) { if (trap_registers.sr & SR_T_BIT_MASK) addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); else addr = trap_registers.pc + 4; /* Not in delay slot */ } /* BF */ else if (OPCODE_BF(op)) { if (!(trap_registers.sr & SR_T_BIT_MASK)) addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); else addr = trap_registers.pc + 2; } /* BFS */ else if (OPCODE_BFS(op)) { if (!(trap_registers.sr & SR_T_BIT_MASK)) addr = trap_registers.pc + 4 + OPCODE_BTF_DISP(op); else addr = trap_registers.pc + 4; /* Not in delay slot */ } /* BRA */ else if (OPCODE_BRA(op)) addr = trap_registers.pc + 4 + OPCODE_BRA_DISP(op); /* BRAF */ else if (OPCODE_BRAF(op)) addr = trap_registers.pc + 4 + trap_registers.regs[OPCODE_BRAF_REG(op)]; /* BSR */ else if (OPCODE_BSR(op)) addr = trap_registers.pc + 4 + OPCODE_BSR_DISP(op); /* BSRF */ else if (OPCODE_BSRF(op)) addr = trap_registers.pc + 4 + trap_registers.regs[OPCODE_BSRF_REG(op)]; /* JMP */ else if (OPCODE_JMP(op)) addr = trap_registers.regs[OPCODE_JMP_REG(op)]; /* JSR */ else if (OPCODE_JSR(op)) addr = trap_registers.regs[OPCODE_JSR_REG(op)]; /* RTS */ else if (OPCODE_RTS(op)) addr = trap_registers.pr; /* RTE */ else if (OPCODE_RTE(op)) addr = trap_registers.regs[15]; /* Other */ else addr = trap_registers.pc + 2; kgdb_flush_icache_range(addr, addr + 2); return (short *) addr;}/* Set up a single-step. Replace the instruction immediately after the current instruction (i.e. next in the expected flow of control) with a trap instruction, so that returning will cause only a single instruction to be executed. Note that this model is slightly broken for instructions with delay slots (e.g. B[TF]S, BSR, BRA etc), where both the branch and the instruction in the delay slot will be executed. */static void do_single_step(void){ unsigned short *addr = 0; /* Determine where the target instruction will send us to */ addr = get_step_address(); stepped_address = (int)addr; /* Replace it */ stepped_opcode = *(short *)addr; *addr = STEP_OPCODE; /* Flush and return */ kgdb_flush_icache_range((long) addr, (long) addr + 2); return;}/* Undo a single step */static void undo_single_step(void){ /* If we have stepped, put back the old instruction */ /* Use stepped_address in case we stopped elsewhere */ if (stepped_opcode != 0) { *(short*)stepped_address = stepped_opcode; kgdb_flush_icache_range(stepped_address, stepped_address + 2); } stepped_opcode = 0;}/* Send a signal message */static void send_signal_msg(const int signum){#ifndef CONFIG_KGDB_THREAD out_buffer[0] = 'S'; out_buffer[1] = highhex(signum); out_buffer[2] = lowhex(signum); out_buffer[3] = 0; put_packet(out_buffer);#else /* CONFIG_KGDB_THREAD */ int threadid; threadref thref; char *out = out_buffer; const char *tstring = "thread"; *out++ = 'T'; *out++ = highhex(signum); *out++ = lowhex(signum); while (*tstring) { *out++ = *tstring++; } *out++ = ':'; threadid = trapped_thread->pid; if (threadid == 0) threadid = PID_MAX; int_to_threadref(&thref, threadid); pack_threadid(out, &thref); out += BUF_THREAD_ID_SIZE; *out++ = ';'; *out = 0; put_packet(out_buffer);#endif /* CONFIG_KGDB_THREAD */}/* Reply that all was well */static void send_ok_msg(void){ strcpy(out_buffer, "OK"); put_packet(out_buffer);}/* Reply that an error occurred */static void send_err_msg(void){ strcpy(out_buffer, "E01"); put_packet(out_buffer);}/* Empty message indicates unrecognised command */static void send_empty_msg(void){ put_packet("");}/* Read memory due to 'm' message */static void read_mem_msg(void){ char *ptr; int addr; int length; /* Jmp, disable bus error handler */ if (setjmp(rem_com_env) == 0) { kgdb_nofault = 1; /* Walk through, have m<addr>,<length> */ ptr = &in_buffer[1]; if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) if (hex_to_int(&ptr, &length)) { ptr = 0; if (length * 2 > OUTBUFMAX) length = OUTBUFMAX / 2; mem_to_hex((char *) addr, out_buffer, length); } if (ptr) send_err_msg(); else put_packet(out_buffer); } else send_err_msg(); /* Restore bus error handler */ kgdb_nofault = 0;}/* Write memory due to 'M' or 'X' message */static void write_mem_msg(int binary){ char *ptr; int addr; int length; if (setjmp(rem_com_env) == 0) { kgdb_nofault = 1; /* Walk through, have M<addr>,<length>:<data> */ ptr = &in_buffer[1]; if (hex_to_int(&ptr, &addr) && (*ptr++ == ',')) if (hex_to_int(&ptr, &length) && (*ptr++ == ':')) { if (binary) ebin_to_mem(ptr, (char*)addr, length); else hex_to_mem(ptr, (char*)addr, length); kgdb_flush_icache_range(addr, addr + length); ptr = 0; send_ok_msg(); } if (ptr) send_err_msg(); } else send_err_msg(); /* Restore bus error handler */ kgdb_nofault = 0;}/* Continue message */static void continue_msg(void){ /* Try to read optional parameter, PC unchanged if none */ char *ptr = &in_buffer[1]; int addr; if (hex_to_int(&ptr, &addr)) trap_registers.pc = addr;}/* Continue message with signal */static void continue_with_sig_msg(void){ int signal; char *ptr = &in_buffer[1]; int addr; /* Report limitation */ kgdb_to_gdb("Cannot force signal in kgdb, continuing anyway.\n"); /* Signal */ hex_to_int(&ptr, &signal); if (*ptr == ';') ptr++; /* Optional address */ if (hex_to_int(&ptr, &addr)) trap_registers.pc = addr;}/* Step message */static void step_msg(void){ continue_msg(); do_single_step();}/* Step message with signal */static void step_with_sig_msg(void){ continue_with_sig_msg(); do_single_step();}/* Send register contents */static void send_regs_msg(void){#ifdef CONFIG_KGDB_THREAD if (!current_thread) kgdb_regs_to_gdb_regs(&trap_registers, registers); else thread_regs_to_gdb_regs(current_thread, registers);#else kgdb_regs_to_gdb_regs(&trap_registers, registers);#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -