Linux内核---42.arm 内存初始化2

几个名词:
  1. Page Global Directory (PGD)
  2. Page Upper Directory (PUD)
  3. Page Middle Directory (PMD)
  4. Page Table (PTE)
  5. Page directory 页目录
  6. Page table entry 页表项

 Page Frame Number--> PFN

1. paging_init
setup_arch
    --> paging_init
  1. void __init paging_init(struct machine_desc *mdesc)
  2. {
  3.     void *zero_page;
  4.     //设置memblock.current_limit = lowmem_limit
  5.     memblock_set_current_limit(lowmem_limit);

  6.     build_mem_type_table();        //1.设置memblock.current_limit 
  7.     prepare_page_table();          //2.初始化pgd并清除pgd中所有项除映射实际内存地址的项
  8.     map_lowmem();                  //3.映射lowmem部分,即实际的内存部分
  9.     devicemaps_init(mdesc);        //4.设置memblock.current_limit
  10.     kmap_init();                   // 空

  11.     top_pmd = pmd_off_k(0xffff0000);

  12.     /* allocate the zero page. */
  13.     zero_page = early_alloc(PAGE_SIZE);

  14.     bootmem_init();                //5.设置memblock.current_limit

  15.     empty_zero_page = virt_to_page(zero_page);
  16.     __flush_dcache_page(NULL, empty_zero_page);
  17. }

2. page_table初始化
setup_arch
    --> paging_init
        --> prepare_page_table
这儿是要把pgd初始化:
初始化的过程并不是把pgd中所有的项全部清0,而是清除pgd中表示虚拟地址0-2048的项,
然后空出表示2048-(2048+mem_size)的项,
再清除表示(2048+mem_size - 4096M)的项.
  1. static inline void prepare_page_table(void)
  2. {   
  3.     //MODULES_VADDR=0x7f000000=2032M, PGDIR_SIZE=0x200000=2048K=2M
  4.     //清除0-2032M (0x0-0x7f000000)的页目录表
  5.     for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE)
  6.         pmd_clear(pmd_off_k(addr));
  7.     //清除2032M-2048M (0x7f000000-0x80000000)的页目录表//清除2032M-2048M的页目录表
  8.     for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)
  9.         pmd_clear(pmd_off_k(addr));
  10.     //memory.regions[0].base=0x60000000, size=0x8000000
  11.     end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
  12.     if (end >= lowmem_limit)
  13.         end = lowmem_limit;
  14.     //清除(2176M-3968M)0x88000000-0xf8000000处的页目录表 (2176-2048=128M)
  15.     for (addr = __phys_to_virt(end); addr < VMALLOC_END; addr += PGDIR_SIZE)
  16.         pmd_clear(pmd_off_k(addr));
  17. }
其中一个pmd可以表示1M的内存空间
A:这个pmd_clear函数为什么要把pmdp的下一个地址即pmdp[1]清除呢?
  1. #define pmd_clear(pmdp)            \
  2.     do {                \
  3.         pmdp[0] = __pmd(0);    \
  4.         pmdp[1] = __pmd(0);    \
  5.         clean_pmd_entry(pmdp);    \
  6.     } while (0)
要理解这个需要知道两点:
a. 因为一个pud可以表示2M的内存即上面的PGDIR_SIZE,而一个pmd可表示1M的内存

b. 这儿的pud和pmd的基地址是一样的,即清pmd时也就是清pud
B: 宏的定义
  1. //下面这两个只是去掉第二个参数,没有多大意义
  2. #define pmd_offset(dir, addr)    ((pmd_t *)(dir))

  3. #define pud_offset(pgd, start)        (pgd)

  4. //pgd_offset_k 就是取得mm->pgd+index的地址
  5. #define pgd_offset_k(addr)    pgd_offset(&init_mm, addr)
  6. #define pgd_offset(mm, addr)    ((mm)->pgd + pgd_index(addr))
  7. #define pgd_index(addr)        ((addr) >> PGDIR_SHIFT)
3.映射lowmem部分,即实际的内存部分
setup_arch
    --> paging_init
        --> map_lowmem
映射的过程说白了其实很简单:
假设要将phy_addr=0x60000000 size= 0x08000000的内存映射到virtual_addr= 0x80000000处
a. 通过pgd_offset 查出virtual_addr在pgd中的地址
b. 将phy_addr写到pgd中去

将虚拟地址 virtual_addr=0x80000000  转为物理地址的方法是:
a. 通过pgd_offset 查出virtual_addr在pgd中的地址
b. 读出pgd中的内容就是物理地址
  1. static void __init map_lowmem(void)
  2. {
  3.     struct memblock_region *reg;
  4.     for_each_memblock(memory, reg) {
  5.         //start=0x60000000, end=0x68000000
  6.         phys_addr_t start = reg->base;
  7.         phys_addr_t end = start + reg->size;
  8.         struct map_desc map;

  9.         if (end > lowmem_limit)
  10.             end = lowmem_limit;
  11.         if (start >= end)
  12.             break;
  13.              
  14.         map.pfn = __phys_to_pfn(start);       //map.pfn=0x60000
  15.         map.virtual = __phys_to_virt(start);  //把实际内存0x60000000映射到0x80000000处
  16.         map.length = end - start;             //映射的长度是0x08000000
  17.         map.type = MT_MEMORY;                 //映射的类型是MEMORY

  18.         create_mapping(&map);                 //映射的主函数
  19.     }
  20. }
3.1 对lowmem部分进行映射
setup_arch
    --> paging_init
        --> map_lowmem
            --> create_mapping
  1. static void __init create_mapping(struct map_desc *md)
  2. {
  3.     unsigned long addr, length, end;
  4.     phys_addr_t phys;
  5.     const struct mem_type *type;
  6.     pgd_t *pgd;

  7.     type = &mem_types[md->type];

  8.     addr = md->virtual & PAGE_MASK;     //addr=0x80000000
  9.     phys = __pfn_to_phys(md->pfn);      //phys=0x60000000
  10.     length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));  //length=0x08000000

  11.     pgd = pgd_offset_k(addr);           //pgd=0x80006000,pgd是虚地址addr所在页目录表的地址 
  12.     end = addr + length;                //end=0x88000000
  13.     do {
  14.         unsigned long next = pgd_addr_end(addr, end);    //下一个页目录表所映射的地址

  15.         alloc_init_pud(pgd, addr, next, phys, type);

  16.         phys += next - addr;
  17.         addr = next;
  18.     } while (pgd++, addr != end);
  19. }
3.1.1 映射
setup_arch
    --> paging_init
        --> map_lowmem
            --> create_mapping
                 --> alloc_init_pud
pgd 页目录表的基地址
addr 要映射的虚地址
phy  要映射的虚地址的物理地址
end 要映射的虚地址+偏移(PUD_SIZE)
  1. static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
  2.     unsigned long phys, const struct mem_type *type)
  3. {
  4.     pud_t *pud = pud_offset(pgd, addr);      //虚地址addr在页目录表的地址
  5.     unsigned long next;
  6.     do {
  7.         next = pud_addr_end(addr, end);      //next=addr+PUD_SIZE
  8.         alloc_init_section(pud, addr, next, phys, type);
  9.         phys += next - addr;
  10.     } while (pud++, addr = next, addr != end);
  11. }
3.1.1.1 真正的映射过程
setup_arch
    --> paging_init
        --> map_lowmem
            --> create_mapping
                 --> alloc_init_pud
                      --> alloc_init_section 
  1. #ifndef pud_addr_end
  2. #define pud_addr_end(addr, end)                        \
  3. ({    unsigned long __boundary = ((addr) + PUD_SIZE) & PUD_MASK;    \
  4.     (__boundary - 1 < (end) - 1)? __boundary: (end);        \
  5. })
  6. #endif

  7. pud: 虚地址addr在页目录表的地址
  8. addr: 要映射的虚地址
  9. end:  addr+PUD_SIZE
  10. phys: 虚地址addr的物理地址
  11. static void __init alloc_init_section(pud_t *pud, unsigned long addr,
  12.                  unsigned long end, phys_addr_t phys, const struct mem_type *type)
  13. {
  14.     //#define pmd_offset(dir, addr)    ((pmd_t *)(dir))
  15.     pmd_t *pmd = pmd_offset(pud, addr);
  16.     if (((addr | end | phys) & ~SECTION_MASK) == 0) {
  17.         pmd_t *= pmd;

  18.         if (addr & SECTION_SIZE)
  19.             pmd++;
  20.         //SECTION_SIZE=1<<20PUD_SIZE=1<<21,所以下面要执行两次
  21.         do {
  22.            //真正的映射这就这一句,很简单嘛:
  23.            //把要映射的地址加上标志填充到pgd中
  24.             *pmd = __pmd(phys | type->prot_sect);       
  25.             phys += SECTION_SIZE;
  26.         } while (pmd++, addr += SECTION_SIZE, addr != end);
  27.         flush_pmd_entry(p);
  28.     } else {   //接下来会用到,不过map_lowmem时不执行 
  29.         alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
  30.     }
  31. }
4. devicemaps_init
会调用memblock_alloc_base从物理内存的顶端分配一些内存
  1. static void __init devicemaps_init(struct machine_desc *mdesc)
  2. {
  3.     struct map_desc map;
  4.     unsigned long addr;
  5.     //没有具体看early_alloc干了什么,不过从打印信息看,它的作用是:
  6.     //在物理地址的最高处分配了一个page的内存: vectors_page=0x87fff000
  7.     vectors_page = early_alloc(PAGE_SIZE);             //为中断向量分配一个page的内存
  8.     //清除pgd映射了虚拟地址VAMLLOC_END-4G的项
  9.     //addr每次步进2M,直到溢出addr=0才结束
  10.     for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
  11.         pmd_clear(pmd_off_k(addr));

  12.     map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
  13.     map.virtual = 0xffff0000;                   //把中断向量表映射到0xffff0000   
  14.     map.length = PAGE_SIZE;                     //中断向量表要映射的长度是1个page 4K
  15.     map.type = MT_HIGH_VECTORS;                 //映射的类型是 MT_HIGH_VECTORS
  16.     create_mapping(&map);

  17.     if (mdesc->map_io)
  18.         mdesc->map_io();
  19.     
  20.     local_flush_tlb_all();
  21.     flush_cache_all();
  22. }
5. bootmem_init
setup_arch
    --> paging_init
        --> bootmem_init
  1. void __init bootmem_init(void)
  2. {
  3.     unsigned long min, max_low, max_high;
  4.     max_low = max_high = 0;
  5.     //内存开始0x60000000-0x68000000,其页帧号的起始0x60000-0x68000
  6.     //页帧号就是把地址 >> PAGE_SHIFT
  7.     find_limits(&min, &max_low, &max_high);    //5.1 获取页帧号的起始地址
  8.     arm_bootmem_init(min, max_low);            //5.2 初始化bootmem
  9.     arm_memory_present();                      //空 
  10.     sparse_init();                             //空        
  11.     arm_bootmem_free(min, max_low, max_high);  //5.3 初始化内存结点

  12.     high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1
  13.     max_low_pfn = max_low - PHYS_PFN_OFFSET;
  14.     max_pfn = max_high - PHYS_PFN_OFFSET;      //将一些值保存在全局变量中
  15. }
5.1  find_limits    获取页帧号的起始地址
setup_arch
    --> paging_init
        --> bootmem_init
               --> find_limits
  1. static void __init find_limits(unsigned long *min, unsigned long *max_low, unsigned long *max_high)
  2. {
  3.     struct meminfo *mi = &meminfo;
  4.     int i;
  5.     *min = -1UL;
  6.     *max_low = *max_high = 0;

  7.     for_each_bank (i, mi) {
  8.         struct membank *bank = &mi->bank[i];
  9.         unsigned long start, end;
  10.         //函数bank_pfn_start即把地址bank >> PAGE_SHIFT
  11.         start = bank_pfn_start(bank);  //内存开始bank_start=0x60000000,转为页帧号start=0x60000
  12.         end = bank_pfn_end(bank);      //内存结束bank_end=0x68000000,转为页帧号end=0x68000
  13.         if (*min > start)
  14.             *min = start;
  15.         if (*max_high < end)
  16.             *max_high = end;
  17.         if (bank->highmem)
  18.             continue;
  19.         if (*max_low < end)
  20.             *max_low = end;
  21.     }    //最后min=0x60000, max_low=0x68000, max_high=0x68000
  22. }
5.2  初始化bootmem
setup_arch
    --> paging_init
        --> bootmem_init
               --> arm_bootmem_init

  1. static void __init arm_bootmem_init(unsigned long start_pfn,  unsigned long end_pfn)
  2. {
  3.     struct memblock_region *reg;
  4.     unsigned int boot_pages;
  5.     phys_addr_t bitmap;
  6.     pg_data_t *pgdat;
  7.     //页帧号的起始:start_pfn=0x60000, end_pfn=0x68000
  8.     //所以共有页0x8000个,可以用0x8000/8=0x1000个Byte的位图来表示
  9.     //0x1000正好需要1个page来表示
  10.     boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);   //5.2.1
  11.     
  12.     //为位图分配内存: 物理地址bitmap=0x67ffa000,
  13.    //这儿为什么是0x67ffa000呢?因为memblock_alloc_base这个函数先从物理地址的结束0x68000000处开始分配内存,
  14.    //然后依次递减,这个过程好像类似于压栈
  15.     bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES, __pfn_to_phys(end_pfn));   

  16.     node_set_online(0);        //空函数
  17.     pgdat = NODE_DATA(0);      //获取pglist_data结构体contig_page_data的地址
  18.     init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);  //5.2.2初始化内存位图

  19.     for_each_memblock(memory, reg) {
  20.         unsigned long start = memblock_region_memory_base_pfn(reg);
  21.         unsigned long end = memblock_region_memory_end_pfn(reg);
  22.         if (end >= end_pfn)
  23.             end = end_pfn;
  24.         if (start >= end)
  25.             break;
  26.         //对物理内存在位图中置0,代表free
  27.         free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);         
  28.     }
  29.    
  30.     for_each_memblock(reserved, reg) {
  31.         unsigned long start = memblock_region_reserved_base_pfn(reg);
  32.         unsigned long end = memblock_region_reserved_end_pfn(reg);
  33.         if (end >= end_pfn)
  34.             end = end_pfn;
  35.         if (start >= end)
  36.             break;
  37.         //对reserve的memory在内存位图中置1
  38.         reserve_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);
  39.     }
  40. }
5.2.1 计算出整个内存位图所占的字节数
setup_arch
    --> paging_init
        --> bootmem_init
               --> arm_bootmem_init

                   -->  bootmem_bootmap_pages
  1. unsigned long __init bootmem_bootmap_pages(unsigned long pages)
  2. {
  3.     unsigned long bytes = bootmap_bytes(pages);  
  4.     return PAGE_ALIGN(bytes) >> PAGE_SHIFT;     //将表示整个位图需要的字节数,转为page
  5. }

  6. static unsigned long __init bootmap_bytes(unsigned long pages)
  7. {
  8.     unsigned long bytes = (pages + 7) / 8;       //位图中1个page占1位,那么表示整个位图需要的字节数
  9.     return ALIGN(bytes, sizeof(long));
  10. }
5.2.2 初始化内存位图
setup_arch
    --> paging_init
        --> bootmem_init
               --> arm_bootmem_init

                   -->  init_bootmem_node
                        --> init_bootmem_core
init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn);
pgdat 是
  获取pglist_data结构体contig_page_data的地址
freepfn 是内存位图的页帧
start_pfn与end_pfn是内存的起始页帧
  1. unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn,
  2.                 unsigned long startpfn, unsigned long endpfn)
  3. {
  4.     return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn);
  5. }

  6. static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
  7.     unsigned long mapstart, unsigned long start, unsigned long end)
  8. {
  9.     unsigned long mapsize;
  10.     mminit_validate_memmodel_limits(&start, &end);    //空函数
  11.     bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));  //将内存位图的首地址由物理地址转为虚地址
  12.     bdata->node_min_pfn = start;                      //
  13.     bdata->node_low_pfn = end;                        //
  14.     link_bootmem(bdata);                              //链表 
  15.     mapsize = bootmap_bytes(end - start); //上次计算的时储存整个位图需要的页数,这次只计算位图的大小size=0x1000     
  16.     memset(bdata->node_bootmem_map, 0xff, mapsize);   //将整个位图初始化为0xff
  17.     return mapsize;
  18. }
list_add_tail只执行这一句
  1. static void __init link_bootmem(bootmem_data_t *bdata)
  2. {
  3.     struct list_head *iter;

  4.     list_for_each(iter, &bdata_list) {
  5.         bootmem_data_t *ent;
  6.         ent = list_entry(iter, bootmem_data_t, list);
  7.         if (bdata->node_min_pfn < ent->node_min_pfn)
  8.             break;
  9.     }
  10.     list_add_tail(&bdata->list, iter);          //只执行了这一句
  11. }
5.3 初始化内存结点
setup_arch
    --> paging_init
        --> bootmem_init
               --> arm_bootmem_free

  1. static void __init arm_bootmem_free(unsigned long min, unsigned long max_lowunsigned long max_high)
  2. {
  3.     //min=0x60000, max_low=0x68000, max_high=0x68000, MAX_NR_ZONES=2
  4.     unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
  5.     struct memblock_region *reg;
  6.     memset(zone_size, 0, sizeof(zone_size));
  7.     zone_size[0] = max_low - min;           //zone_size[0]=0x8000
  8.     memcpy(zhole_size, zone_size, sizeof(zhole_size));
  9.     for_each_memblock(memory, reg) {
  10.         unsigned long start = memblock_region_memory_base_pfn(reg); //start=0x60000
  11.         unsigned long end = memblock_region_memory_end_pfn(reg);    //end=0x68000
  12.         if (start < max_low) {
  13.             unsigned long low_end = min(end, max_low);
  14.             zhole_size[0] -= low_end - start;  //zhole_size[0]=0x0,说明没有留下空洞hole
  15.         }
  16.     }

  17.     free_area_init_node(0, zone_size, min, zhole_size);   //初始化结点中所有内存区
  18. }


  1. void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
  2.         unsigned long node_start_pfn, unsigned long *zholes_size)
  3. {
  4.     pg_data_t *pgdat = NODE_DATA(nid);    //contig_page_data的基地址
  5.     pgdat->node_id = nid;
  6.     pgdat->node_start_pfn = node_start_pfn;
  7.     calculate_node_totalpages(pgdat, zones_size, zholes_size);
  8.     alloc_node_mem_map(pgdat);
  9.     free_area_init_core(pgdat, zones_size, zholes_size);
  10. }

分析不下去了,需要再好好看一下-->    memblock_alloc_base 等
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值