123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /*
- * 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--;
- }
|