几个名词:
1. paging_init
setup_arch
--> paging_init
2. page_table初始化
setup_arch
--> paging_init
--> prepare_page_table
这儿是要把pgd初始化:
初始化的过程并不是把pgd中所有的项全部清0,而是清除pgd中表示虚拟地址0-2048的项,
然后空出表示2048-(2048+mem_size)的项,
再清除表示(2048+mem_size - 4096M)的项.
其中一个pmd可以表示1M的内存空间
A:这个pmd_clear函数为什么要把pmdp的下一个地址即pmdp[1]清除呢?
要理解这个需要知道两点:
a. 因为一个pud可以表示2M的内存即上面的PGDIR_SIZE,而一个pmd可表示1M的内存
b. 这儿的pud和pmd的基地址是一样的,即清pmd时也就是清pud
B: 宏的定义
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中的内容就是物理地址
3.1 对lowmem部分进行映射
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
3.1.1 映射
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
--> alloc_init_pud
pgd 页目录表的基地址
addr 要映射的虚地址
phy 要映射的虚地址的物理地址
end 要映射的虚地址+偏移(PUD_SIZE)
3.1.1.1 真正的映射过程
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
--> alloc_init_pud
--> alloc_init_section
4. devicemaps_init
会调用memblock_alloc_base从物理内存的顶端分配一些内存
5. bootmem_init
setup_arch
--> paging_init
--> bootmem_init
5.1
find_limits
获取页帧号的起始地址
setup_arch
--> paging_init
--> bootmem_init
--> find_limits
5.2
初始化bootmem
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_init
5.2.1 计算出整个内存位图所占的字节数
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_init
--> bootmem_bootmap_pages
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是内存的起始页帧
list_add_tail只执行这一句
5.3 初始化内存结点
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_free
分析不下去了,需要再好好看一下--> memblock_alloc_base 等
- Page Global Directory (PGD)
- Page Upper Directory (PUD)
- Page Middle Directory (PMD)
- Page Table (PTE)
- Page directory 页目录
- Page table entry 页表项
Page Frame Number--> PFN
setup_arch
--> paging_init
- void __init paging_init(struct machine_desc *mdesc)
- {
- void *zero_page;
- //设置memblock.current_limit = lowmem_limit
- memblock_set_current_limit(lowmem_limit);
-
- build_mem_type_table(); //1.设置memblock.current_limit
- prepare_page_table(); //2.初始化pgd并清除pgd中所有项除映射实际内存地址的项
- map_lowmem(); //3.映射lowmem部分,即实际的内存部分
- devicemaps_init(mdesc); //4.设置memblock.current_limit
- kmap_init(); // 空
-
- top_pmd = pmd_off_k(0xffff0000);
-
- /* allocate the zero page. */
- zero_page = early_alloc(PAGE_SIZE);
-
- bootmem_init(); //5.设置memblock.current_limit
-
- empty_zero_page = virt_to_page(zero_page);
- __flush_dcache_page(NULL, empty_zero_page);
- }
2. page_table初始化
setup_arch
--> paging_init
--> prepare_page_table
这儿是要把pgd初始化:
初始化的过程并不是把pgd中所有的项全部清0,而是清除pgd中表示虚拟地址0-2048的项,
然后空出表示2048-(2048+mem_size)的项,
再清除表示(2048+mem_size - 4096M)的项.
- static inline void prepare_page_table(void)
- {
- //MODULES_VADDR=0x7f000000=2032M, PGDIR_SIZE=0x200000=2048K=2M
- //清除0-2032M (0x0-0x7f000000)的页目录表
- for (addr = 0; addr < MODULES_VADDR; addr += PGDIR_SIZE)
- pmd_clear(pmd_off_k(addr));
- //清除2032M-2048M (0x7f000000-0x80000000)的页目录表//清除2032M-2048M的页目录表
- for ( ; addr < PAGE_OFFSET; addr += PGDIR_SIZE)
- pmd_clear(pmd_off_k(addr));
- //memory.regions[0].base=0x60000000, size=0x8000000
- end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
- if (end >= lowmem_limit)
- end = lowmem_limit;
- //清除(2176M-3968M)0x88000000-0xf8000000处的页目录表 (2176-2048=128M)
- for (addr = __phys_to_virt(end); addr < VMALLOC_END; addr += PGDIR_SIZE)
- pmd_clear(pmd_off_k(addr));
- }
A:这个pmd_clear函数为什么要把pmdp的下一个地址即pmdp[1]清除呢?
- #define pmd_clear(pmdp) \
- do { \
- pmdp[0] = __pmd(0); \
- pmdp[1] = __pmd(0); \
- clean_pmd_entry(pmdp); \
- } while (0)
a. 因为一个pud可以表示2M的内存即上面的PGDIR_SIZE,而一个pmd可表示1M的内存
b. 这儿的pud和pmd的基地址是一样的,即清pmd时也就是清pud
B: 宏的定义
- //下面这两个只是去掉第二个参数,没有多大意义
- #define pmd_offset(dir, addr) ((pmd_t *)(dir))
-
- #define pud_offset(pgd, start) (pgd)
-
- //pgd_offset_k 就是取得mm->pgd+index的地址
- #define pgd_offset_k(addr) pgd_offset(&init_mm, addr)
- #define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr))
- #define pgd_index(addr) ((addr) >> PGDIR_SHIFT)
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中的内容就是物理地址
- static void __init map_lowmem(void)
- {
- struct memblock_region *reg;
- for_each_memblock(memory, reg) {
- //start=0x60000000, end=0x68000000
- phys_addr_t start = reg->base;
- phys_addr_t end = start + reg->size;
- struct map_desc map;
-
- if (end > lowmem_limit)
- end = lowmem_limit;
- if (start >= end)
- break;
-
- map.pfn = __phys_to_pfn(start); //map.pfn=0x60000
- map.virtual = __phys_to_virt(start); //把实际内存0x60000000映射到0x80000000处
- map.length = end - start; //映射的长度是0x08000000
- map.type = MT_MEMORY; //映射的类型是MEMORY
-
- create_mapping(&map); //映射的主函数
- }
- }
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
- static void __init create_mapping(struct map_desc *md)
- {
- unsigned long addr, length, end;
- phys_addr_t phys;
- const struct mem_type *type;
- pgd_t *pgd;
-
- type = &mem_types[md->type];
-
- addr = md->virtual & PAGE_MASK; //addr=0x80000000
- phys = __pfn_to_phys(md->pfn); //phys=0x60000000
- length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); //length=0x08000000
-
- pgd = pgd_offset_k(addr); //pgd=0x80006000,pgd是虚地址addr所在页目录表的地址
- end = addr + length; //end=0x88000000
- do {
- unsigned long next = pgd_addr_end(addr, end); //下一个页目录表所映射的地址
-
- alloc_init_pud(pgd, addr, next, phys, type);
-
- phys += next - addr;
- addr = next;
- } while (pgd++, addr != end);
- }
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
--> alloc_init_pud
pgd 页目录表的基地址
addr 要映射的虚地址
phy 要映射的虚地址的物理地址
end 要映射的虚地址+偏移(PUD_SIZE)
- static void alloc_init_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
- unsigned long phys, const struct mem_type *type)
- {
- pud_t *pud = pud_offset(pgd, addr); //虚地址addr在页目录表的地址
- unsigned long next;
- do {
- next = pud_addr_end(addr, end); //next=addr+PUD_SIZE
- alloc_init_section(pud, addr, next, phys, type);
- phys += next - addr;
- } while (pud++, addr = next, addr != end);
- }
setup_arch
--> paging_init
--> map_lowmem
--> create_mapping
--> alloc_init_pud
--> alloc_init_section
- #ifndef pud_addr_end
- #define pud_addr_end(addr, end) \
- ({ unsigned long __boundary = ((addr) + PUD_SIZE) & PUD_MASK; \
- (__boundary - 1 < (end) - 1)? __boundary: (end); \
- })
- #endif
-
- pud: 虚地址addr在页目录表的地址
- addr: 要映射的虚地址
- end: addr+PUD_SIZE
- phys: 虚地址addr的物理地址
- static void __init alloc_init_section(pud_t *pud, unsigned long addr,
- unsigned long end, phys_addr_t phys, const struct mem_type *type)
- {
- //#define pmd_offset(dir, addr) ((pmd_t *)(dir))
- pmd_t *pmd = pmd_offset(pud, addr);
- if (((addr | end | phys) & ~SECTION_MASK) == 0) {
- pmd_t *p = pmd;
-
- if (addr & SECTION_SIZE)
- pmd++;
- //SECTION_SIZE=1<<20而PUD_SIZE=1<<21,所以下面要执行两次
- do {
- //真正的映射这就这一句,很简单嘛:
- //把要映射的地址加上标志填充到pgd中
- *pmd = __pmd(phys | type->prot_sect);
- phys += SECTION_SIZE;
- } while (pmd++, addr += SECTION_SIZE, addr != end);
- flush_pmd_entry(p);
- } else { //接下来会用到,不过map_lowmem时不执行
- alloc_init_pte(pmd, addr, end, __phys_to_pfn(phys), type);
- }
- }
会调用memblock_alloc_base从物理内存的顶端分配一些内存
- static void __init devicemaps_init(struct machine_desc *mdesc)
- {
- struct map_desc map;
- unsigned long addr;
- //没有具体看early_alloc干了什么,不过从打印信息看,它的作用是:
- //在物理地址的最高处分配了一个page的内存: vectors_page=0x87fff000
- vectors_page = early_alloc(PAGE_SIZE); //为中断向量分配一个page的内存
- //清除pgd映射了虚拟地址VAMLLOC_END-4G的项
- //addr每次步进2M,直到溢出addr=0才结束
- for (addr = VMALLOC_END; addr; addr += PGDIR_SIZE)
- pmd_clear(pmd_off_k(addr));
-
- map.pfn = __phys_to_pfn(virt_to_phys(vectors_page));
- map.virtual = 0xffff0000; //把中断向量表映射到0xffff0000处
- map.length = PAGE_SIZE; //中断向量表要映射的长度是1个page 4K
- map.type = MT_HIGH_VECTORS; //映射的类型是 MT_HIGH_VECTORS
- create_mapping(&map);
-
- if (mdesc->map_io)
- mdesc->map_io();
-
- local_flush_tlb_all();
- flush_cache_all();
- }
setup_arch
--> paging_init
--> bootmem_init
- void __init bootmem_init(void)
- {
- unsigned long min, max_low, max_high;
- max_low = max_high = 0;
- //内存开始0x60000000-0x68000000,其页帧号的起始0x60000-0x68000
- //页帧号就是把地址 >> PAGE_SHIFT
- find_limits(&min, &max_low, &max_high); //5.1 获取页帧号的起始地址
- arm_bootmem_init(min, max_low); //5.2 初始化bootmem
- arm_memory_present(); //空
- sparse_init(); //空
- arm_bootmem_free(min, max_low, max_high); //5.3 初始化内存结点
-
- high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1;
- max_low_pfn = max_low - PHYS_PFN_OFFSET;
- max_pfn = max_high - PHYS_PFN_OFFSET; //将一些值保存在全局变量中
- }
setup_arch
--> paging_init
--> bootmem_init
--> find_limits
- static void __init find_limits(unsigned long *min, unsigned long *max_low, unsigned long *max_high)
- {
- struct meminfo *mi = &meminfo;
- int i;
- *min = -1UL;
- *max_low = *max_high = 0;
-
- for_each_bank (i, mi) {
- struct membank *bank = &mi->bank[i];
- unsigned long start, end;
- //函数bank_pfn_start即把地址bank >> PAGE_SHIFT
- start = bank_pfn_start(bank); //内存开始bank_start=0x60000000,转为页帧号start=0x60000
- end = bank_pfn_end(bank); //内存结束bank_end=0x68000000,转为页帧号end=0x68000
- if (*min > start)
- *min = start;
- if (*max_high < end)
- *max_high = end;
- if (bank->highmem)
- continue;
- if (*max_low < end)
- *max_low = end;
- } //最后min=0x60000, max_low=0x68000, max_high=0x68000
- }
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_init
- static void __init arm_bootmem_init(unsigned long start_pfn, unsigned long end_pfn)
- {
- struct memblock_region *reg;
- unsigned int boot_pages;
- phys_addr_t bitmap;
- pg_data_t *pgdat;
- //页帧号的起始:start_pfn=0x60000, end_pfn=0x68000
- //所以共有页0x8000个,可以用0x8000/8=0x1000个Byte的位图来表示
- //0x1000正好需要1个page来表示
- boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn); //5.2.1
-
- //为位图分配内存: 物理地址bitmap=0x67ffa000,
- //这儿为什么是0x67ffa000呢?因为memblock_alloc_base这个函数先从物理地址的结束0x68000000处开始分配内存,
- //然后依次递减,这个过程好像类似于压栈
- bitmap = memblock_alloc_base(boot_pages << PAGE_SHIFT, L1_CACHE_BYTES, __pfn_to_phys(end_pfn));
-
- node_set_online(0); //空函数
- pgdat = NODE_DATA(0); //获取pglist_data结构体contig_page_data的地址
- init_bootmem_node(pgdat, __phys_to_pfn(bitmap), start_pfn, end_pfn); //5.2.2初始化内存位图
-
- for_each_memblock(memory, reg) {
- unsigned long start = memblock_region_memory_base_pfn(reg);
- unsigned long end = memblock_region_memory_end_pfn(reg);
- if (end >= end_pfn)
- end = end_pfn;
- if (start >= end)
- break;
- //对物理内存在位图中置0,代表free
- free_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT);
- }
-
- for_each_memblock(reserved, reg) {
- unsigned long start = memblock_region_reserved_base_pfn(reg);
- unsigned long end = memblock_region_reserved_end_pfn(reg);
- if (end >= end_pfn)
- end = end_pfn;
- if (start >= end)
- break;
- //对reserve的memory在内存位图中置1
- reserve_bootmem(__pfn_to_phys(start), (end - start) << PAGE_SHIFT, BOOTMEM_DEFAULT);
- }
- }
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_init
--> bootmem_bootmap_pages
- unsigned long __init bootmem_bootmap_pages(unsigned long pages)
- {
- unsigned long bytes = bootmap_bytes(pages);
- return PAGE_ALIGN(bytes) >> PAGE_SHIFT; //将表示整个位图需要的字节数,转为page
- }
-
- static unsigned long __init bootmap_bytes(unsigned long pages)
- {
- unsigned long bytes = (pages + 7) / 8; //位图中1个page占1位,那么表示整个位图需要的字节数
- return ALIGN(bytes, sizeof(long));
- }
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是内存的起始页帧
- unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn,
- unsigned long startpfn, unsigned long endpfn)
- {
- return init_bootmem_core(pgdat->bdata, freepfn, startpfn, endpfn);
- }
-
- static unsigned long __init init_bootmem_core(bootmem_data_t *bdata,
- unsigned long mapstart, unsigned long start, unsigned long end)
- {
- unsigned long mapsize;
- mminit_validate_memmodel_limits(&start, &end); //空函数
- bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart)); //将内存位图的首地址由物理地址转为虚地址
- bdata->node_min_pfn = start; //
- bdata->node_low_pfn = end; //
- link_bootmem(bdata); //链表
- mapsize = bootmap_bytes(end - start); //上次计算的时储存整个位图需要的页数,这次只计算位图的大小size=0x1000
- memset(bdata->node_bootmem_map, 0xff, mapsize); //将整个位图初始化为0xff
- return mapsize;
- }
- static void __init link_bootmem(bootmem_data_t *bdata)
- {
- struct list_head *iter;
-
- list_for_each(iter, &bdata_list) {
- bootmem_data_t *ent;
- ent = list_entry(iter, bootmem_data_t, list);
- if (bdata->node_min_pfn < ent->node_min_pfn)
- break;
- }
- list_add_tail(&bdata->list, iter); //只执行了这一句
- }
setup_arch
--> paging_init
--> bootmem_init
--> arm_bootmem_free
- static void __init arm_bootmem_free(unsigned long min, unsigned long max_low, unsigned long max_high)
- {
- //min=0x60000, max_low=0x68000, max_high=0x68000, MAX_NR_ZONES=2
- unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
- struct memblock_region *reg;
- memset(zone_size, 0, sizeof(zone_size));
- zone_size[0] = max_low - min; //zone_size[0]=0x8000
- memcpy(zhole_size, zone_size, sizeof(zhole_size));
- for_each_memblock(memory, reg) {
- unsigned long start = memblock_region_memory_base_pfn(reg); //start=0x60000
- unsigned long end = memblock_region_memory_end_pfn(reg); //end=0x68000
- if (start < max_low) {
- unsigned long low_end = min(end, max_low);
- zhole_size[0] -= low_end - start; //zhole_size[0]=0x0,说明没有留下空洞hole
- }
- }
-
- free_area_init_node(0, zone_size, min, zhole_size); //初始化结点中所有内存区
- }
- void __paginginit free_area_init_node(int nid, unsigned long *zones_size,
- unsigned long node_start_pfn, unsigned long *zholes_size)
- {
- pg_data_t *pgdat = NODE_DATA(nid); //contig_page_data的基地址
- pgdat->node_id = nid;
- pgdat->node_start_pfn = node_start_pfn;
- calculate_node_totalpages(pgdat, zones_size, zholes_size);
- alloc_node_mem_map(pgdat);
- free_area_init_core(pgdat, zones_size, zholes_size);
- }
分析不下去了,需要再好好看一下--> memblock_alloc_base 等