内核启动时,将调用setup_arch函数(arch/blackfin/kernel/setup.c)。在此函数中与bootmem相关的代码有:……………../** give all the memory to the bootmap allocator,tell it to put the* boot mem_map at the start of memory*/bootmap_size = init_bootmem_node(NODE_DATA(0), memory_start >> PAGE_SHIFT,/* map goes here */PAGE_OFFSET >> PAGE_SHIFT,memory_end >> PAGE_SHIFT);/** free the usable memory,we have to make sure we do not free* the bootmem bitmap so we then reserve it after freeing it :-)*/free_bootmem(memory_start, memory_end - memory_start);reserve_bootmem(memory_start, bootmap_size);此时,内存区域管理的初始化函数paging_init尚未调用,由此可知bootmem的作用是在内核启动完成前提供一种简单的内存管理策略。其中PAGE_OFFSET的定义位于include/asm/page.h:#define PAGE_OFFSET(PAGE_OFFSET_RAW)PAGE_OFFSET的定义位于include/asm/page_offset.h:#ifdef CONFIG_BLACKFIN#define PAGE_OFFSET_RAW0x00000000#endif
1.1.3.2init_bootmem_node
在setup_arch函数中调用了init_bootmem_node,这个函数的实现在mm/bootmem.c中:/** Called once to set up the allocator itself.*/staticunsignedlong __init init_bootmem_core(pg_data_t *pgdat,unsignedlong mapstart, unsignedlong start, unsignedlong end){bootmem_data_t *bdata = pgdat->bdata;unsignedlong mapsize;bdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));bdata->node_boot_start = PFN_PHYS(start);bdata->node_low_pfn = end;link_bootmem(bdata);/** Initially all pages are reserved - setup_arch() has to* register free RAM areas explicitly.*/mapsize = get_mapsize(bdata);memset(bdata->node_bootmem_map, 0xff, mapsize);return mapsize;}unsignedlong __init init_bootmem_node(pg_data_t *pgdat, unsignedlong freepfn,unsignedlong startpfn, unsignedlong endpfn){return init_bootmem_core(pgdat, freepfn, startpfn, endpfn);}当参数传递到init_bootmem_core时,各个参数的对应值为:mapstart取的是memory_start >> PAGE_SHIFT,即内核代码结束后的第一个页序号start取的是PAGE_OFFSET >> PAGE_SHIFT,即0。end取的是memory_end >> PAGE_SHIFT,即SDRAM的最后一个页号。请记住,pgdat指向的是全局唯一的一个变量contig_page_data,它的bdata成员指向全局唯一的一个变量contig_bootmem_data。从这个初始化函数可以大致看出pg_data_t结构体中几个成员的意义。lnode_bootmem_mapbdata->node_bootmem_map = phys_to_virt(PFN_PHYS(mapstart));在这行语句中,mapstart是内核代码结束后的第一个页序号,PFN_PHYS定义为:#define PFN_PHYS(x)((x) << PAGE_SHIFT)而phys_to_virt则定义为:#define phys_to_virt(vaddr)((void *) (vaddr))因此node_bootmem_map保存了内核结束后第一个页的页面地址,而不是页号。lnode_boot_startbdata->node_boot_start = PFN_PHYS(start);取值为0。lnode_low_pfnbdata->node_low_pfn = end;指向SDRAM中最后一个页的页序号。从最后两行可以看出,初始化时将所有的页面都标记为已经使用。
1.1.3.3link_bootmem
在init_bootmem_core函数中调用了link_bootmem,这个函数实现为:/** link bdata in order*/staticvoid __init link_bootmem(bootmem_data_t *bdata){bootmem_data_t *ent;if (list_empty(&bdata_list)) {list_add(&bdata->list, &bdata_list);return;}/* insert in order */list_for_each_entry(ent, &bdata_list, list) {if (bdata->node_boot_start < ent->node_boot_start) {list_add_tail(&bdata->list, &ent->list);return;}}list_add_tail(&bdata->list, &bdata_list);}它的作用就是将bdata放到一个叫bdata_list的链表中,bdata_list的定义在bootmem.c中:static LIST_HEAD(bdata_list);对于BF561来讲,这个函数将只调用一次,用红 {MOD}标出的代码将不会执行。因此bdata_list的链表也将只有一个元素!
1.1.3.4get_mapsize
在init_bootmem_core函数中调用了get_mapsize,其实现为:/** Given an initialised bdata, it returns the size of the boot bitmap*/staticunsignedlong __init get_mapsize(bootmem_data_t *bdata){unsignedlong mapsize;