源代码:
/* * 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;}
其中的一些映射关系参考内存管理的分页管理