“DevMem note”的版本间的差异

来自个人维基
跳转至: 导航搜索
第53行: 第53行:
  
 
4. mmap to /dev/mem
 
4. mmap to /dev/mem
   
+
offset 如何变成是物理地址? offset 跟vma->pgoff 的关系?
 +
vma->vm_start = addr ?  若  addr=NULL 如何给址?
 +
如何确认offset 合理区域? length 可以有多大?
  
5./dev/mem mmap operator:
+
5./dev/mem driver in inux-kernel/drivers/char/mem.c
 
<source lang="c">
 
<source lang="c">
 
static int mmap_mem(struct file *file, struct vm_area_struct *vma)
 
static int mmap_mem(struct file *file, struct vm_area_struct *vma)
 
{
 
{
 
     size_t size = vma->vm_end - vma->vm_start;
 
     size_t size = vma->vm_end - vma->vm_start;
    phys_addr_t offset = (phys_addr_t)vma->vm_pgoff << PAGE_SHIFT;
+
     if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))//没检查
 
+
          return -EINVAL;
    /* It's illegal to wrap around the end of the physical address space. */
+
     if (!private_mapping_ok(vma))//vm_flags must have VM_MAYSHARE
    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;
 
         return -ENOSYS;
  
     if (!range_is_allowed(vma->vm_pgoff, size))
+
     if (!range_is_allowed(vma->vm_pgoff, size))//check each page is allowed by calling devmem_is_allowed. ARCH_HAS_VALID_PHYS_ADDR_RANGE
 
         return -EPERM;
 
         return -EPERM;
  
 
     if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
 
     if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
                         &vma->vm_page_prot))
+
                         &vma->vm_page_prot))//没检查
 
         return -EINVAL;
 
         return -EINVAL;
  
 
     vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
 
     vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
 
                         size,
 
                         size,
                         vma->vm_page_prot);
+
                         vma->vm_page_prot);// 跟 cache 有关 先不管
  
 
     vma->vm_ops = &mmap_mem_ops;
 
     vma->vm_ops = &mmap_mem_ops;
  
 
     /* Remap-pfn-range will mark the range VM_IO */
 
     /* Remap-pfn-range will mark the range VM_IO */
     if (remap_pfn_range(vma,---------------------------------------------将内核中vma->vm_pgoff对应的size个页面,映射到vma区域,返回的虚拟空间起始地址是vma->vm_start。
+
     if (remap_pfn_range(vma,
                 vma->vm_start,
+
                 vma->vm_start, //Virt 开头位址
 
                 vma->vm_pgoff,
 
                 vma->vm_pgoff,
 
                 size,
 
                 size,
第95行: 第91行:
 
}
 
}
 
</source>
 
</source>
 +
remap_pfn_range im ./mm/memory.c
 +
<source lang="c">
 +
remap_pfn_range(vma, //user vma to map to
 +
                addr, //target user address
 +
                pfn, //physic address of kernel memory. Page #?
 +
                size, //size of map area
 +
                prot //page protection flags for this mapping
 +
              )
 +
{
 +
    vma->vm_pgoff = pfn;
 +
    vma->flags |= VM_IO|VM_PFNMAP|VM_DONTEXPAD |VM_DONTDUMP;
 +
    mm = vma->mm;
 +
    do {
 +
        //for each page, update mm
 +
        next = pgd_addr_end(addr, end)
 +
        remap_pud_range(mm, pgd, addr, next, pfn+page#, prot)
 +
        pgd++, addr = next,
 +
    }while (addr != end)
 +
 +
 +
 +
</source>
 +
 +
  
 
vm_area_struct结构体
 
vm_area_struct结构体

2019年11月5日 (二) 18:03的版本

1.devmem ADDRESS [WIDTH [VALUE]]

Read/write from physical address
 
        ADDRESS Address to act upon
        WIDTH   Width (8/16/...)
        VALUE   Data to be written

2. devmem -> mmap

int devmem_main(int argc UNUSED_PARAM, char **argv)
{
      fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC)); //根据第三个参数确定是以只读形式打开,还是以读写形式打开。 
      mapped_size = page_size = getpagesize(); //4K bytes
      //if cross page, mapped_size *=2 
      map_base = mmap(NULL,
            mapped_size, // 
            argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ,
            MAP_SHARED,
            fd,
            target & ~(off_t)(page_size - 1)//offset from 0x0000, page start address
          );
      virt_addr = (char*)map_base + offset_in_page;
}

3. mmap

#include <sys/mman.h>
 
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);

详细参数如下:

参数 详细说明
addr 需要映射的虚拟内存地址;如果为NULL,系统会自动选定。映射成功后返回该地址
length 需要映射多大的数据量. Mus be multiple of PAGE SIZE
prot 描述映射区域内存保护方式,包括:PROT_EXEC、PROT_READ、PROT_WRITE、PROT_NONE.
flags 描述映射区域的特性,比如是否对其他进程共享,是否建立匿名映射,是否创建私有的cow.
fd 要映射到内存中的文件描述符
offset 文件映射的偏移量

4. mmap to /dev/mem
offset 如何变成是物理地址? offset 跟vma->pgoff 的关系?
vma->vm_start = addr ? 若 addr=NULL 如何给址?
如何确认offset 合理区域? length 可以有多大?

5./dev/mem driver in inux-kernel/drivers/char/mem.c

static int mmap_mem(struct file *file, struct vm_area_struct *vma)
{
    size_t size = vma->vm_end - vma->vm_start;
    if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))//没检查
          return -EINVAL;
    if (!private_mapping_ok(vma))//vm_flags must have VM_MAYSHARE
        return -ENOSYS;
 
    if (!range_is_allowed(vma->vm_pgoff, size))//check each page is allowed by calling devmem_is_allowed. ARCH_HAS_VALID_PHYS_ADDR_RANGE 
        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);// 跟 cache 有关 先不管
 
    vma->vm_ops = &mmap_mem_ops;
 
    /* Remap-pfn-range will mark the range VM_IO */
    if (remap_pfn_range(vma,
                vma->vm_start, //Virt 开头位址 
                vma->vm_pgoff,
                size,
                vma->vm_page_prot)) {
        return -EAGAIN;
    }
    return 0;
}

remap_pfn_range im ./mm/memory.c

remap_pfn_range(vma, //user vma to map to
                addr, //target user address
                pfn, //physic address of kernel memory. Page #?
                size, //size of map area
                prot //page protection flags for this mapping
               )
{
    vma->vm_pgoff = pfn;
    vma->flags |= VM_IO|VM_PFNMAP|VM_DONTEXPAD |VM_DONTDUMP;
    mm = vma->mm;
    do {
        //for each page, update mm
        next = pgd_addr_end(addr, end)
        remap_pud_range(mm, pgd, addr, next, pfn+page#, prot)
        pgd++, addr = next,
    }while (addr != end)


vm_area_struct结构体