Linux内核有两个重要的宏:PHYS_OFFSET和PAGE_OFFSET。PHYS_OFFSET是物理内存的起始地址,PAGE_OFFSET是Linux内核空间的虚拟起始地址(默认为0xC0000000,可通过menuconfig配置,CONFIG_PAGE_OFFSET)。PHYS_OFFSET可通过menuconfig配置(CONFIG_PHYS_OFFSET),但一般不直接配置。 如果定义了CONFIG_ARM_PATCH_PHYS_VIRT,则内核会根据实际所在的地址调整PHYS_OFFSET的值。
内核镜像生成的时候需要PHYS_OFFSET和PAGE_OFFSET对应的物理地址,在Makefile.boot中指定。对于SMDK2440开发板,则为arch/arm/mach-s3c24xx/Makefile.boot,如下,zreladdr-y即为__pa(PAGE_OFFSET+TEXT_OFFSET)(TEXT_OFSSET在makefile中写死了为0x8000)。params_phys-y即为PHYS_OFFSET
ifeq ($(CONFIG_PM_H1940),y)
zreladdr-y += 0x30108000
params_phys-y := 0x30100100
else
zreladdr-y += 0x30008000
params_phys-y := 0x30000100
endif
Linux内核中内存初始化主要是初始化内存的地址范围和布局,以及建立页表(MMU),不会再初始化内存控制器。内存控制器在u-boot中已经初始化好并且已经把代码拷贝到内存运行,初始化内存控制器也可能导致内存中的数据异常。uboot中可以通过两种方式传递内存信息:在启动参数中使用mem=xxx或者使用atag_mem。
对于mem=xxx参数,在early_param中初始化memblock,这里实际上添加了一个起始地址为PHYS_OFFSET,大小为mem=指定的内存bank。
static int __init early_mem(char *p)
{
static int usermem __initdata = 0;
u64 size;
u64 start;
char *endp;
/*
* If the user specifies memory size, we
* blow away any automatically generated
* size.
*/
if (usermem == 0) {
usermem = 1;
memblock_remove(memblock_start_of_DRAM(),
memblock_end_of_DRAM() - memblock_start_of_DRAM());
}
start = PHYS_OFFSET;
size = memparse(p, &endp);
if (*endp == '@')
start = memparse(endp + 1, NULL);
arm_add_memory(start, size);
return 0;
}