accessing physical memory from linux kernel
Can we access any physical memory via some kernel code.? Because, i wrote a device driver which only had init_module and exit_module.. the code is following.
and a dummy exit_module.. the problem is the computer gets hung when i do lsmod.. What happens? Should i get some kinda permission to access the mem location? kindly explain.. I’m a beginner!
This is a very complicated programming area and you’re not likely to get the answer you need in such limited space. Have you read any documentation in this area?
yeah, i know the basics.. the question is i know i can access physical memory as a device driver. Bu why does my PC freeze.. after all i’m just reading ; not writing. ?
It’s likely that the address 0x10 is a special register that you can’t just read. You’re treading in dangerous waters here. Arbitrary memory accesses can be hazardous to your health.
Even the kernel has no direct access to physical memory in the way your are thinking. Basically the kind of memory you seem to imagine (linear memory with flat addressing) is something that is constructed by the kernel (from physical memory banks) and exported, it doesn’t «exist». To know about these things and on how to access the different kind of addresses the kernel can handle you really would have to look deeper into the kernel documentation. (Also a value that is not divisible by 4 is certainly a bad choice to expect an unsigned ).
This code is not attempting to access physical memory. It’s attempting to access an invalid (unmapped) address in virtual memory. This is rather bad. 🙂
3 Answers 3
To access real physical memory you should use phys_to_virt function. In case it is io memory (e.g. PCI memory) you should have a closer look at ioremap.
This whole topic is very complex, if you are a beginner I would suggest some kernel/driver development books/doc.
I suggest reading the chapter about memory in this book:
It’s available online for free. Good stuff!
Inside the kernel, memory is still mapped virtually, just not the same way as in userspace.
The chances are that 0x10 is in a guard page or something, to catch null pointers, so it generates an unhandled page fault in the kernel when you touch it.
Normally this causes an OOPS not a hang (but it can be configured to cause a panic). OOPS is an unexpected kernel condition which can be recovered from in some cases, and does not necessarily bring down the whole system. Normally it kills the task (in this case, insmod)
Did you do this on a desktop Linux system with a GUI loaded? I recommend that you set up a Linux VM (Vmware, virtualbox etc) with a simple (i.e. quick to reboot) text-based distribution if you want to hack around with the kernel. You’re going to crash it a bit and you want it to reboot as quickly as possible. Also by using a text-based distribution, it is easier to see kernel crash messages (Oops or panic)
How to access physical addresses from user space in Linux?
On a ARM based system running Linux, I have a device that’s memory mapped to a physical address. From a user space program where all addresses are virtual, how can I read content from this address?
2 Answers 2
busybox devmem
busybox devmem is a tiny CLI utility that mmaps /dev/mem .
You can get it in Ubuntu with: sudo apt-get install busybox
Usage: read 4 bytes from the physical address 0x12345678 :
sudo busybox devmem 0x12345678
Write 0x9abcdef0 to that address:
sudo busybox devmem 0x12345678 w 0x9abcdef0
mmap MAP_SHARED
When mmapping /dev/mem , you likely want to use:
open("/dev/mem", O_RDWR | O_SYNC); mmap(. PROT_READ | PROT_WRITE, MAP_SHARED, . )
MAP_SHARED makes writes go to physical memory immediately, which makes it easier to observe, and makes more sense for hardware register writes.
CONFIG_STRICT_DEVMEM and nopat
To use /dev/mem to view and modify regular RAM on kernel v4.9, you must fist:
- disable CONFIG_STRICT_DEVMEM (set by default on Ubuntu 17.04)
- pass the nopat kernel command line option for x86
IO ports still work without those.
Cache flushing
If you try to write to RAM instead of a register, the memory may be cached by the CPU: How to flush the CPU cache for a region of address space in Linux? and I don’t see a very portable / easy way to flush it or mark the region as uncacheable:
So maybe /dev/mem can’t be used reliably to pass memory buffers to devices?
This can’t be observed in QEMU unfortunately, since QEMU does not simulate caches.
How to test it out
Now for the fun part. Here are a few cool setups:
- Userland memory
- allocate volatile variable on an userland process
- get the physical address with /proc//maps + /proc//pagemap
- modify the value at the physical address with devmem , and watch the userland process react
- allocate kernel memory with kmalloc
- get the physical address with virt_to_phys and pass it back to userland
- modify the physical address with devmem
- query the value from the kernel module
- create a platform device with known physical register addresses
- use devmem to write to the register
- watch printf s come out of the virtual device in response
Bonus: determine the physical address for a virtual address
FalsinSoft
If you want to find a way for access physical memory in Linux there are only two solutions. The first is to develop a module running in kernel space with the correct privileges to access physical memory and the second is to use a special devices called «/dev/mem«. If your purpose is only to read or write some small parts of physical memory from user space this device is the right solution for you. Basically you can manage it as a file with additional use of some special functions required for get a pointer to the memory address your are interested to work in.
The first operation is to get handle of this special device, Please, note that in Linux system the access of this device is usually guarantee only to root user. If you want to get handle from a different user you need to change device access permission. Anyway the command for get the handle is the following:
int mem_dev = open("/dev/mem", O_RDWR | O_SYNC); if(mem_dev == -1) < // Error >
and once finished obviously:
Now the most important part. On Linux the physical memory pointer you want must be page aligned. This mean we need to change, in case, the original memory address you need to something the system can accept. The operation is quite simple and all you need are the following lines of code:
const uint32_t mem_address = 0x10001234 const uint32_t mem_size = 0x100; uint32_t alloc_mem_size, page_mask, page_size; void *mem_pointer, *virt_addr; page_size = sysconf(_SC_PAGESIZE); alloc_mem_size = (((mem_size / page_size) + 1) * page_size); page_mask = (page_size - 1); mem_pointer = mmap(NULL, alloc_mem_size, PROT_READ | PROT_WRITE, MAP_SHARED, mem_dev, (mem_address & ~page_mask) ); if(mem_pointer == MAP_FAILED) < // Error >virt_addr = (mem_pointer + (mem_address & page_mask));
At the end of this code, if no errors occurred, you have a pointer virt_addr to the physical memory space from address 0x10001234 to 0x10001334. You can use this pointer in read/write mode but be very carefully since an incorrect modify to some critical memory section will crash the system. Once finished to use the pointer you must free it as follow:
munmap(mem_pointer, alloc_mem_size);