函数 ioremap 物理地址到内核映射空间的映射函数

    技术2022-05-11  22

     源代码:

    /* * Remap an arbitrary physical address space into the kernel virtual * address space. Needed when the kernel wants to access high addresses * directly. * * NOTE! We need to allow non-page-aligned mappings too: we will obviously * have to convert them into an offset in a page-aligned mapping, but the * caller shouldn't need to know that small detail. * * 'flags' are the extra L_PTE_ flags that you want to specify for this * mapping.  See include/asm-arm/proc-armv/pgtable.h for more information. */void * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags){ void * addr; struct vm_struct * area; unsigned long offset, last_addr;

     /* Don't allow wraparound or zero size */ last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr)  return NULL;

     /*  * Mappings have to be page-aligned  */ offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; size = PAGE_ALIGN(last_addr) - phys_addr;

     /*  * Ok, go for it..  */ area = get_vm_area(size, VM_IOREMAP); if (!area)  return NULL; addr = area->addr; if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) {  vfree(addr);  return NULL; } return (void *) (offset + (char *)addr);}

    功能就是将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB).

    1、返回映射后的虚拟地址(get_vm_area获取

    2、对获取的虚拟空间进行页面映射(remap_area_pages

    下面是相关的函数:

    struct vm_struct * get_vm_area(unsigned long size, unsigned long flags){ unsigned long addr, next; struct vm_struct **p, *tmp, *area;

     area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); if (!area)  return NULL;

     size += PAGE_SIZE; if (!size) {  kfree (area);  return NULL; }

     addr = VMALLOC_START; write_lock(&vmlist_lock); for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {  if ((size + addr) < addr)   goto out;  if (size + addr <= (unsigned long) tmp->addr)   break;  next = tmp->size + (unsigned long) tmp->addr;  if (next > addr)    addr = next;  if (addr > VMALLOC_END-size)   goto out; } area->flags = flags; area->addr = (void *)addr; area->size = size; area->next = *p; *p = area; write_unlock(&vmlist_lock); return area;

    out: write_unlock(&vmlist_lock); kfree(area); return NULL;}

    static int remap_area_pages(unsigned long address, unsigned long phys_addr,     unsigned long size, unsigned long flags){ int error; pgd_t * dir; unsigned long end = address + size;

     phys_addr -= address; dir = pgd_offset_k(address); flush_cache_all(); if (address >= end)  BUG(); spin_lock(&init_mm.page_table_lock); do {  pmd_t *pmd;  pmd = pmd_alloc(&init_mm, dir, address);  error = -ENOMEM;  if (!pmd)   break;  if (remap_area_pmd(pmd, address, end - address,      phys_addr + address, flags))   break;  error = 0;  address = (address + PGDIR_SIZE) & PGDIR_MASK;  dir++; } while (address && (address < end)); spin_unlock(&init_mm.page_table_lock); flush_tlb_all(); return error;}

    其中的一些映射关系参考内存管理的分页管理


    最新回复(0)