281 lines
8.1 KiB
C
281 lines
8.1 KiB
C
/*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <termios.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
|
|
#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;
|
|
}
|