首页 ioremap的完成细节[优质文档]

ioremap的完成细节[优质文档]

举报
开通vip

ioremap的完成细节[优质文档]ioremap的完成细节[优质文档] ioremap的实现细节 linux内核ioremap函数解析:其中涉及到页表的映射,以及高端虚 拟内存的使用,在linux中ioremap函数和vmalloc函数的实现大体 相同。 /* * Remap an arbitrary physical address space into the kernel virtual * address space. Needed when the kernel wants to access high addresses * ...

ioremap的完成细节[优质文档]
ioremap的完成细节[优质文档] ioremap的实现细节 linux内核ioremap函数解析:其中涉及到页表的映射,以及高端虚 拟内存的使用,在linux中ioremap函数和vmalloc函数的实现大体 相同。 /* * 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 __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags, unsigned long align) { 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.. */ /* 获得一个可用虚拟区间,对低端虚拟地址区间内核用vm_area_struct结构表示, 而高端虚拟地址区间用vm_struct表示 */ area = get_vm_area(size, VM_IOREMAP); if (!area) return NULL; addr = area->addr; /* 进行真正的页面映射,需要分配页表 */ if (remap_area_pages((unsigned long) addr, phys_addr, size, flags)) { vfree(addr); return NULL; } /* 关于返回值,程序作者做了很好的解释,那就是内核实际的映射是要页 对齐的,这里把页对齐的地址转换成非页对齐的地址 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.*/ return (void __iomem *) (offset + (char *)addr); } struct vm_struct *get_vm_area(unsigned long size, unsigned long flags) { /* #define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define VMALLOC_VMADDR(x) ((unsigned long)(x)) #define VMALLOC_END (0xE0000000) */ /* 关于high_memory:linux内核规定,只映射0-896M真是物理 内存(如果有的话, 称为low memory)到内核空间,也就是0xc0000000+0到 0xc0000000+896M,而且是线性映射,有物理地址0<=x<=896M, 就有内核地址0xc0000000+x.如果物理内存y<896M,则 high_memory=0xc0000000+y(是虚拟地址);否则 high_memory=0xc0000000+896M或者说high_memory不能超 过0xc0000000+896M //high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); 那么 high_memory是“具体物理内存的上限对应的虚拟地 址”就比较好理解了.*/ return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END); } struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, unsigned long start, unsigned long end) { struct vm_struct **p, *tmp, *area; unsigned long align = 1; unsigned long addr; if (flags & VM_IOREMAP) { int bit = fls(size); /* 用来计算所要分配的页面数 */ if (bit > IOREMAP_MAX_ORDER) bit = IOREMAP_MAX_ORDER; else if (bit < PAGE_SHIFT) bit = PAGE_SHIFT; align = 1ul << bit; } addr = ALIGN(start, align); /* start的值要bit页面对齐 */ area = kmalloc(sizeof(*area), GFP_KERNEL); if (unlikely(!area)) return NULL; /* * We always allocate a guard page. */ size += PAGE_SIZE;/* 预留一个页面的空洞,这是linux的规定,据说容易检测到内存越 界访问 */ if (unlikely(!size)) { kfree (area);/* unlikely是说(!size)为真的可能性小,这样编译器在编译时,就会 进行相应的代码优化 */ return NULL; } static int remap_area_pages(unsigned long start, unsigned long phys_addr, unsigned long size, unsigned long flags) { unsigned long address = start; unsigned long end = start + size; int err = 0; pgd_t * dir; phys_addr -= address; /* 根据虚拟地址,计算出所在的目录项 */ dir = pgd_offset(&init_mm, address); BUG_ON(address >= end); spin_lock(&init_mm.page_table_lock); do { pmd_t *pmd = pmd_alloc(&init_mm, dir, address); if (!pmd) { err = -ENOMEM; break; } /* 由于linux采用2映射,所以pmd==pgt */ if (remap_area_pmd(pmd, address, end - address, phys_addr + address, flags)) { err = -ENOMEM; break; } /* PGIDR_SIZE = 1<<20 */ address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++;/* 在ARM中每个目录项对应1M */ } while (address && (address < end)); spin_unlock(&init_mm.page_table_lock); flush_cache_vmap(start, end); return err; } static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, unsigned long phys_addr, unsigned long flags) { unsigned long end; pgprot_t pgprot; address &= ~PGDIR_MASK; end = address + size; if (end > PGDIR_SIZE) end = PGDIR_SIZE; phys_addr -= address; BUG_ON(address >= end); /* 页面标志 */ pgprot = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | L_PTE_WRITE | flags); do { pte_t * pte = pte_alloc_kernel(&init_mm, pmd, address); if (!pte) return -ENOMEM; remap_area_pte(pte, address, end - address, address + phys_addr, pgprot); address = (address + PMD_SIZE) & PMD_MASK; pmd++; } while (address && (address < end));/* linux采用2级映 射,所以此while循环一 次 */ return 0; } static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, unsigned long phys_addr, pgprot_t pgprot) { unsigned long end; address &= ~PMD_MASK; end = address + size; if (end > PMD_SIZE) end = PMD_SIZE; BUG_ON(address >= end); do { if (!pte_none(*pte)) goto bad; /* 把物理地址设置到pte中 */ set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot)); address += PAGE_SIZE; phys_addr += PAGE_SIZE; pte++; /* 一个pte对应4k */ } while (address && (address < end)); return; bad: printk("remap_area_pte: page already exists\n"); BUG(); }
本文档为【ioremap的完成细节[优质文档]】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_180829
暂无简介~
格式:doc
大小:26KB
软件:Word
页数:9
分类:生活休闲
上传时间:2017-09-26
浏览量:12