最近看了一下linux中有关ARM的体系结构有关的内容。在针对每一开发板时都会有这样一个宏,以2410例。其实是一个结构体,该结构体与体系结构上的移植关系非常密切。
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch to SMDK2410 */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = smdk2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = smdk_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
MACHINE_START定义在include/asm-arm/mach/arch.h中
#define MACHINE_START(_type,_name) / static const struct machine_desc __mach_desc_##_type / __attribute_used__ / __attribute__((__section__(".arch.info.init"))) = { / .nr = MACH_TYPE_##_type, / .name = _name, #define MACHINE_END / }; |
将前面定义的MACHINE_START展开后得到,
static const struct machine_desc __mach_desc_SMDK2410
__attribute_used__
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_SMDK2410, /* architecture number */
.name = "SMDK2410", /* architecture name */
/* Maintainer: Jonas Dietsche */
.phys_io = S3C2410_PA_UART, /* start of physical io */
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100, /* tagged list */
.map_io = smdk2410_map_io, /* IO mapping function */
.init_irq = s3c24xx_init_irq,
.init_machine = smdk_machine_init,
.timer = &s3c24xx_timer,
}
其中MACH_TYPE_SMDK2410为机器号,该值将与我们在bootloader中传递给linux入口参数的机器号比较,如果在引导过程中出现无法识别机器号,这是问题就应该出在这里了。
MACH_TYPE_SMDK2410定义在arch/include/asm-arm/mach-types.h内,值为193.
/* arch/include/asm-arm/mach-types.h */
#define MACH_TYPE_SMDK2410 193
注意:mach-types.h在未编译之前时不存在的,在头文件由arch/arm/tool/mach-types里面定义的数据结合脚本在编译时生成的。
/* arch/arm/tool/mach-types */
smdk2410 ARCH_SMDK2410 SMDK2410 193
其它结构:
phys_io:IO空间的开始地址
io_pg_offst:IO对于的虚拟地址
boot_params:为我们bootloader产生的TAG列表的开始地址,这两者要一致。
map_io:实现对IO空间的映射
init_irq:对中断进行初始化
由上发现,MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
各个成员函数在不同时期被调用:
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用
init_irq这个成员在系统初始化的时候会被赋值给init_arch_irq全局变量,如下:
/* arch/arm/kernel/setup.c */ void __init setup_arch(char **cmdline_p) { …… cpu_init(); /* * Set up various architecture-specific pointers */ init_arch_irq = mdesc->init_irq; system_timer = mdesc->timer; init_machine = mdesc->init_machine; …… } |
注: 可以看到这里不仅初始化了init_arch_irq 全局变量,同时初始化了system_timer,init_machine等全局变量。这是kernel支持多平台的一种机制。当然这里 system_timer和init_machine我不多描述,有兴趣的可以大家自己去看。机制和init_arch_irq大同小异。
init_arch_irq函数指针定义在体系架构无关的arch/arm/kernel/irq.c内
/* arch/arm/kernel/irq.c */
void (*init_arch_irq)(void) __initdata = NULL;
并且在init_IRQ函数内会去执行它。
/* arch/arm/kernel/irq.c */ void __init init_IRQ(void) { int irq; for (irq = 0; irq < NR_IRQS; irq++) irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_DELAYED_DISABLE | IRQ_NOPROBE; #ifdef CONFIG_SMP bad_irq_desc.affinity = CPU_MASK_ALL; bad_irq_desc.cpu = smp_processor_id(); #endif init_arch_irq(); } |
那init_IRQ在哪里被调用呢? 我们猜想肯定是在系统初始化的时会调用它。
实际结果也正是,init_IRQ会在init/main.c里的start_kernel函数内被调用:
asmlinkage void __init start_kernel(void)
{
……
trap_init();
rcu_init();
init_IRQ();
pidhash_init();
clockevents_init();
init_timers();
……
}
这样,我们定义的新平台irq初始化函数就会在系统启动时被调用,对我们的硬件中断进行初始化后再去使用它。
3. map_io 在 setup_arch() --> paging_init() --> devicemaps_init()被调用
其他主要都在 setup_arch() 中用到。