六、s3c2440内核启动过程:
n 内核映象自引导过程
n 典型的内核映象是zImage,它是经过压缩的、具备自引导能力的linux内核映象;
n zImage映像的入口代码是自引导程序;也就是说,当CPU跳转到zImage的时候,第一个执行的是自引导程序;
n 自引导程序的主要责任是解压zImage中的vmlinux,并且引导vmlinux;
n 自引导程序的入口在 arch/$(ARCH)/boot/compressed/head.S文件中;
n CPU跳转到vmlinux的入口地址时;
n 顺序执行内核启动程序,其中包括;
n 初始化内核各个子系统;
n 挂载根文件系统;
n 初始化驱动程序;
n 启动用户空间的init进程等。
n Linux内核在挂载根文件系统之后,要执行文件系统中的应用程序;
n 内核首先检查在命令行参数中有没有指定内核执行的第一个应用程序;
n 如果没有指定,则顺序执行/sbin/init, /etc/init, /bin/init, /bin/sh;
在BootLoader完成系统的引导并将Linux内核调入内存之后,调用bootLinux(),这个函数将跳转到kernel的起始位置。
如果kernel没有压缩,就可以启动了。
如果kernel压缩过,则要进行解压,在压缩过的kernel头部有解压程序。
压缩过得kernel入口第一个文件源码位置在arch/arm/boot/compressed/head.S。
它将调用函数decompress_kernel(),这个函数在文件arch/arm/boot/compressed/misc.c中,decompress_kernel()又调用proc_decomp_setup(),arch_decomp_setup()进行设置,然后使用在打印出信息“Uncompressing Linux...”后,调用gunzip()。将内核放于指定的位置。
1、 Start:->2、decompress_kernel->3、call_kernel->
/arch/arm/boot/compress/head.s/(其中decompress_kerne 是在/arch/arm/boot/compress /misc.c 实现)
----------------------------
start: // 1
----------------------------
wont_overwrite: mov r0, r4
mov r3, r7
bl decompress_kernel // 2 调用解压内核映像的C函数
// 在 arch/arm/boot/compressed/misc.c 实现
b call_kernel // 3 调用跳转到内核映像入口的子程序
----------------------------
call_kernel: bl cache_clean_flush // 3
bl cache_off
mov r0, #0
mov r1, r7 @ restore architecture number
mov pc, r4 @ call kernel // 5 调用内核
----------------------------
* r4 = kernel execution address // 6 内核执行的地址
----------------------------
arch/arm/boot/compressed/misc.c
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p, // 2
int arch_id)
{
arch_decomp_setup(); /*解压前的初始化*/
makecrc(); /*CRC 校验*/
putstr("Uncompressing Linux...");
gunzip(); /*调用解压函数 */
putstr(" done, booting the kernel.\n");
return output_ptr;
}
----------------------------
现在总结一下在进入解压后的内核入口前都做了些什么:
(1) 保存从uboot中传入的参数
(2) 执行一段处理器相关的代码
(3)接着会判断一下要不要重定位,我们这里是不需要重定位,所以开始对bss段清零。
(4)之后初始化页表,进行1:1映射。因为打开cache前必须打开mmu,所以这里先对页表进行初始化,然后打开mmu和cache。
(5)这些都准备好后,判断一下解压内核是否会覆盖未解压的内核映像。如果会,则进行一些调整,然后开始解压内核;如果不会,则直接解压。最后是刷新cache,关闭mmu和dcache,使 cache和tlb内容无效,跳到解压后的内核入口执行arm相关的内核代码。
4、Stext->5、b start_kernel->6、setup_arch()->7、rest_init()->8、init()->9、do_basic_setup()->10、prepare_namespace()->11、execve()
/arch/arm/kernel/head.s
------------------------------
ENTRY(stext) /* 4 内核入口点 */
-------------------------------
b start_kernel /* 5 由此跳到C 代码 */
-------------------------------
/init/main.c
asmlinkage void __init start_kernel(void) /* 5 C 代码的入口 */
{......
setup_arch(&command_line); /* 6 激活处理器 */
............
rest_init(); /* 7 调用此函数,启动 init 线程*/
}
----------------------------------
static void noinline rest_init(void)
{
kernel_thread(init, NULL, CLONE_FS | CLONE_SIGHAND); /*8 启动init 线程 */
…..
}
-----------------------------------
static int init(void * unused)
{………….
populate_rootfs(); /*在调用initcalls之前安装文件系统*/
……………..
do_basic_setup(); /* 9 初始化设备驱动*/
……………….
prepare_namespace(); /* 10 挂载根文件系统*/
………………….
run_init_process(execute_command); /* 启动用户空间的init 进程*/
/*init 进程是通过执行根文件系统中的init程序启动的*/
/*如果没有指定,则顺序执行/sbin/init, /etc/init, /bin/init, /bin/sh; */
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.");
}
init的进程号是1,从这一点就能看出,init进程是系统所有进程的起点,Linux在完成核内引导以后,就开始运行init程序。init程序需要读取配置文件/etc/inittab。
--------------------------------
static void run_init_process(char *init_filename) //内核首先检查在命令行参数中有没有指定内核执行的第一个应用程序;
{
argv_init[0] = init_filename;
execve(init_filename, argv_init, envp_init); /* 11 */
}
-----------------------------------
Long execve(…..)
{
…………
Ret =do_ execve(…………..) /* do_ execve 是执行用户空间的程序 */
}
七 、 系统环境变量的设置方法
1、配置路径
#echo $PATH
# vi ~/.bashrc
在其内添加:
export PATH=/usr/local/arm/3.4.1/bin:$PATH
#reboot
2、在编译的时候配置
#export PATH=/usr/local/arm/3.4.1/bin:$PATH
注意:
如果修改了 .bashrc路径,需要重新启动linux
3、编译之前显示编译的版本信息
#arm-linux-gcc -v
八、实验步奏
1、安装3.4.1 工作链
2、配置路径
3、解压内核原码
#mkdir /root/build_kernel
#mkdir /root/build_kernel/linux (路经无关)
#cd /root/build_kernel/linux
拷贝gec2440-linux-2.6.12.tar.bz2
#tar -jxvf gec2440-linux-2.6.12.tar.bz2
4、查看makefile
# vi makefile
查看选项:
ARCH :=arm //即打开arm特有的代码
CROSS_COMPILE :=/usr/local/arm/3.4.1/bin/arm-linux-
如果不是,则改过来。
5、使用默认的配置文件
#cp /arch/arm/configs/smdk2410_defconfig .config
(.config 会自动转换成 include/linux/autoconf.h 头文件。在 include/linux/config.h 文件中,将包含使用 include/linux/autoconfig.h 头文件.
另外,.config文件也会在下面的配置菜单中导进来。
)
(每种参考板都有省缺内核配置文件,在/arch/$(ARCH)/configs 目录下。Smdk2410的省缺文件就是/arch/arm/configs/smdk2410_defconfig )
或者执行如下命令使省缺配置生效:
#make smdk2410_defconfig
6、打开配置菜单,重新调整配置菜单
#make menuconfig (此时很多警告!)
SYSTEM TYPE ---->
(0) S3C2410 UART to use for low-level messages
(ctrl + 数据)
7、编译:
#make
编译结束,将生成 /vmlinux及 arch/arm/boot/zImage 等。
五、下载内核
#tftp 30008000 zImage
#nand erase 40000 1c0000
#nand write 30008000 40000 1c0000
#setenv bootcmd nand read 30008000 40000 1c0000\;go 30008000
#saveenv
补充:Makefile 中变量的赋值
1、 “=” 直接赋值
2、 “:=” 前面的变量不能使用后面的变量。
例如: y:=$(x)bar
X:=foo
则 y=bar 而不是 y=foobar
3、 “?=” 例如: foo ?= bar
如果foo 没被定义过,则 foo = bar
否则 用之前定义过的值
4、“+=” 将右边的变量值附加给左边的变量
例如 foo = string1
Foo+=string2
则 foo = string1string2