#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
/* we don't have page 0 mapped on sparc and m68k.. */
if (p < PAGE_SIZE) {
sz = size_inside_page(p, count);
/* Hmm. Do something? */
buf += sz;
p += sz;
count -= sz;
written += sz;
}
#endif
while (count > 0) {
int allowed;
sz = size_inside_page(p, count);
allowed = page_is_allowed(p >> PAGE_SHIFT);
if (!allowed)
return -EPERM;
/* Skip actual writing when a page is marked as restricted. */
if (allowed == 1) {
/*
* On ia64 if a page has been mapped somewhere as
* uncached, then it must also be accessed uncached
* by the kernel or data corruption may occur.
*/
ptr = xlate_dev_mem_ptr(p);--------------------------------__va()进行物理地址到虚拟地址的转换。
if (!ptr) {
if (written)
break;
return -EFAULT;
}
copied = copy_from_user(ptr, buf, sz);
unxlate_dev_mem_ptr(p, ptr);
if (copied) {
written += sz - copied;
if (written)
break;
return -EFAULT;
}
}
buf += sz;
p += sz;
count -= sz;
written += sz;
}
*ppos += written;
return written;
}
对比mmap和read()/write()两种方式可知:
•mmap()可以读写的范围更大;read()/write()的范围局限在low_memory。
•mmap()的读写速度更快,操作更方便。
1.4 hexdump使用
devmem一次读写的内容有限,hexdump可以一次dump大量数据。
但是hexdump是通过read()/write()来获取数据,物理地址的范围受到限制。而devmem通过mmap()则没有这些限制。
hexdump -s 0x10000000 -n 256 /dev/mem
10000000 0005 1908 fc11 18ff edf7 03fe e914 020b
10000010 0d00 1fe4 f202 1601 f703 0412 e814 1cfb
10000020 09fc 000b 06eb 07f0 ec12 01e6 11e9 03f7
10000030 1a2d 11eb f700 ece9 eef3 05f7 0009 eb03
10000040 ff1a e50b 1e08 0f16 0cfa 13fb 0b06 0a1b
10000050 0401 fefd fd1e 0b05 f317 f9ea f00a 3ef5
10000060 f118 fe02 f606 0f02 f1ec f4fe 0216 eefb
10000070 0c02 eefd f8ff 06eb 08fc f603 05fb f80e
10000080 f6fb 2503 f207 0a19 12ee fb0d 0512 09f8
10000090 fbfa 1303 f9fe 0dfc f2fa 06fb fef4 04fa
100000a0 2007 170e 1a05 f3f6 0c2d 0601 0f0b 061f
100000b0 1108 0b18 f80d ebef 05f8 f3eb 0207 e8ff
100000c0 fb07 fdea 0efd fb02 0f10 f8f8 f016 f8f2
100000d0 130f 0803 0909 0100 0b03 fc06 0307 1e10
100000e0 011b 2814 f7f3 fc01 f6f9 03ec 0afb ecf1
100000f0 05fb 070a f904 fbf5 f7fa 0304 f502 0d02
2. devkmem读取内核虚拟地址空间数据
某些情况下需要读取内核某个变量的值,这时候可以通过/dev/kmem。
2.1 /dev/kmem
要使用/dev/kmem就需要在内核中打开CONFIG_DEVKMEM,menuconfig路径为:Device Drivers->Character devices->/dev/kmem virtual device support。
static const struct file_operations __maybe_unused kmem_fops = {
.llseek = memory_lseek,
.read = read_kmem,
.write = write_kmem,
.mmap = mmap_kmem,
.open = open_kmem,
#ifndef CONFIG_MMU
.get_unmapped_area = get_unmapped_area_mem,
.mmap_capabilities = memory_mmap_capabilities,
#endif
};
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
{
unsigned long pfn;
/* Turn a kernel-virtual address into a physical page frame */
pfn = __pa((u64)vma->vm_pgoff << PAGE_SHIFT) >> PAGE_SHIFT;------------------将内核虚拟地址通过__pa()转换成物理地址。
if (!pfn_valid(pfn))
return -EIO;
vma->vm_pgoff = pfn;
return mmap_mem(file, vma);
}
read_kmem()和write_kmem()需要对low_memory和high_memory进行区别对待。
对low_memory需要经过xlate_dev_kmem_ptr()后进行读写;对high_memory通过vread()/vwrite()进行读写。