Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use /dev/kmem?

Tags:

c

linux

mmap

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;
} 
like image 659
Tom Xue Avatar asked May 29 '12 14:05

Tom Xue


People also ask

What is Dev KMEM?

The /dev/mem and /dev/kmem character special files provide access to a pseudo device driver that allows read and write access to system memory or I/O address space.

What are KMEM and VMEM allocators?

The kmem allocator relies on two lower-level system services to create slabs: a virtual address allocator to provide kernel virtual addresses, and VM routines to back those addresses with physical pages and establish virtual-to-physical translations.

What is Config_strict_devmem?

Enabling CONFIG_STRICT_DEVMEM implements strict access to /dev/mem so that it only allows user-space access to memory mapped peripherals. With this option disabled, the root user from user-space can access all kernel and user-space memory through /dev/mem.


2 Answers

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

like image 144
Costa Avatar answered Nov 09 '22 21:11

Costa


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.

like image 21
p_l Avatar answered Nov 09 '22 20:11

p_l