分享

[原创]Linux arm 启动 c语言部分详解第二讲(Start kernel-> setup_a - 技术文档 - 安装启动 Linux时代 - 开源、自由、共享 - 中国最大的Linux技术社区

 langhuayipian 2010-09-01
Written by leeming
这一讲是主要讲setup_arch中那个没有解释的函数解释完毕,完成setup_arch的函数,好让我们的start_kernel继续下去。
/*
* paging_init() sets up the page tables, initialises the zone memory
* maps, and sets up the zero page, bad page and bad page tables.
*这部分的主要工作建立页表,初始化内存。
*/
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
       void *zero_page;
//这个函数主要是用来建立各种类型的页表选项(比如内存是MEMORY类型,设备室DEVICE,中断向量表是HIGH_VECTORS)
       build_mem_type_table();
{
       struct cachepolicy *cp;
       //获取cp15处理器的c1寄存器位
       unsigned int cr = get_cr();
       unsigned int user_pgprot, kern_pgprot;
       //获取处理器架构版本
       int cpu_arch = cpu_architecture();
       int i;

       //根据处理器版本号调整cache政策,不是写缓冲区的政策
#if defined(CONFIG_CPU_DCACHE_DISABLE)
       if (cachepolicy > CPOLICY_BUFFERED)
              cachepolicy = CPOLICY_BUFFERED;
#elif defined(CONFIG_CPU_DCACHE_WRITETHROUGH)
       if (cachepolicy > CPOLICY_WRITETHROUGH)
              cachepolicy = CPOLICY_WRITETHROUGH;
#endif
       if (cpu_arch
              if (cachepolicy >= CPOLICY_WRITEALLOC)
                     cachepolicy = CPOLICY_WRITEBACK;
              ecc_mask = 0;//因为v5前的处理器的一级描述符没有定义第9位作为保护标志位
       }

       if (cpu_arch
              //mem_types是一个全局数组arch/arm/mm-armv.c,里面有所有类型
              for (i = 0; i
                     //prot_l1 prot_sect都是一级描述符的意思
                     //将一级描述符的第4位置1
                     if (mem_types.prot_l1)
                            mem_types.prot_l1 |= PMD_BIT4;
                     if (mem_types.prot_sect)
                            mem_types.prot_sect |= PMD_BIT4;
              }
       }

       //我们的cachepolicy是3,因此相应的配置如下
       //     .policy          = "writeback",
       //     .cr_mask       = 0,
       //     .pmd             = PMD_SECT_WB,
       //     .pte        = PTE_BUFFERABLE|PTE_CACHEABLE,
       cp = &cache_policies[cachepolicy];
       //kern_pgprot user_pgprot是内核和用户空间的二级页表描述符
       kern_pgprot = user_pgprot = cp->pte;

       //以下删除了非v4t架构的高版本代码

       for (i = 0; i
              //这里依次获取16个默认的保护类型的值
              unsigned long v = pgprot_val(protection_map);
              //(L_PTE_BUFFERABLE|L_PTE_CACHEABLE)这是linux pte的定义
              //内核中有linux和hardware两种定义方式,为了更好的兼容性
              //这里两者间是匹配的,这里将值再加上我们的设置就是
              //最新的16个值,将它写回更新
              v = (v & ~(L_PTE_BUFFERABLE|L_PTE_CACHEABLE)) | user_pgprot;
              protection_map = __pgprot(v);
       }

       mem_types[MT_LOW_VECTORS].prot_pte |= kern_pgprot;
       mem_types[MT_HIGH_VECTORS].prot_pte |= kern_pgprot;
       mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1);

       pgprot_kernel = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG |
                             L_PTE_DIRTY | L_PTE_WRITE |
                             L_PTE_EXEC | kern_pgprot);

       mem_types[MT_LOW_VECTORS].prot_l1 |= ecc_mask;
       mem_types[MT_HIGH_VECTORS].prot_l1 |= ecc_mask;
       mem_types[MT_MEMORY].prot_sect |= ecc_mask | cp->pmd;
       mem_types[MT_ROM].prot_sect |= cp->pmd;

       switch (cp->pmd) {
       case PMD_SECT_WT:
              mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT;
              break;
       case PMD_SECT_WB:
       case PMD_SECT_WBWA:
              mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
              break;
       }
       //以上所有的操作都是为了给mem_types这个结构体中的各种类型中的页表参数添加上我们的要求,主要是一级页表,二级页表,ap(访问权限控制);至于domain是利用系统初始化时的值,不用我们再进行干预。
//系统的domain类型一共有四种,kernel——0;user——1;io——2
       printk("Memory policy: ECC %sabled, Data cache %s\n",
              ecc_mask ? "en" : "dis", cp->policy);
}
       bootmem_init(mi);
{
              unsigned long addr, memend_pfn = 0;
       int node, initrd_node, i;

       /*
        * Invalidate the node number for empty or invalid memory banks
        */
       for (i = 0; i nr_banks; i++)
              if (mi->bank.size == 0 || mi->bank.node >= MAX_NUMNODES)
                     mi->bank.node = -1;

       //将在4020.c fixup函数中定义的内存信息添加到meminfo结构体中
       memcpy(&meminfo, mi, sizeof(meminfo));

      
//MODULE_START是0xc0000000-16M; 以2M为单位,清除内核空间一下的用户空间
       for (addr = 0; addr
//内核在进入保护模式前, 还没有启用分页功能, 在这之前内核要先建立一个临时内核页表,因为在进入保护模式后, 内核继续初始化直到建
//立完整的内存映射机制之前, 仍然需要用到页表来映射相应的内存地址。 临时页表的初始化是在arch/i386/kernel/head.S中进行的:
//swapper_pg_dir是临时页全局目录表, 它是在内核编译过程中静态初始化的.
//pg0是第一个页表开始的地方, 它也是内核编译过程中静态初始化的.
//pmd_off_k是获取虚拟地址为addr的页表项地址
//pmd_clear是将()中的页表项地址中的数据清0
       pmd_clear(pmd_off_k(addr));
      
#ifdef CONFIG_XIP_KERNEL
       /* The XIP kernel is mapped in the module area -- skip over it */
       addr = ((unsigned long)&_etext + PGDIR_SIZE - 1) & PGDIR_MASK;
#endif

       //防止xip之后会有变化,检查,做一次用户空间的清除
       for ( ; addr
              pmd_clear(pmd_off_k(addr));

       /*
        * Clear out all the kernel space mappings, except for the first
        * memory bank, up to the end of the vmalloc region.
        */
        //清除内核空间,但是不清楚内存所在区域,也就是
        //0xc2000000-0xd0000000的空间
       for (addr = __phys_to_virt(mi->bank[0].start + mi->bank[0].size);
            addr
              pmd_clear(pmd_off_k(addr));

       /*
        * Locate which node contains the ramdisk image, if any.
        */
        //返回如果有initrd所在的内存节点
       initrd_node = check_initrd(mi);

       /*
        * Run through each node initialising the bootmem allocator.
        */
       for_each_node(node) {
              unsigned long end_pfn;

              //为内存建立一级页表(多的话还有二级页表)
              end_pfn = bootmem_init_node(node, initrd_node, mi);

              /*
               * Remember the highest memory PFN.
               */
              if (end_pfn > memend_pfn)
                     memend_pfn = end_pfn;
       }

       high_memory = __va(memend_pfn

       /*
        * This doesn't seem to be used by the Linux memory manager any
        * more, but is used by ll_rw_block.  If we can get rid of it, we
        * also get rid of some of the stuff above as well.
        *
        * Note: max_low_pfn and max_pfn reflect the number of _pages_ in
        * the system, not the maximum PFN.
        */
       max_pfn = max_low_pfn = memend_pfn - PHYS_PFN_OFFSET;
}
       devicemaps_init(mdesc);
{
       struct map_desc map;
       unsigned long addr;
       void *vectors;

       /*
        * Allocate the vector page early.
        */
        //为中断向量表申请一页的空间,申请的位置
        //就是之前在内存中建立的页表的物理地址
       vectors = alloc_bootmem_low_pages(PAGE_SIZE);
       BUG_ON(!vectors);

       for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
       //pmd_off_k是获取虚拟地址为addr的页表项地址
       //pmd_clear是将()中的页表项地址中的数据清0
              pmd_clear(pmd_off_k(addr));

       /*
        * Map the kernel if it is XIP.
        * It is always first in the modulearea.
        */
#ifdef CONFIG_XIP_KERNEL
       map.pfn = __phys_to_pfn(CONFIG_XIP_PHYS_ADDR & PGDIR_MASK);
       map.virtual = MODULE_START;
       map.length = ((unsigned long)&_etext - map.virtual + ~PGDIR_MASK) & PGDIR_MASK;
       map.type = MT_ROM;
       create_mapping(&map);
#endif

       /*
        * Map the cache flushing regions.
        */
#ifdef FLUSH_BASE
       map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS);
       map.virtual = FLUSH_BASE;
       map.length = PGDIR_SIZE;
       map.type = MT_CACHECLEAN;
       create_mapping(&map);
#endif
#ifdef FLUSH_BASE_MINICACHE
       map.pfn = __phys_to_pfn(FLUSH_BASE_PHYS + PGDIR_SIZE);
       map.virtual = FLUSH_BASE_MINICACHE;
       map.length = PGDIR_SIZE;
       map.type = MT_MINICLEAN;
       create_mapping(&map);
#endif

       /*
        * Create a mapping for the machine vectors at the high-vectors
        * location (0xffff0000).  If we aren't using high-vectors, also
        * create a mapping at the low-vectors virtual address.
        */
        //为中断向量表建立页映射,相应的选项配置在
        //build_mem_type_table()中已经设置好了
       map.pfn = __phys_to_pfn(virt_to_phys(vectors));
       map.virtual = 0xffff0000;
       map.length = PAGE_SIZE;
       map.type = MT_HIGH_VECTORS;
       create_mapping(&map);

       //如果没有配置为高端向量
       if (!vectors_high()) {
              map.virtual = 0;
              map.type = MT_LOW_VECTORS;
              create_mapping(&map);
       }

       /*
        * Ask the machine support to map in the statically mapped devices.
        */
        //其实就是调用了iotable_init(sep4020_io_desc, ARRAY_SIZE(sep4020_io_desc))函数
        //而这个iotable_init函数调用了create_mapping这个函数把我们这个数组中的各个成员建立页表;
       if (mdesc->map_io)
              mdesc->map_io();

       /*
        * Finally flush the caches and tlb to ensure that we're in a
        * consistent state wrt the writebuffer.  This also ensures that
        * any write-allocated cache lines in the vector page are written
        * back.  After this point, we can start to touch devices again.
        */
        //建立完页表一定要刷tlb,原因见上英文部分
        local_flush_tlb_all();
       flush_cache_all();
}
       //关于pgd,pmd,pte
       //PGD每个条目中指向一个PUD,PUD的每个条目指向
       //一个PMD,PMD的每个条目指向一个PTE,PTE的每个条目指向一个页面(Page)的物理首地址。
       //在arm中没有使用pud,pmd也是直接返回的
       //这里就是返回0xffff0000这一页在pgd中的偏移项
       top_pmd = pmd_off_k(0xffff0000);

       /*
        * allocate the zero page.  Note that we count on this going ok.
        */
        //empty_zero_page是一中特殊的页,供初始化为0的数据和写时复制(cow)使用
       zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
       memzero(zero_page, PAGE_SIZE);
       empty_zero_page = virt_to_page(zero_page);
       flush_dcache_page(empty_zero_page);
}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多