devmem读写物理内存和devkmem读取内核虚拟内存(3)

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;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/12046.html