offset: 物理空间(I/O设备上的一块物理内存)的起始地址 size: 物理空间的大小 给一段物理地址(起始地址offset)建立页表(地址映射) -------------------------------------------------- static inline void __iomem * ioremap(unsigned long offset, unsigned long size) { return __ioremap(offset, size, 0); } Remap an arbitrary physical address space into the kernel virtual address space. Needed when the kernel wants to access high addresses directly. -------------------------------------------------- void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags){ void __iomem * addr; struct vm_struct * area; unsigned long offset, last_addr; last_addr = phys_addr + size - 1; if (!size || last_addr < phys_addr) return NULL; if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS) return (void __iomem *) phys_to_virt(phys_addr); 如果需要映射的空间的起始地址phys_addr为于ZONE_NORMAL区,通过virt_to_page()进行映射 |------------------------------------------------------------------------------| | if (phys_addr <= virt_to_phys(high_memory - 1)) | | { | | char *t_addr, *t_end; | | struct page *page; | | t_addr = __va(phys_addr); | | t_end = t_addr + (size - 1); | | for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) -| | if(!PageReserved(page)) | | return NULL; | | } | |------------------------------------------------------------------------------| offset = phys_addr & ~PAGE_MASK; phys_addr &= PAGE_MASK; size = PAGE_ALIGN(last_addr+1) - phys_addr; 通过“非连续存储器区”进行映射 get_vm_area()创建类型为vm_struct的新描述符 get_vm_area()首先调用kmalloc()为新描述符获得一个存储区;然后扫描类型为struct vm_struct的描述符表,查找一个可用线性地址空间(至少包含size+4096个地址)。 |----------------------------------------------------------| | area = get_vm_area(size, VM_IOREMAP | (flags << 20)); -| | if (!area) | | return NULL; | |----------------------------------------------------------| area->phys_addr = phys_addr; addr = (void __iomem *) area->addr; ioremap_page_range()负责为一段物理地址(起始地址为phys_addr)和一段线性地址(起始地址为addr > VMALLOC_START, 大小为size)建立映射页表(建立的页表由内核页表的最高32项的其中几个目录项进行管理),如果建立成功则返回0 |-----------------------------------------------------------| | if (ioremap_page_range( -(unsigned long) addr, | | (unsigned long) addr + size, | | phys_addr, | | flags)) | | { | | vunmap((void __force *) addr); | | return NULL; | | } | |-----------------------------------------------------------| return (void __iomem *) (offset + (char __iomem *)addr); } |
|