read PCI memory and config spyce through /dev/mem

Warlich, Christof christof.warlich at siemens.com
Wed May 8 02:49:45 EDT 2013


Jun Hu <jhu_com at 163.com> wrote:
> strace your application, like as:
> ...

It's the lseek that fails: Obviously, /dev/mem is not seekable.
What really puzzles me is that dd _does_ work on /dev/mem when
looking at ordinary RAM, but I'm reluctant to further debug why.

But anyhow, as I think it may at times be also useful for others
to have a simple tool that allows to read (and write!) PCI memory,
I share the few lines of code that I wrote to fulfill my needs. I
hope that the included "documentation" is verbose enough. And I'm
certainly interested if anyone finds bugs :-):
 
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <sys/mman.h>
void usage(const char *name) {
    fprintf(stderr, "Usage: %s address access [size]\033[2m\n", name);
    fprintf(stderr, "       Either - prints \"size\" bytes \033[1mfrom\033[2m physical memory,\n");
    fprintf(stderr, "                starting at \"address\" and using \"access\"\n");
    fprintf(stderr, "                bytes per access \033[1mto\033[2m stdout\n");
    fprintf(stderr, "       or     - writes as many bytes as are available \033\[1mfrom\033[2m\n");
    fprintf(stderr, "                stdin \033[1mto\033[2m physical memory starting at \"address\"\n");
    fprintf(stderr, "                using \"access\" bytes per access.\n");
    fprintf(stderr, "       Note that both \"address\" and either \"size\" or the\n");
    fprintf(stderr, "       number of available bytes from stdin must be a multiple of\n");
    fprintf(stderr, "       \"access\", and \"access\" must either be %d, %d, %d or %d.\033[0m\n",
           sizeof(uint8_t), sizeof(uint16_t), sizeof(uint32_t), sizeof(uint64_t));
    exit(-1);
}
template<typename T> void readOrWrite(uint8_t *src, uint8_t *mem) {
    if(src) *(T *) mem = *(T *) src;
    else {
        T t = *(T *) mem;
        assert(write(1, &t, sizeof(T)) == sizeof(T));
    }
}
int main( int argc, char *argv[]) {
    int fd, pageSize = getpagesize(), size = 0, n;
    uint8_t *mem, *b = 0, *buffer = 0;
    // Paranoia check to ensure that page size is a power of 2.
    assert((pageSize != 0) && !(pageSize & (pageSize - 1)));
    if (argc == 3) do {
        #define CHUNK_SIZE 1000
        assert(b = buffer = (uint8_t *) realloc(buffer, size + CHUNK_SIZE));
        assert((n = read(0, buffer + size, CHUNK_SIZE)) >= 0);
        size += n;
    } while(n);
    else if(argc == 4) size = strtoul(argv[3], 0, 0);
    else usage(argv[0]);
    off_t address = strtoul(argv[1], 0, 0);
    size_t access = strtoul(argv[2], 0, 0);
    if(address % access || size % access) usage(argv[0]);
    assert((fd = open ("/dev/mem", O_RDWR)) >= 0);
    size_t s = (((address & (pageSize - 1)) + size - 1) / pageSize + 1) * pageSize;
    mem = (uint8_t *) mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED, fd, address & ~(pageSize - 1));
    assert(mem != MAP_FAILED);
    for(uint8_t *i = mem; i < mem + size; i += access) {
        #define ROW(t) sizeof(t): readOrWrite<t>(b, i); break
        switch(access) {
            case ROW(uint8_t);
            case ROW(uint16_t);
            case ROW(uint32_t);
            case ROW(uint64_t);
            default: usage(argv[0]);
        }
        if(b) b += access;
    }
    free(buffer);
    munmap(mem, getpagesize());
    close(fd);
    return 0;
}


More information about the Kernelnewbies mailing list