这个函数的实现在mm/bootmem.c中:
/*
* Called once to set up the allocator itself.
*/
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;
unsigned long 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;
}
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);
}
当参数传递到
init_bootmem_core时,各个参数的对应值为:
mapstart取的是
memory_start >> PAGE_SHIFT
,即内核代码结束后的第一个页序号
start取的是
PAGE_OFFSET >> PAGE_SHIFT
,即0。
end取的是
memory_end >> PAGE_SHIFT
,即SDRAM的最后一个页号。
1 bdata成员的意义
从这个函数的实现可以看出它只初始化了pg_data_t这个结构体的bdata成员,让我们看看bootmem_data_t的定义:
/*
* node_bootmem_map is a map pointer - the bits represent all physical
* memory pages (including holes) on the node.
*/
typedef
struct bootmem_data {
unsigned long node_boot_start;
unsigned long node_low_pfn;
void *node_bootmem_map;
unsigned long last_offset;
unsigned long last_pos;
unsigned long last_success; /* Previous allocation point. To speed
* up searching */
struct list_head list;
} bootmem_data_t;
从这个初始化函数可以大致看出几个成员的意义。
l
node_bootmem_map
bdata->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保存了内核结束后第一个页的页面地址,而不是页号。
l
node_boot_start
bdata->node_boot_start = PFN_PHYS(start);
取值为0。
l
node_low_pfn
bdata->node_low_pfn = end;
指向SDRAM中最后一个页的页序号。
2 link_bootmem
这个函数实现为:
/*
* link bdata in order
*/
static
void __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来讲,这个函数将只调用一次,用红色标出的代码将不会执行。因此bdata_list的链表也将只有一个元素!
3 get_mapsize
get_mapsize的实现为:
/*
* Given an initialised bdata, it returns the size of the boot bitmap
*/
static
unsigned long __init get_mapsize(bootmem_data_t *bdata)
{
unsigned long mapsize;
unsigned long start = PFN_DOWN(bdata->node_boot_start);
unsigned long end = bdata->node_low_pfn;
mapsize = ((end - start) + 7) / 8;
return ALIGN(mapsize, sizeof(long));
}
其中bdata->node_boot_start指向SDRAM的起始位置,其值为0。
bdata->node_low_pfn则为SDRAM最后一个页的页号。至此我们可以看出,bootmem是试图用一个二进制位表示一页是否占用,如果空闲则该位为0,如果已经使用则该位为1。
对于64M的SDRAM来讲,其页面大小为4K,因此其总共的页数为0x3fff(16K),所需要的字节数为0x800。
从这里还可以知道,bootmem用内核代码结束后的第1页来保存所有的SDRAM页面使用情况。