浅析armlinux-setup_arch()->bootmem_init()函数4
文章来源:http://gliethttp.cublog.cn
建议首先参考《浅析armlinux2_4_19启动程序[head-armv.s文件]》[http://gliethttp.cublog.cn]
//---------------------------------------- //1.arch/arm/mm/Init.c->bootmem_init() void __init bootmem_init(struct meminfo *mi) { struct node_info node_info[NR_NODES], *np = node_info; unsigned int bootmap_pages, bootmap_pfn, map_pg; int node, initrd_node; //计算为了管理所有mem内存,需管理位图占据页数目bootmap_pages,np中存储mem对应的页帧号 bootmap_pages = find_memend_and_nodes(mi, np); //查找存放位图管理页的物理页帧号,实际是存放到_end后的后续页中 bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages); //检查initrd的合法性,同时将initrd所在内存bank的node返回给initrd_node initrd_node = check_initrd(mi); map_pg = bootmap_pfn;//位图页帧号 np += numnodes - 1; for (node = numnodes - 1; node >= 0; node--, np--) { /* * If there are no pages in this node, ignore it. * Note that node 0 must always have some pages. */ if (np->end == 0) { if (node == 0) BUG(); continue; } //将map_pg开始的位图管理空间全部置0xff init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); //释放虚拟地址node_bootmem_map开始的位图管理的所有页,使相应页可用[gliethttp] free_bootmem_node_bank(node, mi); map_pg += np->bootmap_pages; /* * If this is node 0, we need to reserve some areas ASAP - * we may use bootmem on node 0 to setup the other nodes. */ if (node == 0)//我的at91rm9200开发板仅仅有一个node=0 //将kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用 reserve_node_zero(bootmap_pfn, bootmap_pages); } #ifdef CONFIG_BLK_DEV_INITRD//在我的at91rm9200开发板中,initrd是开启的 //并且phys_initrd_start=0x21100000 //phys_initrd_size=6000000=0x5B8D80=5.73M //initrd_node=0; if (phys_initrd_size && initrd_node >= 0) { //将initrd占用的页对应的页位图置1,标识相应页已被占用 reserve_bootmem_node(NODE_DATA(initrd_node), phys_initrd_start, phys_initrd_size); initrd_start = __phys_to_virt(phys_initrd_start);//转成虚拟地址 initrd_end = initrd_start + phys_initrd_size; } #endif if (map_pg != bootmap_pfn + bootmap_pages)//保证所有bootmap都已经被遍历 BUG(); } //---------------------------------------- //2.arch/arm/mm/Init.c->find_memend_and_nodes() static unsigned int __init find_memend_and_nodes(struct meminfo *mi, struct node_info *np) {unsigned int i, bootmem_pages = 0, memend_pfn = 0; for (i = 0; i < NR_NODES; i++) {//默认失效 np[i].start = -1U; np[i].end = 0; np[i].bootmap_pages = 0; } for (i = 0; i < mi->nr_banks; i++) { unsigned long start, end; int node; if (mi->bank[i].size == 0) { mi->bank[i].node = -1;//该bank的node无效-1 continue; } node = mi->bank[i].node;//at91rm9200dk中mem连续node=0 if (node >= numnodes) { numnodes = node + 1; if (numnodes > NR_NODES) BUG(); } //获取当前bank的pfns //#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)//页边界对齐 //define O_PFN_UP(x) (PAGE_ALIGN(x) >> PAGE_SHIFT) start = O_PFN_UP(mi->bank[i].start);//获取该bank.start对应物理页帧号 //同理,//获取该bank.end对应物理页帧号 end = O_PFN_DOWN(mi->bank[i].start + mi->bank[i].size); if (np[node].start > start) np[node].start = start;//存储 if (np[node].end < end) np[node].end = end;//存储 if (memend_pfn < end) memend_pfn = end; } for (i = 0; i < numnodes; i++) { if (np[i].end == 0) continue; //bootmem_bootmap_pages计算pages个页需要多少个页来存储其位图管理信息 np[i].bootmap_pages = bootmem_bootmap_pages(np[i].end - np[i].start); bootmem_pages += np[i].bootmap_pages;//累计位图管理信息页总数 } /* * This doesn't seem to be used by the Linux memory * manager any more. If we can get rid of it, we * also get rid of some of the stuff above as well. */ max_low_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET); // max_pfn = memend_pfn - O_PFN_DOWN(PHYS_OFFSET); mi->end = memend_pfn << PAGE_SHIFT;//所管理的物理内存结束地址 return bootmem_pages; } //---------------------------------------- //3.mm/Bootmem.c->bootmem_bootmap_pages() //计算pages个页需要多少个页来存储其位图管理信息 unsigned long __init bootmem_bootmap_pages (unsigned long pages) {unsigned long mapsize; mapsize = (pages+7)/8;//所需8bits个数 mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK;//mapsize个字节页对齐 mapsize >>= PAGE_SHIFT;//mapsize个字节对应页数目 return mapsize; } //---------------------------------------- //4.arch/arm/mm/Init.c->find_bootmap_pfn() //查找存放位图管理页的物理页帧号-init_mm.brk static unsigned int __init find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages) {unsigned int start_pfn, bank, bootmap_pfn; start_pfn = V_PFN_UP(&_end);//将_end虚拟内存转换为对应的物理页帧号 bootmap_pfn = 0; for (bank = 0; bank < mi->nr_banks; bank ++) { unsigned int start, end; if (mi->bank[bank].node != node) continue; start = O_PFN_UP(mi->bank[bank].start); end = O_PFN_DOWN(mi->bank[bank].size + mi->bank[bank].start); if (end < start_pfn) continue; if (start < start_pfn) start = start_pfn;//_end对应init_mm.brk临界点
if (end <= start) continue; if (end - start >= bootmap_pages) { bootmap_pfn = start;//从init_mm.brk开始存放位图管理页 break; } } if (bootmap_pfn == 0) BUG(); return bootmap_pfn;//ok[gliethttp] } //---------------------------------------- //5.arch/arm/mm/Init.c->check_initrd() //检查initrd的合法性 static int __init check_initrd(struct meminfo *mi) { int initrd_node = -2; unsigned long end = phys_initrd_start + phys_initrd_size; #ifdef CONFIG_BLK_DEV_INITRD /* * Make sure that the initrd is within a valid area of * memory. */ if (phys_initrd_size) { unsigned int i; initrd_node = -1; for (i = 0; i < mi->nr_banks; i++) { unsigned long bank_end; bank_end = mi->bank[i].start + mi->bank[i].size; if (mi->bank[i].start <= phys_initrd_start && end <= bank_end) initrd_node = mi->bank[i].node; } } if (initrd_node == -1) { printk(KERN_ERR "initrd (0x%08lx - 0x%08lx) extends beyond " "physical memory - disabling initrd\n", phys_initrd_start, end); phys_initrd_start = phys_initrd_size = 0; } #endif return initrd_node;//将initrd所在内存bank对应的node返回 } //---------------------------------------- //6.mm/Bootmem.c->init_bootmem_node() unsigned long __init init_bootmem_node (pg_data_t *pgdat, unsigned long freepfn, unsigned long startpfn, unsigned long endpfn) { return(init_bootmem_core(pgdat, freepfn, startpfn, endpfn)); } //mm/Bootmem.c->init_bootmem_core() static unsigned long __init init_bootmem_core (pg_data_t *pgdat, unsigned long mapstart, unsigned long start, unsigned long end) { bootmem_data_t *bdata = pgdat->bdata;//读取&node_bootmem_data[0] unsigned long mapsize = ((end - start)+7)/8; pgdat->node_next = pgdat_list; pgdat_list = pgdat; mapsize = (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL);//4字节对齐 bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT);//位图页帧号转为系统用的虚拟地址 bdata->node_boot_start = (start << PAGE_SHIFT);//本map所处物理内存起始地址 bdata->node_low_pfn = end;//本map所处物理内存结束地址 //用0xff填充map位图页帧后,mapsize个字节数据 //arch/arm/lib/memset.S->memset(),对虚拟地址bdata->node_bootmem_map进行赋值 memset(bdata->node_bootmem_map, 0xff, mapsize); return mapsize; } //---------------------------------------- //7.mm/Bootmem.c->free_bootmem_node_bank() static inline void free_bootmem_node_bank(int node, struct meminfo *mi) { pg_data_t *pgdat = NODE_DATA(node);//读取&node_bootmem_data[0] int bank; for (bank = 0; bank < mi->nr_banks; bank++) if (mi->bank[bank].node == node) free_bootmem_node(pgdat, mi->bank[bank].start, mi->bank[bank].size); } void __init free_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size) { return(free_bootmem_core(pgdat->bdata, physaddr, size)); } static void __init free_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size) { unsigned long i; unsigned long start; /* * round down end of usable mem, partially free pages are * considered reserved. */ unsigned long sidx; unsigned long eidx = (addr + size - bdata->node_boot_start)/PAGE_SIZE; unsigned long end = (addr + size)/PAGE_SIZE; if (!size) BUG(); if (end > bdata->node_low_pfn)//一般相等 BUG(); start = (addr + PAGE_SIZE-1) / PAGE_SIZE;//mem起始物理地址对应的页帧号 sidx = start - (bdata->node_boot_start/PAGE_SIZE);//起始大小 for (i = sidx; i < eidx; i++) {///sidx=0,eidx=size/PAGE_SIZE[gliethttp] //清0各位图管理位,使相应页可用 //如i=4,那么第4物理页可用,清0bdata->node_bootmem_map[0]的第4位 if (!test_and_clear_bit(i, bdata->node_bootmem_map))//node_bootmem_map位图管理起始虚拟地址 BUG(); } } ///arch/arm/lib/testclearbit.S->test_and_clear_bit() ENTRY(test_and_clear_bit) add r1, r1, r0, lsr #3//获取第r0位,对应字节对应的地址 and r3, r0, #7//获取字节中偏移 mov r0, #1 //include/asm-arm/proc-armv/Assembler.h->save_and_disable_irqs[gliethttp] //.macro save_and_disable_irqs, oldcpsr, temp //mrs \oldcpsr, cpsr// //mov \temp, #I_BIT | MODE_SVC //msr cpsr_c, \temp //.endm save_and_disable_irqs ip, r2//将cpsr保存到ip ldrb r2, [r1]//取出字节数据 tst r2, r0, lsl r3//先测一次,我感觉没用 bic r2, r2, r0, lsl r3//将r2的第r3位清0,成功清0后Z=0,因为硬件故障未能成功那么Z=1,返回后会halt系统 strb r2, [r1]//存储清0后的结果 restore_irqs ip//恢复cpsr moveq r0, #0//如果因为硬件故障导致不能清0,那么r0=0;清0失败 RETINSTR(mov,pc,lr) //---------------------------------------- //8.arch/arm/mm/Init.c->reserve_node_zero() //将kernel自身和位图管理页占用的页对应的页位图置1,标识相应页已被占用 static __init void reserve_node_zero(unsigned int bootmap_pfn, unsigned int bootmap_pages) { pg_data_t *pgdat = NODE_DATA(0);//获取node0的所有物理内存管理单元pgdat //对于_end和_stext,参考arch/arm/vmlinux-armv.lds.in链接脚本 //_end为init_mm.brk,是kernel最末端地址 //__pa(&_stext)物理地址开始的&_end - &_stext个数据,对应页位图置1标识相应页已被占用 reserve_bootmem_node(pgdat, __pa(&_stext), &_end - &_stext); #ifdef CONFIG_CPU_32 //将arch/arm/kernel/head-armv.s中4M保留页swapper_pg_dir,我的at91rm9200板子对应的物理地址为0x20004000~0x20008000 //swapper_pg_dir~swapper_pg_dir+4k*4的页对应的页位图置1标识相应页已被占用 //#define PTRS_PER_PGD 4096 reserve_bootmem_node(pgdat, __pa(swapper_pg_dir), PTRS_PER_PGD * sizeof(pgd_t)); #endif //将_end即init_mm.brk之后存放node物理页位图的位图管理空间也保护起来 //bootmap_pfn << PAGE_SHIFT~(bootmap_pfn << PAGE_SHIFT)+(bootmap_pages << PAGE_SHIFT)的页对应的页位图置1标识相应页已被占用 reserve_bootmem_node(pgdat, bootmap_pfn << PAGE_SHIFT, bootmap_pages << PAGE_SHIFT); //以下代码在at91rm9200dk下均不被编译进vmlinuz if (machine_is_integrator())//if(0),否则会因为reserve_bootmem_node回环,halt系统 reserve_bootmem_node(pgdat, 0, __pa(swapper_pg_dir)); if (machine_is_archimedes() || machine_is_a5k()) reserve_bootmem_node(pgdat, 0x02000000, 0x00080000); if (machine_is_edb7211() || machine_is_fortunet()) reserve_bootmem_node(pgdat, 0xc0000000, 0x00020000); if (machine_is_p720t()) reserve_bootmem_node(pgdat, PHYS_OFFSET, 0x00014000); #ifdef CONFIG_SA1111//非SA1111 reserve_bootmem_node(pgdat, PHYS_OFFSET, __pa(swapper_pg_dir)-PHYS_OFFSET); #endif } void __init reserve_bootmem_node (pg_data_t *pgdat, unsigned long physaddr, unsigned long size) {//使pgdat管理的物理内存,physaddr~(physaddr+size)之间的物理内存不可使用--占用保留 //physaddr~(physaddr+size)对应页位图置1标识相应页已被占用 reserve_bootmem_core(pgdat->bdata, physaddr, size); } static void __init reserve_bootmem_core(bootmem_data_t *bdata, unsigned long addr, unsigned long size) { unsigned long i; /* * round up, partially reserved pages are considered * fully reserved. */ //bdata->node_boot_start该bdata对应node物理内存的起始地址 unsigned long sidx = (addr - bdata->node_boot_start)/PAGE_SIZE;//计算addr所处页帧号 unsigned long eidx = (addr + size - bdata->node_boot_start + PAGE_SIZE-1)/PAGE_SIZE;//计算addr+size对应的页帧号 unsigned long end = (addr + size + PAGE_SIZE-1)/PAGE_SIZE; if (!size) BUG(); if (sidx < 0) BUG(); if (eidx < 0) BUG(); if (sidx >= eidx)//数据回环 BUG(); //bdata->node_low_pfn为本node物理内存的地址上限 if ((addr >> PAGE_SHIFT) >= bdata->node_low_pfn) BUG(); if (end > bdata->node_low_pfn) BUG(); //条件符合,那么实行保护措施 //将sidx页到eidx页对应的位图置1,位图置1标识相应页已被占用(保护起来,不被kernel使用) for (i = sidx; i < eidx; i++) if (test_and_set_bit(i, bdata->node_bootmem_map)) printk("hm, page %08lx reserved twice.\n", i*PAGE_SIZE); } ///arch/arm/lib/testclearbit.S->test_and_set_bit() //ENTRY(test_and_set_bit) // add r1, r1, r0, lsr #3//获取第r0位对应的字节地址 // and r3, r0, #7//计算处于字节的第几位 // mov r0, #1 // save_and_disable_irqs ip, r2//ip=cpsr // ldrb r2, [r1] // tst r2, r0, lsl r3 //orr操作,成功操作后Z=0,因为硬件故障未能成功那么Z=1,返回后会halt系统 // orr r2, r2, r0, lsl r3//将r2中的第r3位置1 // strb r2, [r1]//回写 // restore_irqs ip // moveq r0, #0 // RETINSTR(mov,pc,lr) //include/asm-arm/proc-armv/Assembler.h->save_and_disable_irqs[gliethttp] //.macro save_and_disable_irqs, oldcpsr, temp //mrs \oldcpsr, cpsr// //mov \temp, #I_BIT | MODE_SVC
|