You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
333 lines
11 KiB
C
333 lines
11 KiB
C
/*
|
|
* Copyright (C) 2018 bzt (bztsrc@github)
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use, copy,
|
|
* modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include "uart.h"
|
|
|
|
#define DISASSEMBLER 1
|
|
|
|
// array to store register values (see dbg_saveregs in start.S)
|
|
unsigned long dbg_regs[37];
|
|
// command line
|
|
char cmd[256], dbg_running=0;
|
|
|
|
#if DISASSEMBLER
|
|
/**
|
|
* things needed by the disassembler
|
|
*/
|
|
#include "sprintf.h"
|
|
#define NULL ((void*)0)
|
|
typedef unsigned long uint64_t;
|
|
typedef unsigned int uint32_t;
|
|
typedef unsigned short uint16_t;
|
|
typedef unsigned char uint8_t;
|
|
// include the Universal Disassembler Library
|
|
#include "disasm.h"
|
|
#endif
|
|
|
|
/**
|
|
* Decode exception cause
|
|
*/
|
|
void dbg_decodeexc(unsigned long type)
|
|
{
|
|
unsigned char cause=dbg_regs[33]>>26;
|
|
|
|
// print out interruption type
|
|
switch(type) {
|
|
case 0: printf("Synchronous"); break;
|
|
case 1: printf("IRQ"); break;
|
|
case 2: printf("FIQ"); break;
|
|
case 3: printf("SError"); break;
|
|
}
|
|
printf(": ");
|
|
// decode exception type (some, not all. See ARM DDI0487B_b chapter D10.2.28)
|
|
switch(cause) {
|
|
case 0b000000: printf("Unknown"); break;
|
|
case 0b000001: printf("Trapped WFI/WFE"); break;
|
|
case 0b001110: printf("Illegal execution"); break;
|
|
case 0b010101: printf("System call"); break;
|
|
case 0b100000: printf("Instruction abort, lower EL"); break;
|
|
case 0b100001: printf("Instruction abort, same EL"); break;
|
|
case 0b100010: printf("Instruction alignment fault"); break;
|
|
case 0b100100: printf("Data abort, lower EL"); break;
|
|
case 0b100101: printf("Data abort, same EL"); break;
|
|
case 0b100110: printf("Stack alignment fault"); break;
|
|
case 0b101100: printf("Floating point"); break;
|
|
case 0b110000: printf("Breakpoint, lower EL"); break;
|
|
case 0b110001: printf("Breakpoint, same EL"); break;
|
|
case 0b111100: printf("Breakpoint instruction"); break;
|
|
default: printf("Unknown %x", cause); break;
|
|
}
|
|
// decode data abort cause
|
|
if(cause==0b100100 || cause==0b100101) {
|
|
printf(", ");
|
|
switch((dbg_regs[33]>>2)&0x3) {
|
|
case 0: printf("Address size fault"); break;
|
|
case 1: printf("Translation fault"); break;
|
|
case 2: printf("Access flag fault"); break;
|
|
case 3: printf("Permission fault"); break;
|
|
}
|
|
switch(dbg_regs[33]&0x3) {
|
|
case 0: printf(" at level 0"); break;
|
|
case 1: printf(" at level 1"); break;
|
|
case 2: printf(" at level 2"); break;
|
|
case 3: printf(" at level 3"); break;
|
|
}
|
|
}
|
|
printf("\n");
|
|
// if the exception happened in the debugger, we stop to avoid infinite loop
|
|
if(dbg_running) {
|
|
printf("Exception in debugger!\n"
|
|
" elr_el1: %x spsr_el1: %x\n esr_el1: %x far_el1: %x\nsctlr_el1: %x tcr_el1: %x\n",
|
|
dbg_regs[31],dbg_regs[32],dbg_regs[33],dbg_regs[34],dbg_regs[35],dbg_regs[36]);
|
|
while(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* helper to read a line from user. We redefine some control caracters to handle CSI
|
|
* \e[3~ = 1, delete
|
|
* \e[D = 2, cursor left
|
|
* \e[C = 3, cursor right
|
|
*/
|
|
void dbg_getline()
|
|
{
|
|
int i,cmdidx=0,cmdlast=0;
|
|
char c;
|
|
cmd[0]=0;
|
|
// prompt
|
|
printf("\r> ");
|
|
// read until Enter pressed
|
|
while((c=uart_getc())!='\n') {
|
|
// decode CSI key sequences (some, not all)
|
|
if(c==27) {
|
|
c=uart_getc();
|
|
if(c=='[') {
|
|
c=uart_getc();
|
|
if(c=='C') c=3; else // left
|
|
if(c=='D') c=2; else // right
|
|
if(c=='3') {
|
|
c=uart_getc();
|
|
if(c=='~') c=1; // delete
|
|
}
|
|
}
|
|
}
|
|
// Backspace
|
|
if(c==8 || c==127) {
|
|
if(cmdidx>0) {
|
|
cmdidx--;
|
|
for(i=cmdidx;i<cmdlast;i++) cmd[i]=cmd[i+1];
|
|
cmdlast--;
|
|
}
|
|
} else
|
|
// Delete
|
|
if(c==1) {
|
|
if(cmdidx<cmdlast) {
|
|
for(i=cmdidx;i<cmdlast;i++) cmd[i]=cmd[i+1];
|
|
cmdlast--;
|
|
}
|
|
} else
|
|
// cursor left
|
|
if(c==2) {
|
|
if(cmdidx>0) cmdidx--;
|
|
} else
|
|
// cursor right
|
|
if(c==3) {
|
|
if(cmdidx<cmdlast) cmdidx++;
|
|
} else {
|
|
// is there a valid character and space to store it?
|
|
if(c<' ' || cmdlast>=sizeof(cmd)-1) {
|
|
continue;
|
|
}
|
|
// if we're not appending, move bytes after cursor
|
|
if(cmdidx<cmdlast) {
|
|
for(i=cmdlast;i>cmdidx;i--)
|
|
cmd[i]=cmd[i-1];
|
|
}
|
|
cmdlast++;
|
|
cmd[cmdidx++]=c;
|
|
}
|
|
cmd[cmdlast]=0;
|
|
// display prompt and command line, place cursor with CSI code
|
|
printf("\r> %s \r\e[%dC",cmd,cmdidx+2);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
/**
|
|
* helper function to parse the command line for arguments
|
|
*/
|
|
unsigned long dbg_getoffs(int i)
|
|
{
|
|
unsigned long base=0,ret=0;
|
|
int j=0,sign=0;
|
|
// if starts with a register
|
|
if(cmd[i]=='x' || cmd[i]=='r') {
|
|
i++; if(cmd[i]>='0' && cmd[i]<='9') { j=cmd[i]-'0'; }
|
|
i++; if(cmd[i]>='0' && cmd[i]<='9') { j*=10; j+=cmd[i]-'0'; }
|
|
if(j>=0 && j<37) base=dbg_regs[j];
|
|
i++;
|
|
if(cmd[i]=='-') { i++; sign++; }
|
|
if(cmd[i]=='+') i++;
|
|
}
|
|
// offset part
|
|
if(cmd[i]=='0' && cmd[i+1]=='x') {
|
|
i+=2;
|
|
// hex value
|
|
while((cmd[i]>='0'&&cmd[i]<='9')||(cmd[i]>='a'&&cmd[i]<='f')||(cmd[i]>='A'&&cmd[i]<='F')) {
|
|
ret <<= 4;
|
|
if(cmd[i]>='0' && cmd[i]<='9') ret += cmd[i]-'0';
|
|
else if(cmd[i] >= 'a' && cmd[i] <= 'f') ret += cmd[i]-'a'+10;
|
|
else if(cmd[i] >= 'A' && cmd[i] <= 'F') ret += cmd[i]-'A'+10;
|
|
i++;
|
|
}
|
|
} else {
|
|
// decimal value
|
|
while(cmd[i]>='0'&&cmd[i]<='9'){
|
|
ret *= 10;
|
|
ret += cmd[i++]-'0';
|
|
}
|
|
}
|
|
// return base + offset
|
|
return sign? base-ret : base+ret;
|
|
}
|
|
|
|
/**
|
|
* main loop, get and parse commands
|
|
*/
|
|
void dbg_main()
|
|
{
|
|
unsigned long os=0, oe=0, a;
|
|
char c;
|
|
#if DISASSEMBLER
|
|
char str[64];
|
|
#endif
|
|
int i;
|
|
|
|
dbg_running++;
|
|
|
|
// main debugger loop
|
|
while(1) {
|
|
// get command from user
|
|
dbg_getline();
|
|
// parse commands
|
|
if(cmd[0]==0 || cmd[0]=='?' || cmd[0]=='h') {
|
|
// print help
|
|
printf("Mini debugger commands:\n"
|
|
" ?/h\t\tthis help\n"
|
|
" r\t\tdump registers\n"
|
|
" x [os [oe]]\texamine memory from offset start (os) to offset end (oe)\n"
|
|
" i [os [oe]]\tdisassemble instruction from offset start to offset end\n"
|
|
" c\t\tcontinue execution\n");
|
|
continue;
|
|
} else
|
|
// continue execution
|
|
if(cmd[0]=='c') {
|
|
// move instruction pointer, skip over 'brk'
|
|
asm volatile ("msr elr_el1, %0" : : "r" (dbg_regs[31]+4));
|
|
break;
|
|
} else
|
|
// dump registers
|
|
if(cmd[0]=='r') {
|
|
// general purpose registers x0-x30
|
|
for(i=0;i<31;i++) {
|
|
if(i && i%3==0) printf("\n");
|
|
if(i<10) printf(" ");
|
|
printf("x%d: %16x ",i,dbg_regs[i]);
|
|
}
|
|
// some system registers
|
|
printf("elr_el1: %x spsr_el1: %x\n esr_el1: %x far_el1: %x\nsctlr_el1: %x tcr_el1: %x\n",
|
|
dbg_regs[31],dbg_regs[32],dbg_regs[33],dbg_regs[34],dbg_regs[35],dbg_regs[36]);
|
|
continue;
|
|
} else
|
|
// examine or disassemble, commands with arguments
|
|
if(cmd[0]=='x' || cmd[0]=='i') {
|
|
i=1;
|
|
// get first argument
|
|
while(cmd[i]!=0 && cmd[i]!=' ') i++; // skip command
|
|
while(cmd[i]!=0 && cmd[i]==' ') i++; // skip separators
|
|
if(cmd[i]!=0) {
|
|
os=oe=dbg_getoffs(i);
|
|
// get second argument
|
|
while(cmd[i]!=0 && cmd[i]!=' ') i++; // skip 1st arg
|
|
while(cmd[i]!=0 && cmd[i]==' ') i++; // skip separators
|
|
if(cmd[i]!=0) {
|
|
oe=dbg_getoffs(i);
|
|
}
|
|
} else {
|
|
// no arguments, use defaults
|
|
if(cmd[0]=='i') {
|
|
// elr or lr (x30)
|
|
os=oe=dbg_regs[31]?dbg_regs[31]:dbg_regs[30];
|
|
} else {
|
|
// sp (x29)
|
|
os=oe=dbg_regs[29];
|
|
}
|
|
}
|
|
// do the thing
|
|
if(cmd[0]=='i') {
|
|
// must be multiple of 4
|
|
os=os&~3L;
|
|
oe=(oe+3)&~3L;
|
|
if(oe<=os) oe=os+4;
|
|
// disassemble AArch64 bytecode
|
|
while(os<oe) {
|
|
// print out address and instruction bytecode
|
|
printf("%8x: %8x",os,*((unsigned int*)os));
|
|
#if DISASSEMBLER
|
|
// disassemble and print out instruction mnemonic
|
|
os=disasm(os,str);
|
|
printf("\t%s\n",str);
|
|
#else
|
|
os+=4;
|
|
printf("\n");
|
|
#endif
|
|
}
|
|
} else {
|
|
// dump memory
|
|
if(oe<=os) oe=os+16;
|
|
// for each 16 bytes, do
|
|
for(a=os;a<oe;a+=16) {
|
|
// print out address
|
|
printf("%8x: ", a);
|
|
// hex representation
|
|
for(i=0;i<16;i++) {
|
|
printf("%2x%s ",*((unsigned char*)(a+i)),i%4==3?" ":"");
|
|
}
|
|
// character representation
|
|
for(i=0;i<16;i++) {
|
|
c=*((unsigned char*)(a+i));
|
|
printf("%c",c<32||c>=127?'.':c);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
continue;
|
|
} else {
|
|
printf("ERROR: unknown command.\n");
|
|
}
|
|
}
|
|
dbg_running--;
|
|
}
|