static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
size_t size = vma->vm_end - vma->vm_start;
phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
/* It's illegal to wrap around the end of the physical address space. */
if (offset + (phys_addr_t)size - 1 < offset)
return -EINVAL;
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))return -EINVAL;
if (!private_mapping_ok(vma))
return -ENOSYS;
if (!range_is_allowed(vma->vm_pgoff, size))
return -EPERM;
if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
&vma->vm_page_prot))
return -EINVAL;
vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
size,
vma->vm_page_prot);
vma->vm_ops = &mmap_mem_ops;
/* Remap-pfn-range will mark the range VM_IO */
if (remap_pfn_range(vma,---------------------------------------------将内核中vma->vm_pgoff对应的size个页面,映射到vma区域,返回的虚拟空间起始地址是vma->vm_start。
vma->vm_start,
vma->vm_pgoff,
size,
vma->vm_page_prot)) {
return -EAGAIN;
}
return 0;
}
下面两个对应read()和write()两个系统调用。
static ssize_t read_mem(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
phys_addr_t p = *ppos;
ssize_t read, sz;
void *ptr;
if (p != *ppos)
return 0;
if (!valid_phys_addr_range(p, count))---------------------------对输入的物理地址+大小进行验证,确保在low_memory范围内。
return -EFAULT;
read = 0;
...
while (count > 0) {
unsigned long remaining;
int allowed;
sz = size_inside_page(p, count);
allowed = page_is_allowed(p >> PAGE_SHIFT);
if (!allowed)
return -EPERM;
if (allowed == 2) {
/* Show zeros for restricted memory. */
remaining = clear_user(buf, sz);
} else {
ptr = xlate_dev_mem_ptr(p);-----------------------------将物理地址转换成虚拟地址,不成功则返回-EFAULT。注意这里的地址通过_va进行转换,只有特定区域的地址才可以使用。
if (!ptr)
return -EFAULT;
remaining = copy_to_user(buf, ptr, sz);-----------------将物理地址对应内容拷贝到用户空间。
unxlate_dev_mem_ptr(p, ptr);
}
if (remaining)
return -EFAULT;
buf += sz;
p += sz;
count -= sz;
read += sz;
}
*ppos += read;
return read;
}
static ssize_t write_mem(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
phys_addr_t p = *ppos;
ssize_t written, sz;
unsigned long copied;
void *ptr;
if (p != *ppos)
return -EFBIG;
if (!valid_phys_addr_range(p, count))---------------------------确保地址在low_memory范围内。
return -EFAULT;
written = 0;