/* * memwatch.c: Program to read/write from/to memory. * * Copyright (C) 2006-2017 by Digi International Inc. * All rights reserved. * * 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define PROGRAM "memwatch" #define VERSION "v1.2" #define MEM_DEV_FILE "/dev/mem" #define MAP_SIZE 4096UL #define MAP_MASK (MAP_SIZE - 1) #define VERB_PRNT_ADDR 1 typedef enum { BYTE = 1, WORD = 2, DWORD = 4 }access_t; /* Global variables */ static int fd = -1; static int fd_file = -1; /*********************************************************************** * Function: release_resources * Return: Nothing. * Descr: Releases the used system resources. ***********************************************************************/ static void release_resources( void ) { if( fd != -1 ) close( fd ); if( fd_file != -1 ) close( fd_file ); } /*********************************************************************** * Function: release_resources_and_exit * Return: Nothing. * Descr: Releases the used system resources and exits with exitval. ***********************************************************************/ static void release_resources_and_exit( int exitval ) { release_resources(); exit( exitval ); } /*********************************************************************** * Function: show_usage_and_exit * Return: Nothing. * Descr: Shows usage information and exits with the value passed as * parameter. ***********************************************************************/ static void show_usage_and_exit( int exitval ) { printf("Usage: %s [-rxbhw] -a address [-d value] [-f file] [-v level]\n" \ "%s %s Copyright Digi International Inc.\n\n" \ "Utility to Read/Writes physical memory\n" \ "\n" \ " -r, --read Read from memory\n" \ " -x, --write Write to memory\n" \ " -b --byte Byte access\n" \ " -h --halfw Half word access\n" \ " -w --word Word access\n" \ " -l --len Number of bytes to read/write\n" \ " -a --address Start Address of physical memory\n" \ " -d --data Data value to write to memory address\n" \ " -f --file File to read/write\n" \ " -v --verbose Verbosity level\n" \ " --help Print help and exit\n" \ " --version Print version and exit\n" \ "\n", PROGRAM, PROGRAM, VERSION ); exit( exitval ); } /*********************************************************************** * Function: print_hex_formated * Return: Nothing. * Descr: Prints hex data formated accord to the data length. ***********************************************************************/ static void print_hex_formated( unsigned long val, access_t access ) { switch( access ) { case BYTE: printf( "0x%02x ", (unsigned int)val ); break; case WORD: printf( "0x%04x ", (unsigned int)val ); break; case DWORD: printf( "0x%08x ", (unsigned int)val ); break; } } /*********************************************************************** * @Function: main * @Return: * @Descr: Main function. ***********************************************************************/ int main( int argc, char** argv ) { int read_write = 1, len = -1, pending, chunck, verbose = 0, j; access_t access = DWORD; int ret; unsigned long wr_val, rd_val; void *map_base, *virt_addr; off_t address = 0; char *filename = NULL; struct stat filestat; /*Command line vars*/ static int version = 0; static int help = 0; static int opt_index, opt, optcount = 0; static const char *short_options = "rxbhwl:a:d:f:v:"; static const struct option long_options[] = { { "version", no_argument, &version, 1 }, { "help", no_argument, &help, 1 }, { "read", no_argument, NULL, 'r' }, { "write", no_argument, NULL, 'x' }, { "byte", no_argument, NULL, 'b' }, { "halfw", no_argument, NULL, 'h' }, { "word", no_argument, NULL, 'w' }, { "len", required_argument, NULL, 'l' }, { "address", required_argument, NULL, 'a' }, { "data", required_argument, NULL, 'd' }, { "file", required_argument, NULL, 'f' }, { "verbose", required_argument, NULL, 'v' }, { 0, 0, 0, 0 }, }; for( opt_index = 0; ; ) { opt = getopt_long( argc, argv, short_options, long_options, &opt_index ); if( opt == EOF ) break; if( 0 == opt ) { if( version ) { printf( "%s %s, compiled on %s, %s\n", PROGRAM, VERSION, __DATE__, __TIME__ ); exit( EXIT_SUCCESS ); } if( help ) show_usage_and_exit( EXIT_SUCCESS ); } switch( opt ) { case 'r': read_write = 1; break; case 'x': read_write = 0; break; case 'b': access = BYTE; break; case 'h': access = WORD; break; case 'w': access = DWORD; break; case 'l': len = atoi( optarg ); break; case 'a': address = strtoul( optarg, 0, 0 ); break; /*Start address*/ case 'd': wr_val = strtoul( optarg, 0, 0 ); break; /*Value to write*/ case 'f': filename = optarg; break; /*file where to read/write mem info*/ case 'v': verbose = atoi( optarg ); break; case '?': show_usage_and_exit( EXIT_FAILURE ); break; } optcount++; } if ( optcount == 0 ) show_usage_and_exit( EXIT_FAILURE ); if( len == -1 ) len = (int)access; if( ( fd = open( MEM_DEV_FILE, O_RDWR | O_SYNC ) ) < 0 ) { fprintf( stderr, "Error opening mem file: %s\n", MEM_DEV_FILE ); exit( EXIT_FAILURE ); } if( filename != NULL ) { if( ( fd_file = open( filename, O_RDWR | O_CREAT , 0600 ) ) < 0 ) { fprintf( stderr, "Error opening file: %s (%d)\n", filename, errno ); release_resources_and_exit( EXIT_FAILURE ); } if( !read_write ) { if( fstat( fd_file, &filestat ) != 0 ) { fprintf( stderr, "fstat error on %s (%d)\n", filename, errno ); release_resources_and_exit( EXIT_FAILURE ); } len = (int)filestat.st_size; } } /* Check parameters consistency */ // @TODO pending = len; while( pending ) { chunck = (pending > MAP_SIZE) ? MAP_SIZE : pending; /* Map one page maximum */ map_base = mmap( 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, address & ~MAP_MASK ); if( map_base == (void *) -1 ) { fprintf( stderr, "Error mapping memory\n" ); release_resources_and_exit( EXIT_FAILURE ); } virt_addr = map_base + (address & MAP_MASK); for( j = 0; j < chunck; j += access ) { if( read_write ) { switch( access ) { case BYTE: rd_val = *((unsigned char *) virt_addr); break; case WORD: rd_val = *((unsigned short *) virt_addr); break; case DWORD: rd_val = *((unsigned long *) virt_addr); break; } if( filename != NULL ) { if( write( fd_file, (unsigned char *)&rd_val, access ) != access ) { fprintf( stderr, "Error writing in %s (%d)\n", filename, errno ); release_resources_and_exit( EXIT_FAILURE ); } } if( ( verbose < VERB_PRNT_ADDR ) && ( j % 16 == 0 ) ) { if (j > 0) printf("\n"); printf( "0x%08lx: ", address + j ); } print_hex_formated( rd_val, access ); } else { if( filename != NULL ) { if( ( ret = read( fd_file, &wr_val, access ) ) != access ) { fprintf( stderr, "Error reading from %s (%d)\n", filename, errno ); release_resources_and_exit( EXIT_FAILURE ); } } switch( access ) { case BYTE: *((unsigned char *) virt_addr) = wr_val; break; case WORD: *((unsigned short *) virt_addr) = wr_val; break; case DWORD: *((unsigned long *) virt_addr) = wr_val; break; } } virt_addr += access; } printf("\n"); if( munmap( map_base, MAP_SIZE ) == -1 ) { fprintf( stderr, "Error unmapping memory\n" ); release_resources_and_exit( EXIT_FAILURE ); } pending -= chunck; address += chunck; } release_resources(); return 0; }