Tom Xue
Tom Xue

Reputation: 3355

How to use /dev/kmem?

Updated my post...

I got below program. It operates on /dev/kmem and /dev/mem.

I think I can learn something from the code. But when I run it on my Beagle Board, below result is given:

case 1: ( if(1) )
    root@omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3
    found jiffies at (0xc0870080) c0870080
    /dev/kmem read buf = 319317
    jiffies=319317 (read from virtual memory)

    /dev/mem: the offset is 870080
    the page size = 4096
    mmap: Invalid argument



case 2: ( if(0) )
    root@omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3 
    found jiffies at (0xc0870080) c0870080
    /dev/kmem read buf = 333631
    jiffies=333631 (read from virtual memory)

    /dev/mem: the offset is 870080
    /dev/mem read failed: Bad address
    jiffies=0 (read from physical memory)

And I used below command so that mmap can use NULL as its first parameter.

root@omap:/home/ubuntu/tom# echo 0 > /proc/sys/vm/mmap_min_addr
root@omap:/home/ubuntu/tom# cat /proc/sys/vm/mmap_min_addr
0

As you can see, read_kmem() works fine but read_mem() doesn't work, and it seems that the 'offset' transferred to it is wrong. But kernel address - PAGE_OFFSET(0xC0000000) = physical address, is it wrong?

My questions are: (1) Why "mmap: Invalid argument" in case 1? (2) Why the mmap only maps PAGE_SIZE length space? (3) What's wrong with read_mem?

Can anyone help? Thanks!

/*
* getjiff.c
*
* this toolkit shows how to get jiffies value from user space:
* 1. find jiffies's address from kernel image.
* 2. access virtual address space to get jiffies value.
* 3. access physical address sapce to get jiffies value.
*
* demostrate following techniques:
* o get ELF object symbol address by calling nlist()
* o access virtual memory space from /dev/kmem
* o access virtual memory space from /dev/mem
*/

#include <stdio.h>
#include <stdlib.h>         //exit
#include <linux/a.out.h>    //nlist
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <memory.h>

#define LONG *(volatile unsigned long*)

/* read from virtual memory */
int read_kmem(off_t offset, void* buf, size_t count)
{
    int fd;
    int n;

    fd = open("/dev/kmem", O_RDONLY);
    if (fd < 0)
    {
        perror("open /dev/kmem failed");
        return -1;
    }

    lseek(fd, offset, SEEK_SET);
    n = read(fd, buf, count);
    if (n != count)
        perror("/dev/kmem read failed");
    else
        printf("/dev/kmem read buf = %ld\n", *(unsigned long *)buf);

    close(fd);
    return n;
}

/* read from physical memory */
int read_mem(off_t offset, void* buf, size_t count)
{
    int fd;
    int n;
    int page_size;
    void *map_base; 
    unsigned long value;

    printf("/dev/mem: the offset is %lx\n", offset);

    fd = open("/dev/mem", O_RDONLY);
    if (fd < 0)
    {
        perror("open /dev/mem failed");
        return -1;
    }

    if(1){
        page_size = getpagesize();
        printf("the page size = %d\n", page_size);
        map_base = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,offset);
        if (map_base == MAP_FAILED){
            perror("mmap");
            exit(1);
        }
        value = LONG(map_base);
        printf("/dev/mem: the value is %ld\n", value);
        buf = (unsigned long *)map_base;
    }

    if(0){
        lseek(fd, offset, SEEK_SET);
        n = read(fd, buf, count);
        if (n != count)
            perror("/dev/mem read failed");
        else
            printf("/dev/mem read buf = %ld\n", *(unsigned long *)buf);
    }

    close(fd);
    return n;
}

int main(int argc, char **argv)
{
    FILE *fp;
    char addr_str[11]="0x";
    char var[51];
    unsigned long addr;
    unsigned long jiffies;
    char ch;
    int r;

    if (argc != 2) {
            fprintf(stderr,"usage: %s System.map\n",argv[0]);
            exit(-1);
    }

    if ((fp = fopen(argv[1],"r")) == NULL) {
            perror("fopen");
            exit(-1);
    }

    do {
            r = fscanf(fp,"%8s %c %50s\n",&addr_str[2],&ch,var);    // format of System.map
            if (strcmp(var,"jiffies")==0)
                    break;
    } while(r > 0);
    if (r < 0) {
            printf("could not find jiffies\n");
            exit(-1);
    }

    addr = strtoul(addr_str,NULL,16);                               //Convert string to unsigned long integer
    printf("found jiffies at (%s) %08lx\n",addr_str,addr);

    read_kmem(addr, &jiffies, sizeof(jiffies));
    printf("jiffies=%ld (read from virtual memory)\n\n", jiffies);

    jiffies = 0;                                                    //reinit for checking read_mem() below

    read_mem(addr-0xC0000000, &jiffies, sizeof(jiffies));
    printf("jiffies=%ld (read from physical memory)\n", jiffies);

    return 0;
} 

Upvotes: 11

Views: 21602

Answers (2)

Costa
Costa

Reputation: 481

I've tried combinations or offset and bs for dd and found this solution:

On PC, in build directory I've found location of jiffies.

grep -w jiffies System.map
c04660c0 D jiffies

On PandaBoard:

In /proc/iomem you can see:

80000000-9c7fffff : System RAM
80008000-80435263 : Kernel code
80464000-804d0d97 : Kernel data
a0000000-bfefffff : System RAM

RAM starts from physical 80000000, and Kernel data start on 80464000. Looks similar to address of jiffies.

Then convert from virtual address to phys: virt - 0xC000000 + 0x8000000.

dd if=/dev/mem skip=$((0x804660c)) bs=$((0x10)) count=1 2> /dev/null | hexdump
0000000 02b9 0002 0001 0000 0000 0000 0000 0000
0000010

Try several times and see how the value is incrementing.

Summary: /dev/mem uses phys address, RAM starts at phys address 0x8000000

Upvotes: 4

p_l
p_l

Reputation: 1179

For the invalid argument in case 1, the problem is offset being non-page aligned. mmap(2) works by manipulating page tables, and such works only on multiplies of page-size for both size and offset

As for the second case, I'm not sure if you're guaranteed to have kernel space begin at 3G boundary. Also, I'm pretty sure that's the boundary of kernel's virtual space, not location in physical memory - so on beagle board, quite possibly you ended up with a wrapped-around offset pointing who-knows-where.

I think what you might need is PHYS_OFFSET, not PAGE_OFFSET.

Upvotes: 2

Related Questions