📄 remote.cc
字号:
/* * avarice - The "avarice" program. * Copyright (C) 2001 Scott Finneran * Copyright (C) 2002 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * This file contains functions for interfacing with the GDB remote protocol. */#include <stdlib.h>#include <string.h>#include <termios.h>#include <sys/time.h>#include <unistd.h>#include <errno.h>#include <stdio.h>#include <fcntl.h>#include <signal.h>#include "avarice.h"#include "remote.h"#include "jtag.h"enum{ /** BUFMAX defines the maximum number of characters in * inbound/outbound buffers at least NUMREGBYTES*2 are needed for * register packets */ BUFMAX = 400, NUMREGS = 32/* + 1 + 1 + 1*/, /* SREG, FP, PC */ SREG = 32, SP = 33, PC = 34, // Number of bytes of registers. See GDB gdb/avr-tdep.c NUMREGBYTES = (NUMREGS + 1 + 2 + 4),};static char remcomInBuffer[BUFMAX];static char remcomOutBuffer[BUFMAX];static void ok();static void error(int n);int gdbFileDescriptor = -1;void gdbCheck(int status){ unixCheck(status, GDB_CAUSE);}void setGdbFile(int fd){ gdbFileDescriptor = fd; int ret = fcntl(gdbFileDescriptor, F_SETFL, O_NONBLOCK); gdbCheck(ret);}static void waitForGdbOutput(void){ int numfds; fd_set writefds; FD_ZERO (&writefds); FD_SET (gdbFileDescriptor, &writefds); numfds = select (gdbFileDescriptor + 1, 0, &writefds, 0, 0); gdbCheck(numfds);}/** Send single char to gdb. Abort in case of problem. **/static void putDebugChar(char c){ // This loop busy waits when it cannot write to gdb. // But that shouldn't happen for (;;) { int ret = write(gdbFileDescriptor, &c, 1); if (ret == 1) return; if (ret == 0) // this shouldn't happen? check(false, GDB_CAUSE); if (errno != EAGAIN) gdbCheck(ret); waitForGdbOutput(); }}static void waitForGdbInput(void){ int numfds; fd_set readfds; FD_ZERO (&readfds); FD_SET (gdbFileDescriptor, &readfds); numfds = select (gdbFileDescriptor + 1, &readfds, 0, 0, 0); gdbCheck(numfds);}/** Return single char read from gdb. Abort in case of problem, exit cleanly if EOF detected on gdbFileDescriptor. **/int getDebugChar(void){ uchar c = 0; int result; do { waitForGdbInput(); result = read(gdbFileDescriptor, &c, 1); } while (result < 0 && errno == EAGAIN); gdbCheck(result); if (result == 0) // gdb exited { statusOut("gdb exited.\n"); resumeProgram(); exit(0); } return (int)c;}int checkForDebugChar(void){ uchar c = 0; int result; result = read(gdbFileDescriptor, &c, 1); if (result < 0 && errno == EAGAIN) return -1; gdbCheck(result); if (result == 0) // gdb exited { statusOut("gdb exited.\n"); resumeProgram(); exit(0); } return (int)c;} static const unsigned char hexchars[] = "0123456789abcdef";static char *byteToHex(uchar x, char *buf){ *buf++ = hexchars[x >> 4]; *buf++ = hexchars[x & 0xf]; return buf;}static int hex(unsigned char ch){ if((ch >= 'a') && (ch <= 'f')) { return (ch - 'a' + 10); } if((ch >= '0') && (ch <= '9')) { return (ch - '0'); } if((ch >= 'A') && (ch <= 'F')) { return (ch - 'A' + 10); } return (-1);}/** Convert hex string at '*ptr' to an integer. Advances '*ptr' to 1st non-hex character found. Returns number of characters used in conversion. **/static int hexToInt(char **ptr, int *intValue){ int numChars = 0; int hexValue; *intValue = 0; while (**ptr) { hexValue = hex(**ptr); if(hexValue >= 0) { *intValue = (*intValue << 4) | hexValue; numChars++; } else { break; } (*ptr)++; } return (numChars);}/** Convert the memory pointed to by mem into hex, placing result in buf. Return a pointer to the last char put in buf (null).**/static char *mem2hex(uchar *mem, char *buf, int count){ for (int i = 0; i < count; i++) buf = byteToHex(*mem++, buf); *buf = 0; return (buf);}/** Convert the hex array pointed to by buf into binary to be placed in mem. Return a pointer to the character AFTER the last byte written.**/static uchar *hex2mem(char *buf, uchar *mem, int count){ int i; unsigned char ch; for(i = 0; i < count; i++) { ch = hex(*buf++) << 4; ch = ch + hex(*buf++); *mem++ = ch; } return (mem);}/** Convert the binary stream in BUF to memory. Gdb will escape $, #, and the escape char (0x7d). 'count' is the total number of bytes to write into memory.**/static uchar *bin2mem(char *buf, uchar *mem, int count){ int i; for(i = 0; i < count; i++) { // Check for any escaped characters. Be paranoid and // only unescape chars that should be escaped. if(*buf == 0x7d) { switch (*(buf + 1)) { case 0x3: // # case 0x4: // $ case 0x5d: // escape char buf++; *buf |= 0x20; break; default: // nothing break; } } *mem++ = *buf++; } return mem;}static void putpacket(char *buffer);void vgdbOut(const char *fmt, va_list args){ // We protect against reentry because putpacket could try and report // an error which would lead to a call back to vgdbOut static bool reentered = false; if (gdbFileDescriptor >= 0 && !reentered) { char textbuf[BUFMAX], hexbuf[2 * BUFMAX], *textscan, *hexscan; reentered = true; vsnprintf(textbuf, BUFMAX, fmt, args); hexscan = hexbuf; *hexscan++ = 'O'; for (textscan = textbuf; *textscan; textscan++) hexscan = byteToHex(*textscan, hexscan); *hexscan = '\0'; putpacket(hexbuf); reentered = false; }}void gdbOut(const char *fmt, ...){ va_list args; va_start(args, fmt); vgdbOut(fmt, args); va_end(args);}/** Fill 'remcomOutBuffer' with a status report for signal 'sigval' Reply with a packet of the form: "Tss20:rr;21:llhh;22:aabbccdd;" where (all values in hex): ss = signal number (usually SIGTRAP) rr = SREG value llhh = SPL:SPH (stack pointer) aabbccdd = PC (program counter) This actually saves having to read the 32 general registers when stepping over code since gdb won't send a 'g' packet until the PC it is hunting for is found. */static void reportStatusExtended(int sigval){ uchar *jtagBuffer; unsigned long pc = getProgramCounter(); // Read in SPL SPH SREG jtagBuffer = jtagRead(0x5D + DATA_SPACE_ADDR_OFFSET, 0x03); if (jtagBuffer) { // We have SPL SPH SREG and need SREG SPL SPH snprintf (remcomOutBuffer, sizeof(remcomOutBuffer), "T%02x" "20:%02x;" "21:%02x%02x;" "22:%02x%02x%02x%02x;", sigval & 0xff, jtagBuffer[2], // SREG jtagBuffer[0], // SPL jtagBuffer[1], // SPH pc & 0xff, (pc >> 8) & 0xff, (pc >> 16) & 0xff, (pc >> 24) & 0xff); delete [] jtagBuffer; jtagBuffer = 0; } else { error(1); return; }}/** Fill 'remcomOutBuffer' with a status report for signal 'sigval' **/static void reportStatus(int sigval){ char *ptr = remcomOutBuffer; // We could use 'T'. But this requires reading SREG, SP, PC at least, so // won't be significantly faster than the 'g' operation that gdb will // request if we use 'S' here. [TRoth/2003-06-12: this is not necessarily // true. See above comment for reportStatusExtended().] *ptr++ = 'S'; // notify gdb with signo ptr = byteToHex(sigval, ptr); *ptr++ = 0;}// little-endian word readunsigned int readLWord(unsigned int address){ unsigned char *mem = jtagRead(DATA_SPACE_ADDR_OFFSET + address, 2); if (!mem) return 0; // hmm unsigned int val = mem[0] | mem[1] << 8; delete[] mem; return val;}// big-endian word readunsigned int readBWord(unsigned int address){ unsigned char *mem = jtagRead(DATA_SPACE_ADDR_OFFSET + address, 2); if (!mem) return 0; // hmm unsigned int val = mem[0] << 8 | mem[1]; delete[] mem; return val;}unsigned int readSP(void){ return readLWord(0x5d);}bool handleInterrupt(void){ bool result; // Set a breakpoint at return address debugOut("INTERRUPT\n"); unsigned int intrSP = readSP(); unsigned int retPC = readBWord(intrSP + 1) << 1; debugOut("INT SP = %x, retPC = %x\n", intrSP, retPC); bool needBP = !codeBreakpointAt(retPC); for (;;) { if (needBP) { // If no breakpoint at return address (normal case), // add one. // Normally, this breakpoint add should succeed as gdb shouldn't // be using a momentary breakpoint when doing a step-through-range, // thus leaving is a free hw breakpoint. But if for some reason it // the add fails, interrupt the program at the interrupt handler // entry point if (!addBreakpoint(retPC, CODE, 0)) return false; } result = jtagContinue(true); if (needBP) deleteBreakpoint(retPC, CODE, 0); if (!result || !needBP) // user interrupt or hit user BP at retPC break; // We check that SP is > intrSP. If SP <= intrSP, this is just // an unrelated excursion to retPC if (readSP() > intrSP) break; } return result;}static bool stepThrough(int start, int end){ // Try and use a breakpoint at end and "break on change of flow" // This doesn't seem to provide much benefit... bool flowIntr = !codeBreakpointBetween(start, end); for (;;) { if (flowIntr) { setJtagParameter(JTAG_P_BP_FLOW, 1); stopAt(end); if (!jtagContinue(false)) return false; } else { if (!jtagSingleStep()) gdbOut("Failed to single-step"); int gdbIn = checkForDebugChar(); if (gdbIn >= 0) if (gdbIn == 3) return false; else debugOut("Unexpected GDB input `%02x'\n", gdbIn); } for (;;) { int newPC = getProgramCounter(); if (codeBreakpointAt(newPC)) return true; if (newPC >= start && newPC < end) break; // assume interrupt when PC goes into interrupt table if (ignoreInterrupts && newPC < global_p_device_def->vectors_end) { if (!handleInterrupt()) return false; } else return true; } }}static bool singleStep(){ if (!jtagSingleStep()) gdbOut("Failed to single-step"); int newPC = getProgramCounter(); if (codeBreakpointAt(newPC)) return true; // assume interrupt when PC goes into interrupt table if (ignoreInterrupts && newPC < global_p_device_def->vectors_end) return handleInterrupt(); return true;}/** Read packet from gdb into remcomInBuffer, check checksum and confirm reception to gdb. Return pointer to null-terminated, actual packet data (without $, #, the checksum)**/static char *getpacket(void){ char *buffer = &remcomInBuffer[0]; unsigned char checksum; unsigned char xmitcsum; int count; char ch; // scan for the sequence $<data>#<checksum> while(1) { // wait around for the start character, ignore all other characters while((ch = getDebugChar()) != '$') ; retry: checksum = 0; xmitcsum = 0; // This was -1 but compiler got upset count = 0; // now, read until a # or end of buffer is found while(count < BUFMAX - 1) { ch = getDebugChar(); if(ch == '$') { goto retry; } if(ch == '#') { break; } checksum = checksum + ch; buffer[count] = ch; count = count + 1; } buffer[count] = 0; if(ch == '#') { ch = getDebugChar(); xmitcsum = hex(ch) << 4; ch = getDebugChar(); xmitcsum += hex(ch); if(checksum != xmitcsum) { char buf[16];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -