ARM-Linux内核移植之(一)——内核启动流程分析
K-Style
转载请注明来自于衡阳师范学院08电2 K-Style http://blog.csdn.net/ayangke,QQ:843308498 邮箱:yangkeemail@qq.com
内核版本:2.6.22 为什么要采用这样一个较低的版本进行移植了,因为韦东山大牛说了,低版本的才能学到东西,越是高版本需要移植时做的工作量越少,学的东西越少。
内核启动分为三个阶段,第一是运行head.S文件和head-common.S,第三个阶段是允许第二是运行main.c文件
对于ARM的处理器,内核第一个启动的文件是arc/arm/kernel下面的head.S文件。当然arc/arm/boot/compress下面也有这个文件,这个文件和上面的文件略有不同,当要生成压缩的内核时zImage时,启动的是后者,后者与前者不同的时,它前面的代码是做自解压的,后面的代码都相同。我们这里这分析arc/arm/kernel下面的head.S文件。当head.S所作的工作完成后它会跳到init/目录下跌的main.c的start_kernel函数开始执行。
第一阶段:
首先截取部分head.S文件
ENTRY(stext)
msr cpsr_c,#PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
@ andirqs disabled
mrc p15,0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
movs r10,r5 @ invalidprocessor (r5=0)?
beq __error_p @ yes, error 'p'
bl __lookup_machine_type @ r5=machinfo
movs r8,r5 @ invalidmachine (r5=0)?
beq __error_a @ yes, error 'a'
bl __create_page_tables
/*
*The following calls CPU specific code in a position independent
*manner. See arch/arm/mm/proc-*.S fordetails. r10 = base of
*xxx_proc_info structure selected by __lookup_machine_type
*above. On return, the CPU will be readyfor the MMU to be
*turned on, and r0 will hold the CPU control register value.
*/
ldr r13,__switch_data @ address to jump toafter
@ mmuhas been enabled
adr lr,__enable_mmu @ return (PIC)address
第一步,执行的是__lookup_processor_type,这个函数是检查处理器型号,它读取你的电路板的CPU型号与内核支持的处理器进行比较看是否能够处理。这个我们不关心它的具体实现过程,因为现在主流处理器内核都提供了支持。
第二步,执行的是__lookup_machine_type,这个函数是来检查机器型号的,它会读取你bootloader传进来的机器ID和他能够处理的机器ID进行比较看是否能够处理。内核的ID号定义在arc/arm/tool/mach_types文件中MACH_TYPE_xxxx宏定义。内核究竟就如何检查是否是它支持的机器的呢?实际上每个机器都会在/arc/arm/mach-xxxx/smdk-xxxx.c文件中有个描述特定机器的数据结构,如下
MACHINE_START(S3C2440,"SMDK2440")
/* Maintainer: Ben Dooks<ben@fluff.org> */
.phys_io =S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq =s3c24xx_init_irq,
.map_io =smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer =&s3c24xx_timer,
MACHINE_END
MACHINE_START和 MACHINE_END实际上被展开成一个结构体
#defineMACHINE_START(_type,_name) \
staticconst struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init")))= { \
.nr =MACH_TYPE_##_type, \
.name =_name,
#defineMACHINE_END \
};
于是上面的数据结构就被展开为
staticconst struct machine_desc __mach_desc_S3C2440 \
__used \
__attribute__((__section__(".arch.info.init")))= { \
.nr =MACH_TYPE_S3C2440, \
.name =”SMDK2440”,};
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq =s3c24xx_init_irq,
.map_io =smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer =&s3c24xx_timer,
}
每个机器都会有一个machine_desc__mach_desc结构,内核通过检查每个machine_desc__mach_desc的nr号和bootloader传上来的ID进行比较,如果相同,内核就认为支持该机器,而且内核在后面的工作中会调用该机器的machine_desc__mach_desc_结构中的方法进行一些初始化工作。
第三步,创建一级页表。
第四步,在R13中保存__switch_data 这个函数的地址,在第四步使能mmu完成后会跳到该函数执行。
第五步,执行的是__enable_mmu,它是使能MMU,这个函数调用了__turn_mmu_on函数,让后在_turn_mmu_on在最后将第三步赋给R13的值传给了PC指针 (mov pc, r13),于是内核开始跳到__switch_data这个函数开始执行。
我们再来看arch/arm/kenel/head-common.S这个文件中的__switch_data函数
__switch_data:
.long __mmap_switched
.long __data_loc @ r4
.long __data_start @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long cr_alignment @ r6
.long init_thread_union+ THREAD_START_SP @ sp
/*
* The following fragment of code is executedwith the MMU on in MMU mode,
* and uses absolute addresses; this is notposition independent.
*
* r0 =cp#15 control register
* r1 = machine ID
* r9 = processor ID
*/
.type __mmap_switched,%function
__mmap_switched:
adr r3,__switch_data + 4
ldmia r3!,{r4, r5, r6, r7}
cmp r4,r5 @ Copy datasegment if needed
1: cmpne r5,r6
ldrne fp,[r4], #4
strne fp,[r5], #4
bne 1b
mov fp,#0 @ Clear BSS(and zero fp)
1: cmp r6,r7
strcc fp,[r6],#4
bcc 1b
ldmia r3,{r4, r5, r6, sp}
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
bic r4,r0, #CR_A @ Clear 'A' bit
stmia r6,{r0, r4} @ Save controlregister values
b start_kernel
这个函数做的工作是,复制数据段清楚BBS段,设置堆在指针,然后保存处理器内核和机器内核等工作,最后跳到start_kernel函数。于是内核开始执行第二阶段。
第二阶段:
我们再来看init/目录下的main.c的start_kernel函数,这里我只截图了部分。
asmlinkage void __init start_kernel(void)
{
…………………….
……………………..
printk(KERN_NOTICE);
printk(linux_banner);
setup_arch(&command_line);
setup_command_line(command_line);
parse_early_param();
parse_args("Booting kernel",static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption);
……………………
…………………………
init_IRQ();
pidhash_init();
init_timers();
hrtimers_init();
softirq_init();
timekeeping_init();
time_init();
profile_init();
…………………………
……………………………
console_init();
………………………………
………………………………
rest_init();
}
从上面可以看出start_kernel首先是打印内核信息,然后对bootloader传进来的一些参数进行处理,再接着执行各种各样的初始化,在这其中会初始化控制台。最后会调用rest_init();
我们再来看rest_init()函数
static void noinline __init_refok rest_init(void)
__releases(kernel_lock)
{
int pid;
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
............
}
他启动了kernel_init这个函数,再来看kerne_init函数
static int __init kernel_init(void * unused)
{
..............................
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
}
/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
init_post();
return 0;
}
kernel_init先调用了prepare_namespace();然后调用了init_post函数
void __init prepare_namespace(void)
{
..........................
mount_root();
.....................
}
可以看出prepare_namespace调用了mount_root挂接根文件系统。接着kernel_init再执行init_post
static int noinline init_post(void)
{
.......................................
/*打开dev/console控制台,并设置为标准输入、输出*/
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
//如果bootloader指定了init参数,则启动init参数指定的进程
if (execute_command) {
run_init_process(execute_command);
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
//如果没有指定init参数,则分别带sbin、etc、bin目录下启动init进程
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
注意上面的run_init_process的会等待init进程返回才往后面执行,所有它一旦找到一个init可执行的文件它将一去不复返。
综上,内核启动的过程大致为以下几步:
1.检查CPU和机器类型
2.进行堆栈、MMU等其他程序运行关键的东西进行初始化
3.打印内核信息
4.执行各种模块的初始化
5.挂接根文件系统
6.启动第一个init进程
分享到:
相关推荐
本文档详细描述了交叉编译链的安装过程,以及在BeagleBone平台(带有LCD)移植Xenomai实时内核、移植IGH-EtherCAT、移植LinuxCNC的详细过程,以及可能遇到的具体问题,并且对问题作出了解决 备注:这里移植的Linux...
6.3 Linux-2.4内核向ARM平台的移植 6.3.1 根目录 6.3.2 arch目录 6.3.3 arch/arm/boot目录 6.3.4 arch/arm/def-configs目录 6.3.5 arch/arm/kernel目录 6.3.6 arch/arm/mm目录 6.3.7 arch/arm/mach-s3c2410...
·嵌入式Linux之我行——内核访问外设I/O资源的方式 ·嵌入式Linux之我行——深入理解DM9000在mini2440上的驱动 ·嵌入式Linux之我行——LCD背光驱动在2440上的实例开发 ·嵌入式Linux之我行——LED驱动在2440上的...
分析Linux内核的代码结构以及启动过程,并介绍如何移植到开发板上。 介绍嵌入式Linux文件系统的目录结构,然后构造嵌入式Linux文件系统。 嵌入式Linux驱动程序开发和移植。 嵌入式系统中的GUI介绍。 ...
华清远见嵌入式培训专用教材 内容涵盖嵌入式系统概述.arm处理器介绍.linux编程环境.交叉开发环境.交叉开发环境工具链.bootloader.配置编译内核.内核移植浅析.内核调试技术.制作linux根文件系统.充分利用开源软件....
第8章 嵌入式linux c语言基础——arm linux内核常见数据结构 225 8.1 链表 226 8.1.1 链表概述 226 8.1.2 单向链表 226 8.1.3 双向链表 233 8.1.4 循环链表 234 8.1.5 arm linux中链表使用实例 ...
1.1 嵌入式Linux基础 1.2 Linux安装 1.3 Linux文件及文件系统 1.4 实验内容——安装Linux操作系统 本章小结 思考与练习 第2章 Linux基础命令 2.1 Linux常用操作命令 2.2 Linux启动过程详解 ...
嵌入式系统开发基础——基于ARM微处理器和Linux操作系统[滕英岩][习题解答] 目录第1章 嵌入式系统基础知识 1.1 嵌入式系统的特点及分类 1.1.1 嵌入式系统的特点 1.1.2 嵌入式系统的分类 1.2 嵌入式系统的软硬件...
59——通过Make与Makefile对Linux进行移植与裁剪(基于ARM9内核的2410开发板)
5.3 实验内容——移植Linux内核 本章小结 思考与练习 第6章 文件I/O编程 6.1 Linux系统调用及用户编程接口(API) 6.2 Linux中文件及文件描述符概述 6.3 不带缓存的文件I/O操作 6.4...
第1章 Linux开发基础.1 1.1 Linux系统概述1 1.1.1 Linux简介1 1.1.2 Linux系统的特点1 1.1.3 Linux系统的组成2 1.2 Linux系统的使用2 实验1.1 熟悉Linux基本命令与文件目录系统2 1.3 全屏幕编辑器与vi...
第1章 Linux开发基础.1 1.1 Linux系统概述1 1.1.1 Linux简介1 1.1.2 Linux系统的特点1 1.1.3 Linux系统的组成2 1.2 Linux系统的使用2 实验1.1 熟悉Linux基本命令与文件目录系统2 1.3 全屏幕编辑器与vi6 ...
我第一次接触Linux在1994年下半年当我拜访Jim Paradis时,当时他正在致力于将Linux移植到Alpha AXP处理器系统上。从1984年开始,我曾经在DEC公司任职,主要工作是网络与通讯。1992年我开始为新成立的Digital ...
║2 嵌入式系统开发技术详解——基于ARM 3.1 Linux 常用工具.............................................................................................................. 28 3.1.1 Shell简介..................
2.2 Linux启动过程详解 2.2.1 概述 2.2.2 内核引导阶段 2.2.3 init阶段 2.3 Linux系统服务 2.3.1 独立运行的服务 2.3.2 xinetd设定的服务 2.3.3 系统服务的其他相关命令 2.4 实验内容 2.4.1 在Linux下解压常见软件 ...
第1章 Linux开发基础.1 1.1 Linux系统概述1 1.1.1 Linux简介1 1.1.2 Linux系统的特点1 1.1.3 Linux系统的组成2 1.2 Linux系统的使用2 实验1.1 熟悉Linux基本命令与文件目录系统2 1.3 全屏幕编辑器与vi6 ...
第1章 Linux开发基础.1 1.1 Linux系统概述1 1.1.1 Linux简介1 1.1.2 Linux系统的特点1 1.1.3 Linux系统的组成2 1.2 Linux系统的使用2 实验1.1 熟悉Linux基本命令与文件目录系统2 1.3 全屏幕编辑器与vi6 ...
5.3 实验内容——移植Linux内核 164 本章小结 165 思考与练习 165 第6章 文件I/O编程 166 6.1 Linux系统调用及用户编程接口(API) 166 6.1.1 系统调用 166 6.1.2 用户编程接口(API) 167 6.1.3 ...