/* debug.c: a handy utility to view registers & memory for NetWinder Copyright 1998 woody@corelcomputer.com Changes for Linux 2.2 Pat Beirne mostly involve change the access from mem_read/write to mem_mmap This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #define DEB_BYTE 1 #define DEB_WORD 2 #define DEB_DWORD 4 #define CMD_DUMP 'd' #define CMD_EDIT 'e' #define CMD_INPUT 'i' #define CMD_OUTPUT 'o' #define CMD_SUPER_IN "si" #define CMD_SUPER_OUT "so" #define CMD_CPLD_READ "cr" #define CMD_CPLD_WRITE "cw" #define VERSION "1.2" /* these globals handle the differences between Linux 2.0.x and 2.2.x */ static int kernel_version = 20; /* or 22 */ #define IO_BASE_20 0xE0000000 #define IO_BASE_22 0x7c000000 static long io_base; #define PAGE_SIZE 0x1000 static char* _pIO; #define MAP_IO(h) _pIO = mmap(0,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_FILE|MAP_SHARED,h,io_base) #define UNMAP_IO munmap(_pIO,PAGE_SIZE) #define IS_OK_IO _pIO #define outb(p, v) *(_pIO+(p)) = (char)v #define inb(p) *(_pIO+(p)) #define UNLOCK outb(0x370, 0x87); outb(0x370, 0x87); #define LOCK outb(0x370, 0xAA); extern int errno; int tmpSuperIO; #define TOASCII(a) (((a)>' ' && (a)<0x7F) ? a : '.') /* display as bytes, shorts or longs bdata pointer to the data to read bcount number of bytes buff a text buffer to fill for display */ void db (int f_mem, long bdata, int bcount, char *buff, int bytes_at_a_time) { int bposition; int i; char asciibuf[17]; char *pmmap,*p; int count; int offset; if (!bcount) return; /* shorts and longs must be aligned */ bdata &= ~(bytes_at_a_time-1); /* option to skip the first few reads in a line */ bposition = bdata & 0x0F; /* map in the zone, rounding down to nearest page */ offset = bdata & (PAGE_SIZE-1); pmmap = mmap(0,bcount+offset,PROT_READ,MAP_FILE|MAP_SHARED, f_mem,bdata - offset); /* and create a pointer to the exact location within the mmap */ p = pmmap + offset; for (count=bcount; count>0;) { strcpy(asciibuf," "); buff += sprintf(buff,"%08X ",bdata & 0xfffffff0); /* if the first few bytes need to be skipped .... */ while (bposition) { buff += sprintf(buff," "); bposition--; } do { unsigned int a; switch(bytes_at_a_time) { case 1: a = *(unsigned char*)p; buff += sprintf(buff,"%02X%c",a, (bdata & 15) == 7 ? '-' : ((bdata & 7) == 3 ? '.' : ' ')); asciibuf[bdata & 0xf] = TOASCII(a); bdata++; break; case 2: a = *(unsigned short*)p; buff += sprintf(buff,"%04X%c ",a, (bdata & 15) == 6 ? '-' : ((bdata & 7) == 2 ? '.' : ' ')); asciibuf[bdata & 0xf] = TOASCII(a & 255); bdata++; asciibuf[bdata & 0xf] = TOASCII(a >> 8); bdata++; break; case 4: a = *(unsigned long*)p; buff += sprintf(buff,"%08X %c ",a, (bdata & 15) == 4 ? '-' : ((bdata & 7) == 2 ? '.' : ' ')); asciibuf[bdata & 0xf] = TOASCII(a & 255); bdata++; asciibuf[bdata & 0xf] = TOASCII((a>>8) & 255); bdata++; asciibuf[bdata & 0xf] = TOASCII((a>>16) & 255); bdata++; asciibuf[bdata & 0xf] = TOASCII(a>>24); bdata++; break; default: return; } p += bytes_at_a_time; count-=bytes_at_a_time; } while (bdata & 0xF); buff += sprintf(buff," [%16s]\n",asciibuf); } munmap(pmmap,bcount+offset); } /* write to memory returns true to continue modifies the first parameter on return */ int eb(long* paddr, int f_mem, int deb_mode, char* scan_buffer) { char* pmmap, *p; long addr = *paddr; unsigned char btemp; unsigned long dtemp; int i; int ret = 1; /* mmap a page just below deb_add */ pmmap = mmap(0,PAGE_SIZE,PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, f_mem, (long) addr & ~(PAGE_SIZE - 1)); if (pmmap==0) { printf("Memory access error!\n"); return 0; } switch (deb_mode) { case DEB_BYTE: btemp = *(char*) (pmmap + ((long)addr & (PAGE_SIZE-1))); printf ("Byte %p: %02X -> ", addr, btemp & 0xFF); fgets (scan_buffer, 127, stdin); i = sscanf (scan_buffer, "%02X", &dtemp); if (i == -1) { ret = 0; break; } *(char*) (pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp; addr++; break; case DEB_WORD: dtemp = *(unsigned short*)(pmmap + ((long)addr & (PAGE_SIZE-1))); printf ("Dword %p: %04X -> ", addr, dtemp); fgets (scan_buffer, 127, stdin); i = sscanf (scan_buffer, "%04X", &dtemp); if (i == -1) { ret = 0; break; } *(unsigned short*)(pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp; addr += 2; break; case DEB_DWORD: dtemp = *(unsigned long*)(pmmap + ((long)addr & (PAGE_SIZE-1))); printf ("Dword %p: %08X -> ", addr, dtemp); fgets (scan_buffer, 127, stdin); i = sscanf (scan_buffer, "%08X", &dtemp); if (i == -1) { ret = 0; break; } *(unsigned long*)(pmmap + ((long)addr & (PAGE_SIZE-1))) = dtemp; addr += 4; break; } // switch munmap(pmmap,PAGE_SIZE); /* pass the new address back to the caller */ *paddr = addr; return ret; } int main (int argc, char **argv) { char textline[2560]; char kbuff[128]; char *ptext; void *deb_addr = &textline[0]; int f_mem; int deb_mode = DEB_BYTE; int i; int last_cmd = 'd'; int temp; int in_edit = 0; int dtemp; int ldev; int j; int btemp; printf ("\nNetwinder Low Level Debug Tool V.%s, Woody, Corel Computer 1998.\n", VERSION); io_base = IO_BASE_20; i=open("/proc/version",O_RDONLY); if (i==-1) { printf("\n/proc directory is not mounted; assuming Linux 2.0.x"); } else { read(i,textline,256); close(i); if (strstr(textline,"2.2.")) { kernel_version = 22; printf("Using Linux 2.2 memory map\n"); io_base = IO_BASE_22; } } if (getuid() != 0) { printf ("This program must be run as root!\n\n"); return EXIT_FAILURE; } f_mem = open ("/dev/mem", O_RDWR); ptext = &textline[0]; while (1) { fflush (stdin); fprintf (stdout, "\n->"); fgets (kbuff, 127, stdin); for (i = 0; i < 127; i++) if (kbuff[i] != 0x20) break; if (kbuff[i] == '\n') { *(int *) &kbuff[i] = last_cmd; } switch (kbuff[i]) { case 'e': case 'E': i++; last_cmd = CMD_EDIT; if (tolower (kbuff[i]) == 'd') { deb_mode = DEB_DWORD; i++; } else if (tolower (kbuff[i]) == 'w') { deb_mode = DEB_WORD; i++; } else if (tolower (kbuff[i]) == 'b') { deb_mode = DEB_BYTE; i++; } i = sscanf (&kbuff[i], "%p", &deb_addr); while(eb((long*)&deb_addr,f_mem,deb_mode,kbuff)); break; // letter E case 'd': case 'D': i++; last_cmd = CMD_DUMP; if (tolower (kbuff[i]) == 'd') { deb_mode = DEB_DWORD; i++; } else if (tolower (kbuff[i]) == 'w') { deb_mode = DEB_WORD; i++; } else if (tolower (kbuff[i]) == 'b') { deb_mode = DEB_BYTE; i++; } i = sscanf (&kbuff[i], "%p", &deb_addr); if (i == -1) deb_addr += 160; if (deb_addr) { db (f_mem, (long)deb_addr, 160, ptext, deb_mode); printf(ptext); } break; case 'i': case 'I': i++; last_cmd = CMD_INPUT; i = sscanf (&kbuff[i], "%p", &deb_addr); // if (i == -1) //if just i - keep using this port // deb_addr ++; { MAP_IO(f_mem); if (!IS_OK_IO) printf ("Memory access error!\n"); btemp = inb((long)deb_addr); UNMAP_IO; i = sprintf (textline, "Port %04X", (unsigned int) deb_addr & 0xFFFF); *(textline + i) = ' '; sprintf (&textline[i + 1], " : %02X.\n", (unsigned char) btemp); printf (textline); } break; case 'o': case 'O': i++; last_cmd = CMD_OUTPUT; i = sscanf (&kbuff[i], "%04p %02X", &deb_addr, &btemp); if (i != -1) { MAP_IO(f_mem); if (!IS_OK_IO) printf ("Port access error (%04X != %04X)!\n", i & 0xFFFF, (unsigned int)deb_addr); else outb((long)deb_addr,btemp); UNMAP_IO; } break; case 's': case 'S': i++; if (kbuff[i] == 'i' || kbuff[i] == 'I') { i++; last_cmd = (int) CMD_SUPER_IN; i = sscanf (&kbuff[i], "%02X %04p", &ldev, &deb_addr); MAP_IO(f_mem); UNLOCK; outb (0x370, 0x07); outb (0x371, ldev); outb (0x370, (unsigned int) deb_addr); btemp = inb (0x371); UNMAP_IO; printf ("Logical dev. %02X, Reg %02X : %02X.\n", ldev, (unsigned int) deb_addr & 0xFFFF, btemp); break; } else if (kbuff[i] == 'o' || kbuff[i] == 'O') { i++; last_cmd = (int) CMD_SUPER_OUT; i = sscanf (&kbuff[i], " %X %04p %02X", &ldev, &deb_addr, &btemp); printf ("Logical dev. %02X, Reg %02X : %02X.\n", ldev, (unsigned int) deb_addr, btemp); MAP_IO(f_mem); UNLOCK; outb (0x370, 0x07); outb (0x371, ldev); outb (0x370, (int) deb_addr); outb (0x371, (char) btemp); UNMAP_IO; break; } else break; case 'c': case 'C': i++; if (kbuff[i] == 'r' || kbuff[i] == 'R') { i++; last_cmd = (int) CMD_CPLD_READ; ldev = open ("/dev/nwdebug", O_RDONLY); if (ldev < 0) { printf ("Error %d opening /dev/nwdebug\n", ldev); break; } else { ioctl (ldev, 0x5D, &btemp); //CMD_READ_CPLD if (!errno) printf ("CPLD: 0x%X (DS1620 rst:%s, 7111:%s, Mute:%s, Flash_wrt:N/A).\n", btemp, btemp & 0x08 ? "OFF" : "ON", btemp & 0x04 ? "disabled" : "enabled", btemp & 0x02 ? "OFF" : "ON"); close (ldev); } break; } else if (kbuff[i] == 'w' || kbuff[i] == 'W') { i++; last_cmd = (int) CMD_CPLD_WRITE; i = sscanf (&kbuff[i], " %02X", &btemp); if (i != -1) { btemp &= 0xEF; ldev = open ("/dev/nwdebug", O_RDWR); if (ldev < 0) { printf ("Error %d opening /dev/nwdebug\n", ldev); break; } else { ioctl (ldev, 0x5D, &j); //CMD_READ_CPLD ioctl (ldev, 0x5E, &btemp); //CMD_WRITE_CPLD if (!errno) { ioctl (ldev, 0x5D, &btemp); //CMD_READ_CPLD printf ("CPLD: old 0x%X, new 0x%X (DS1620 rst:%s, 7111:%s, Mute:%s, Flash_wrt:N/A).\n", j, btemp, btemp & 0x08 ? "OFF" : "ON", btemp & 0x04 ? "disabled" : "enabled", btemp & 0x02 ? "OFF" : "ON"); } } close (ldev); } break; } else break; case 'q': case 'Q': close (f_mem); exit (0); break; case 'm': case 'M': if (kernel_version == 20) { //printf(" ...MEMORY MAP...\n"); printf (" Debuger\n"); printf ("Phys Start End Start(2) PCI Start(1) Contents\n"); printf ("\n"); printf ("0000.0000 01FF.FFFF 0 32 meg SDRAM\n"); printf ("4000.0000 4000.FFFF FFF0.xxxx DRAM mode registers\n"); printf ("4100.0000 413F.FFFF E18x.xxxx 1-4 meg flash ROM\n"); printf ("4200.0000 420F.FFFF E10x.xxxx CSR\n"); printf ("5000.0000 5000.0FFF E130.0xxx StrongARM cache flush\n"); printf ("7800.0000 7800.0FFF E120.0xxx PCI write flush\n"); printf ("7900.0000 7900.0003 E110.0000 PCI IACK address\n"); printf ("7B00.0000 7BFF.FFFF E0nn.xxxx PCI config\n"); printf (" VGA:E018..Ether100:E020,..SCSI:E030..IDE:E050..Ether10:E090..\n"); printf ("7C00.0000 7C00.0FFF E000.0xxx PCI I/O space\n"); printf ("7C00.xxxx D080.xxxx VGA config regs\n"); printf ("8000.0000 80FF.FFFF D0xx.xxxx 08xx.xxxx VGA\n"); // printf ("9000.0000 9000.FFFF D100.xxxx 1000.xxxx modem codec (optional)\n"); printf ("A000.0000 A000.00FF E140.00xx 2000.00xx Ethernet 100\n"); printf ("A001.0000 A001.00FF E141.00xx 2001.00xx SCSI\n"); printf ("\n"); printf ("(1) PCI bus masters see these addresses.\n"); printf ("(2) These are the addresses used by Linux code, after the memory management\n"); printf ("unit has been enabled. Note that the SDRAM is owned by the Linux kernel and\n"); printf ("is managed at address C000.0000. When memory is dished out to user code, it\n"); printf ("appears duplicated at 0-BFFF.FFFF in the user space.\n"); } else { printf ("Debugger Addrs Kernel\n"); printf ("Start End Addr(2) PCI Start(1) Contents\n"); printf ("\n"); printf ("0000.0000 01FF.FFFF C0xx.xxxx 32 meg SDRAM\n"); printf ("4000.0000 4000.FFFF DRAM mode registers\n"); printf ("4100.0000 413F.FFFF D8xx.xxxx 1-4 meg flash ROM\n"); printf ("4200.0000 420F.FFFF FE0x.xxxx CSR\n"); printf ("5000.0000 5000.0FFF DF00.0xxx StrongARM cache flush\n"); printf ("7800.0000 7800.0FFF FD00.0xxx PCI write flush\n"); printf ("7900.0000 7900.0003 FC00.0000 PCI IACK address\n"); printf ("7B00.0000 7BFF.FFFF F8nn.xxxx PCI config\n"); printf (" VGA:7B08..Ether100:7B10,..SCSI:7B20..IDE:7B40..Ether10:7B80..\n"); printf ("7C00.0000 7C00.0FFF FF00.0xxx PCI I/O space\n"); printf ("7C00.xxxx D080.xxxx VGA config regs\n"); printf ("8100.0000 81FF.FFFF E1xx.xxxx 01xx.xxxx VGA\n"); printf ("8080.0000 8080.00FF E080.00xx 0080.00xx Ethernet 100\n"); printf ("80A0.0000 80A0.1FFF E0A0.xxxx 00A0.00xx SCSI\n"); printf ("\n"); printf ("(1) PCI bus masters see these addresses.\n"); printf ("(2) These are the addresses used by Linux code, after the memory management\n"); printf ("unit has been enabled. Note that the SDRAM is owned by the Linux kernel and\n"); printf ("is managed at address C000.0000. When memory is dished out to user code, it\n"); printf ("appears duplicated at 0-BFFF.FFFF in the user space.\n"); } break; case '?': case 'h': case 'H': printf ("Debug commands (all values in hex):\n"); printf ("dd addr - dump dword\n"); printf ("dw addr - dump word\n"); printf ("db addr - dump byte\n"); printf ("ed addr - edit dword\n"); printf ("ew addr - edit word\n"); printf ("eb addr - edit byte\n"); printf ("i addr - in port (byte)\n"); printf ("o addr val - out port (byte)\n"); printf ("si ldev reg - superIO ldev reg in (byte)\n"); printf ("so ldev reg val - superIO ldev reg out (byte)\n"); printf ("cr - read CPLD (3 bits)\n"); printf ("cw val - write CPLD (3 bits)\n"); printf ("m - display memory map\n"); break; default: break; } } }