以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
内核支持什么架构、支持哪款cpu,这是如何确定的?主要是通过机器码来确定的。
内核中定义了一份机器码,uboot也会给内核传递一个机器码。
在内核启动的汇编阶段,head.S文件对比uboot传给内核的机器码和内核定义的机器码,如果匹配则继续启动。
本文主要讲内核如何定义机器码的(以下内容说的是三星待移植版本内核)。
1、struct machine_desc 结构体
这个结构体定义在/arch/arm/include/asm/mach/arch.h文件中,它的内容如下:
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
这个结构体的一个实例,就是一块开发板的软件抽象。
2、mach-xxx/mach-yyy.c文件
在内核源码/arch/arm/目录中有许多mach-xxx目录(不同的xxx表示不同的CPU),这些mach-xxx目录中又有许多mach-yyy.c文件(表示采用相同CPU的不同开发板,而且开发板名字就叫yyy)。
分析发现,每个mach-yyy.c文件中,都通过MACHINE_START宏,定义了一个表征某个开发板的struct machine_desc类型的变量。而且这些结构体变量都被放到.arch.info.init段,表示当前内核可以支持这些机器码对应的开发板。
2.1 宏 MACHINE_START 的简介
这个宏定义在/arch/arm/include/asm/mach/arch.h文件中,它的内容如下:
/*
* Set of macros to define architecture features. This is built into
* a table by the linker.
*/
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
这个宏用来定义一个数据类型为 struct machine_desc 的变量(实际上只定义了结构体前面部分的内容),而且这个变量会被定义到一个特定段.arch.info.init,因此这个变量将来会被链接器链接到这个.arch.info.init段中。
2.1.1 举例说明
比如在/arch/arm/mach-s5pv210/mach-smdkv210.c文件中,有代码如下:
MACHINE_START(SMDKV210, "SMDKV210")
/* 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,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
#ifdef CONFIG_S5P_HIGH_RES_TIMERS
.timer = &s5p_systimer,
#else
.timer = &s3c24xx_timer,
#endif
MACHINE_END
这段代码其实等价于下面这段代码:
static const struct machine_desc __mach_desc_SMDKV210 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_SMDKV210,\//机器码2456
.name = "SMDKV210",
//MACHINE_START(SMDKV210, "SMDKV210")展开后是上面这部分(没有“}”这个符号)
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
.timer = &s5p_systimer,
};
即这段代码定义了一个 struct machine_desc 类型的变量__mach_desc_SMDKV210,它是SMDKV210这个开发板的软件抽象。
(1).nr=MACH_TYPE_SMDKV210
这个表示这个开发板的机器码是MACH_TYPE_SMDKV210。
机器码值“MACH_TYPE_SMDKV210”在/include/generated/mach-types.h文件中定义。
该文件在内核配置过程中自动生成,维护着该版本内核所支持的全部机器码。
(2).name= "SMDKV210"
这个表示这个开发板的名字叫“SMDKV210”。
(3).init_machine = smdkv210_machine_init
表示这个开发板的硬件初始化函数是 smdkv210_machine_init。
该函数包含了内核中各种硬件的初始化操作。
2.2 为 X210 选择 mach-yyy.c 文件
我们的目标开发板叫做X210,采用s5pv210这款CPU,按理应该对应着/arch/arm/mach-s5pv210/mach-x210.c文件。
但是在三星版本内核的/arch/arm/mach-s5pv210/中,找不到X210开发板对应的mach-x210.c文件。
为了减少移植工作量,我们在三星版本内核的/arch/arm/mach-s5pv210/中,寻找一个mach-yyy.c文件,使得该文件对应的开发板与X210开发板最为接近(都使用相同的CPU),并以此文件为基础进行移植。
九鼎的X210开发板是根据三星SMDKV210开发板进行设计的,因此要寻找SMDKV210开发板对应的文件,按理应该是三星版本内核的/arch/arm/mach-s5pv210/mach-smdkv210.c文件。
但是分析三星版本内核源码/arch/arm/mach-s5pv210/Makefile文件后得知,CONFIG_MACH_SMDKV210这个宏(在内核配置阶段生成的./config文件中)实际绑定的是/arch/arm/mach-s5pv210/mach-smdkc110.c这个文件,而非mach-smdkv210.c文件。
因此后续将以/arch/arm/mach-s5pv210/mach-smdkc110.c这个文件为基础进行移植。
2.3 分析mach-smdkc110.c文件
在该文件的最后有以下代码:
#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,
.init_irq = s5pv210_init_irq,
.map_io = smdkc110_map_io,
.init_machine = smdkc110_machine_init,
.timer = &s5p_systimer,
MACHINE_END
它展开后内容如下:
static const struct machine_desc __mach_desc_SMDKV210 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_SMDKV210,\//机器码2456
.name = "SMDKV210",
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,//初始化各种硬件
.timer = &s5p_systimer,
};
其中,init_machine这个函数指针,指向一个机器硬件初始化函数smdkv210_machine_init。
这个函数绑定了内核启动过程中会初始化的各种硬件的信息,在此函数中添加的硬件才会被初始化,没有在此函数中添加的硬件不会被初始化。
关于smdkv210_machine_init的更多介绍,见驱动栏目中的“Linux设备驱动模型”的内容。