注:分析的三星官方的内核。
一、板子启动相关
1.首先得明白一般与板子相关的启动初始化都在mach-xxx.c文件。因为静态映射表我们主要关注的是板子的GPIO,而GPIO也属于板子启动必须初始化的部分,所以我们要找的文件就是/kernel/arch/arm/mach-s5pv210/mach-smdkc110.c文件。
2.熟悉内核的人都知道板子的启动相关初始化都在一个结构体当中。
(1)相关的代码片段
#ifdef CONFIG_MACH_SMDKC110
MACHINE_START(SMDKC110, "SMDKC110")
#elif CONFIG_MACH_SMDKV210
MACHINE_START(SMDKV210, "SMDKV210")
#endif
/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
//.fixup = smdkv210_fixup,
.init_irq = s5pv210_init_irq,
.map_io = smdkc110_map_io,
.init_machine = smdkc110_machine_init,
.timer = &s5p_systimer,
MACHINE_END
(2)MACHINE_START和MACHINE_END是一个宏定义,主要是用来定一个结构体变量的,并且填充其内部一部分变量
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
//__attribute__是给这个结构体设置一个段属性,当内核链接时会根据段的顺序(具体的段顺序在链接脚本里面指定)。来链接生成zImage。
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END };
(3)因为我们分析的S5PV210,所以我们肯定在内核中定义了CONFIG_MACH_SMDKV210,所以上面的宏,展开后就是
static const struct machine_desc __mach_desc_SMDKV210 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_SMDKV210, \
.name = "SMDKV210",
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
//.fixup = smdkv210_fixup,
.init_irq = s5pv210_init_irq,
.map_io = smdkc110_map_io,
.init_machine = smdkc110_machine_init,
.timer = &s5p_systimer,
};
3.(聪明点的人一眼就能看出)smdkc110_map_io函数就是静态映射表的构建函数。因为名字很显眼嘛。好进入函数,下面有一个叫s5p_init_io(NULL, 0, S5P_VA_CHIPID)的函数。继续追,关键的代码就在这个函数里面了。
二、s5p_init_io函数分析
1.s5p_iodesc分析
(1)map_desc结构体在/kernel/arch/arm/include/asm/mach/map.h里面定义
struct map_desc {
//虚拟地址
unsigned long virtual;
//物理地址
unsigned long pfn;
unsigned long length;
unsigned int type;
};
(2)可见定义了一个map_desc类型的数组,里面包含了对应模块的映射信息,每一个元素代表一个映射关系(虚拟地址到物理地址)。
static struct map_desc s5p_iodesc[] __initdata = {
{
.virtual = (unsigned long)S5P_VA_CHIPID,
.pfn = __phys_to_pfn(S5P_PA_CHIPID),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_SYS,
.pfn = __phys_to_pfn(S5P_PA_SYSCON),
.length = SZ_64K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_UART,
.pfn = __phys_to_pfn(S3C_PA_UART),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC0,
.pfn = __phys_to_pfn(S5P_PA_VIC0),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC1,
.pfn = __phys_to_pfn(S5P_PA_VIC1),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_TIMER,
.pfn = __phys_to_pfn(S5P_PA_TIMER),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S5P_VA_GPIO,
.pfn = __phys_to_pfn(S5P_PA_GPIO),
.length = SZ_4K,
.type = MT_DEVICE,
},
};
(3)而这个结构体被iotable_init函数所使用,里面实际调用了create_mapping函数。
void __init iotable_init(struct map_desc *io_desc, int nr)
{
int i;
for (i = 0; i < nr; i++)
create_mapping(io_desc + i);
}
(4)create_mapping函数负责将这个结构体转换成MMU能够识别的页表映射关系,这样开机之后就能直接使用虚拟地址来访问对应的物理地址。
2.而s5p_iodesc结构里面的S5P_VA_CHIPID、S3C_VA_SYS类似的这些宏定义是三星出场针对不同的CPU来定义的。
(1)主映射表位于:arch/arm/plat-s5p/include/plat/map-s5p.h
(2)虚拟地址基地址定义在:arch/arm/plat-samsung/include/plat/map-base.h
#define S3C_ADDR_BASE (0xFD000000)
三、创建好映射表之后
1.在内核启动的时候这个表就会被建立。
2.建立的过程函数,就是一条线路。自己分析。(每一个TAB键就是一个函数层次关系)
start_kernel
setup_arch
paging_init
devicemaps_init
if (mdesc->map_io)
mdesc->map_io();
能力有限,我也是学习者,只能分析到这个程度,大概了解。。。。