This post will introduce a funny way to access physical memory from user space, I will:
- write a kernel module and load it. The module will allocate a page in kernel space and write some characters in this page.
- write a user program, read the content of that page and print.
What’s the difficulty in doing this ?
As we know, a user program (or process) has its own virtual address
space, when it calls malloc() to apply a block of memory, the malloc
will return the start address of the allocated virtual memory, user program doesn’t know the actual start address of physical memory.
Here, I will access the physicall address from a user program with the help of /dev/mem.
Firstly, let’s talk about /dev/mem, and enable access to /dev/mem
/dev/mem is a virtual device which provides a way to
access to system physical memory, but unfortunately, in most Linux
distributions, userspace(including root) can’t read /dev/mem by default [1]. So, if you want to use this feature, please compile a new kernel by yourself. I will show you step by step.
- go to www.kernel.org download a latest version of linux kernel.
- decompress it.
the following is in command line ( shell ):
$ sudo su //change to root
# make menuconfig // this will generate a .config file
# vim .config
set
CONFIG_STRICT_DEVMEM=n
CONFIG_X86_PAT=n
CONFIG_EXPERT=y
ok, continue to compile it.
# make -j4
# make modules
# make module_install
# make install
reboot my computer, and I will enter OS with a new kernel.
Secondly, let’s write a kernel module.
mmap.c :
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Runzhen”);
MODULE_DESCRIPTION(“mmap demo”);
static unsigned long addr = 0;
static int __init mod_init(void)
{
//alloc a page
addr = __get_free_pages(GFP_KERNEL, 0); //kernel virtual address
SetPageReserved(virt_to_page(addr));
printk(“addr= 0x%08x\n”, (int)addr);
// write something
strcpy((char *)addr, “With great power, comes great responsibility\n”);
return 0;
}
static void __exit mod_exit(void)
{
ClearPageReserved(virt_to_page(addr));
free_pages(addr, 0);
}
module_init(mod_init);
module_exit(mod_exit);
to compile the kernel module, I need a Makefile:
obj-m:=mmap.o
CURRENT_PATH :=$(shell pwd)
LINUX_PATH :=/home/run/linux-3.6.11/
all :
make -C $(LINUX_PATH) M=$(CURRENT_PATH) modules
# make
after make, a kernel module “mmap.ko” will appears in my current directory.
# insmod mmap.ko
then, look the “printk(“addr= 0x%08x\n”, (int)addr);” in `dmesg`
# dmesg // will show:
[80005.141345] addr= 0×77128000

Finally, write a user program using mmap() function.
the declaration of mmap is:
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
`man 2 mmap` says:
The contents of a file mapping
are initialized using `length` bytes starting at offset `offset` in the
file (or other object) referred to by the file descriptor fd. `offset`
must be a multiple of the page size.
so, I can use the address printed by mmap.ko (0×77128000) as the `offset` parameter.
user.c
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define PAGE_SIZE (4 * 1024)
#define KERNEL_PHY_ADDR 0×77128000
int main()
{
char *buf;
int fd;
fd = open(“/dev/mem”,O_RDWR);
if (fd == -1)
perror(“open”);
buf = mmap(0, PAGE_SIZE,
PROT_READ|PROT_WRITE, MAP_SHARED,
fd, KERNEL_PHY_ADDR);
if (buf == MAP_FAILED)
perror(“mmap”);
puts(buf);
munmap(buf,PAGE_SIZE);
close(fd);
return 0;
}
Attention, the KERNEL_PHY_ADDR may vary every time you load the mmap.ko, so, if you copy the code and complie it on your own computer, please remember to change it.
ok, compile user.c
# gcc -o user user.c
make sure you are a super user
# ./user
the out put is:
With great power, comes great responsibility

Game Over.
If you meet any problem, please feel free to contact with me.
Code
https://github.com/run/misc_code/tree/master/mmap
References
[1] http:///Articles/267427/
[2] http:///questions/9662193/how-to-access-kernel-space-from-user-spacein-linux
[3] http://hi.baidu.com/damonzhou/item/e15d06e9ae20f1f5e1a5d4a9