Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15531638
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2007-08-04 18:24:32

浅析armlinux-setup_arch()->create_mapping()函数5-2-2

文章来源:http://gliethttp.cublog.cn

建议首先参考《浅析armlinux2_4_19启动程序[head-armv.s文件][http://gliethttp.cublog.cn]
//----------------------------------------
//1.arch/arm/mm/Mm-armv.c->create_mapping()
static void __init create_mapping(struct map_desc *md)
{
    unsigned long virt, length;
    int prot_sect, prot_pte;
    long off;
    if (md->prot_read && md->prot_write &&
     !md->cacheable && !md->bufferable) {
//提示设置不合理
        printk(KERN_WARNING "Security risk: creating user "
         "accessible mapping for 0x%08lx at 0x%08lx\n",
         md->physical, md->virtual);
    }
    if (md->virtual != vectors_base() && md->virtual < PAGE_OFFSET) {
//仅仅能创建0xffff0000开始的用于中断向量的虚拟地址映射页目录
//请参见《浅析arm-linux中断vector向量表的建立流程》
        printk(KERN_WARNING "MM: not creating mapping for "
         "0x%08lx at 0x%08lx in user region\n",
         md->physical, md->virtual);
    }
//页表属性值
    prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
         (md->prot_read ? L_PTE_USER : 0) |
         (md->prot_write ? L_PTE_WRITE : 0) |
         (md->cacheable ? L_PTE_CACHEABLE : 0) |
         (md->bufferable ? L_PTE_BUFFERABLE : 0);
//页目录属性值
    prot_sect = PMD_TYPE_SECT | PMD_DOMAIN(md->domain) |
         (md->prot_read ? PMD_SECT_AP_READ : 0) |
         (md->prot_write ? PMD_SECT_AP_WRITE : 0) |
         (md->cacheable ? PMD_SECT_CACHEABLE : 0) |
         (md->bufferable ? PMD_SECT_BUFFERABLE : 0);
    virt = md->virtual;
    off = md->physical - virt;
    length = md->length;
    while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) {
//如果虚拟地址virt或者其对应的物理地址(virt + off),只要有一个非1M页目录对齐,
//那么首先调整成1M页目录对齐
//将virt和其对应的物理地址(virt + off)建立映射,如果没有设置页目录,那么alloc_init_page
//将自动设置
        alloc_init_page(virt, virt + off, md->domain, prot_pte);
        virt += PAGE_SIZE;
        length -= PAGE_SIZE;
    }
    while (length >= PGDIR_SIZE) {
//还有至少1M的空间需要映射,那么可以使用alloc_init_section进行快速1M大小的节映射
        alloc_init_section(virt, virt + off, prot_sect);
        virt += PGDIR_SIZE;
        length -= PGDIR_SIZE;
    }
    while (length >= PAGE_SIZE) {
//经过1M节映射之后,仍然还有一些余项,那么继续完成4k页表映射[gliethttp]
        alloc_init_page(virt, virt + off, md->domain, prot_pte);
        virt += PAGE_SIZE;
        length -= PAGE_SIZE;
    }
}
//----------------------------------------
//2.rch/arm/mm/Mm-armv.c->alloc_init_page
static inline void
alloc_init_page(unsigned long virt, unsigned long phys, int domain, int prot)
{
    pmd_t *pmdp;
    pte_t *ptep;
//获取中间级目录入口,对于2级小页映射,pmdp就等于一级页目录入口,即:pmdp = &swapper_pg_dir[i]
    pmdp = pmd_offset(pgd_offset_k(virt), virt);
//对于虚拟映射的原理问题,请参考《linux2.4.19下__ioremap函数中remap_area_pages虚拟地址映射建立函数的代码分析》
    if (pmd_none(*pmdp)) {
//如果1级页目录dir,是空的,那么创建之
//其中PTRS_PER_PTE=256项二级页表项
//所以首先申请2*256*4=2k空间,之所以要多申请出1K空间,是因为需要存储相应页表项对应的kernel信息
//详细说明见下面的分析.[gliethttp]
        pte_t *ptep = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));
        ptep += PTRS_PER_PTE;//ptep指向页表项存储区的末端
//将swapper_pg_dir[i]=__mk_pmd(ptep, PMD_TYPE_TABLE | PMD_DOMAIN(domain)),
//一级页目录项,装入数值__mk_pmd(ptep, PMD_TYPE_TABLE| PMD_DOMAIN(domain))
        set_pmd(pmdp, __mk_pmd(ptep, PMD_TYPE_TABLE | PMD_DOMAIN(domain)));
    }
    ptep = pte_offset(pmdp, virt);//找到virt虚拟地址对应的pte页表项入口
    //ptep指向第256+(pte_t*)ptr项,即:1k~2k空间
    set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, __pgprot(prot)));
}
static inline pmd_t __mk_pmd(pte_t *ptep, unsigned long prot)
{
    unsigned long pte_ptr = (unsigned long)ptep;
    pmd_t pmd;
    pte_ptr -= PTRS_PER_PTE * sizeof(void *);//之前的pte_ptr指向的是页表项存储区的末端
    pmd_val(pmd) = __virt_to_phys(pte_ptr) | prot;
    return pmd;
}
//include/asm-arm/Pgtable.h有如下定义
#define pte_offset(dir, addr) ((pte_t *)pmd_page(*(dir)) + __pte_offset(addr))
#define __pte_offset(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
#define pfn_pte(pfn,prot) (__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot)))
//include/asm-arm/proc-armv/Pgtable.h有如下定义
static inline unsigned long pmd_page(pmd_t pmd)
{
    unsigned long ptr;
    ptr = pmd_val(pmd) & ~(PTRS_PER_PTE * sizeof(void *) - 1);
    ptr += PTRS_PER_PTE * sizeof(void *);//指向第256+(pte_t*)ptr项,即:1k~2k空间[gliethttp]
    return __phys_to_virt(ptr);
}
//include/asm-arm/proc-armv/Pgtable.h有如下定义
#define set_pte(pmdp,pmd) cpu_set_pte(ptep,pte)
//include/asm-arm/cpu-single.h中有如下定义
#define cpu_set_pte cpu_fn(CPU_NAME,_set_pte)
翻译之后
#define cpu_set_pte cpu_arm920_set_pte
相应的
#define cpu_set_pgd cpu_arm920_set_pgd
#define cpu_set_pmd cpu_arm920_set_pmd
#define cpu_set_pte cpu_arm920_set_pte
以上三个函数位于arch/arm/mm/proc-arm920.S
    .align    5
ENTRY(cpu_arm920_set_pte)
    str    r1, [r0], #-1024//2*1k空间,0~1k被使用,且r0处于1k~2k之间,那么r0=r0-1k,就位于0~1k空间
    eor    r1, r1, #LPTE_PRESENT | LPTE_YOUNG | LPTE_WRITE | LPTE_DIRTY//异或
    bic    r2, r1, #0xff0
    bic    r2, r2, #3
    orr    r2, r2, #HPTE_TYPE_SMALL
    tst    r1, #LPTE_USER | LPTE_EXEC @ User or Exec?
    orrne  r2, r2, #HPTE_AP_READ
    tst    r1, #LPTE_WRITE | LPTE_DIRTY @ Write and Dirty?
    orreq  r2, r2, #HPTE_AP_WRITE
    tst    r1, #LPTE_PRESENT | LPTE_YOUNG @ Present and Young?
    movne  r2, #0
    str    r2, [r0]//对该pte在0~1k相应的页表项赋值,我想0~1k空间是有kernel使用,和硬件mmu无关
    mov    r0, r0
    mcr    p15, 0, r0, c7, c10, 1         @ clean D entry
    mcr    p15, 0, r0, c7, c10, 4         @ drain WB
    mov    pc, lr
//所以可以看出《linux2.4.19下__ioremap函数中remap_area_pages虚拟地址映射建立函数的代码分析》中的pte是
//一个示意图,并不是实际的物理图,实际的物理图应该如下:
       |->pte-256->存储pte+000对应的kernel信息
       |->pte-255->存储pte+001对应的kernel信息
       ...
       |->pte-002->存储pte+254对应的kernel信息
       |->pte-001->存储pte+255对应的kernel信息
dir+0000->pte+000->4k
       |->pte+001->4k
       |->pte+002->4k
        ...
       |->pte+253->4k
       |->pte+254->4k
       |->pte+255->4k


       |->pte-256->存储pte+000对应的kernel信息
       |->pte-255->存储pte+001对应的kernel信息
       ...
       |->pte-002->存储pte+254对应的kernel信息
       |->pte-001->存储pte+255对应的kernel信息
dir+0001->pte+000->4k
       |->pte+001->4k
       |->pte+002->4k
        ...
       |->pte+253->4k
       |->pte+254->4k
       |->pte+255->4k

       ...


       |->pte-256->存储pte+000对应的kernel信息
       |->pte-255->存储pte+001对应的kernel信息
       ...
       |->pte-002->存储pte+254对应的kernel信息
       |->pte-001->存储pte+255对应的kernel信息
dir+4094->pte+000->4k
       |->pte+001->4k
       |->pte+002->4k
        ...
       |->pte+253->4k
       |->pte+254->4k
       |->pte+255->4k

       |->pte-256->存储pte+000对应的kernel信息
       |->pte-255->存储pte+001对应的kernel信息
       ...
       |->pte-002->存储pte+254对应的kernel信息
       |->pte-001->存储pte+255对应的kernel信息
dir+4095->pte+000->4k
       |->pte+001->4k
       |->pte+002->4k
        ...
       |->pte+253->4k
       |->pte+254->4k
       |->pte+255->4k

阅读(3400) | 评论(0) | 转发(4) |
给主人留下些什么吧!~~