标题:
Uboot -kerne-root 启动流程
内容:
※uboot启动流程
※Kernel启动流程
※Root启动流程
※构建根文件系统
/*********************************
*u-boot: u-boot2012.04.01
*kernel: linux-2.6.22
*busybox: busybox-1.1.6
*********************************/
1.初始化硬件
2.把内核从NAND读到SDRAM
3.设置内核启动参数
4.跳转执行内核
执行第一个代码:/cpu/arm920t/start.S
第一阶段(start.S)
reset:
set the cpu to SVC32 mode //管理模式
turn off the watchdog //关看门狗
mask all IRQs //屏蔽中断
cpu_init_crit
flush v4 I/D caches //关闭caches
disable MMU stuff and caches //关闭MMU
lowlevel_init //配置SDRAM
Relocate //重定位
stack_setup: //设置堆栈
clear_bss: //清楚bss段
start_armboot //跳转执行第二阶段
第二阶段(board.c)
start_armboot (void)
init_sequence
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
flash_init 初始化NOR
nand_init() 初始化NAND
env_relocate () 将环境变量读入指定位置
cs8900_get_enetaddr 初始化网络设备
main_loop () 死循环
s = getenv ("bootdelay")
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
s = getenv ("bootcmd");
Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0 //环境变量
run_command (s, 0);
continue;
Uboot命令的实现
①串口输入命令(字符串)
②动作(函数),命令对应于名字
因而根据命令找到对应函数,来执行。Uboot里面有一个结构体
包含1.名
2.函数
struct cmd_tbl_s {
char *name; /* Command Name */
int maxargs; /* maximum number of arguments */
int repeatable; /* autorepeat allowed? */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage; /* Usage message (short) */
#ifdef CFG_LONGHELP
char *help; /* Help message (long) */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
此时run_command函数根据名字匹配该结构体
Run_command
命令的提取
while (*str) {
/*
* Find separator, or string end
* Allow simple escape of ';' by writing "\;"
*/
for (inquotes = 0, sep = str; *sep; sep++) {
if ((*sep=='\'') &&
(*(sep-1) != '\\'))
inquotes=!inquotes;
if (!inquotes &&
(*sep == ';') && /* separator */
( sep != str) && /* past string start */
(*(sep-1) != '\\')) /* and NOT escaped */
break;
}
parse_line() //解析命令
Cmdtp=find_cmd() //匹配命令 cmdtp就是指向上面的结构体cmd_tbl_s
for (cmdtp = &__u_boot_cmd_start;
cmdtp != &__u_boot_cmd_end;
cmdtp++)
以上:
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
比如启动命令:
Bootcmd=nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0以bootm 0x30007fc0为例
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
);
U_boot_cmd的定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
以上可以展开为;
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd")))
= {bootm, CFG_MAXARGS, 1, do_bootm, "bootm - boot application image from memory\n", help}
以上解释为:bootm命令定义了__u_boot_cmd_bootm结构体,该结构体类型是cmd_tbl_t,并且强制为.u_boot_cmd段属性
Uboot启动内核
内核启动依赖nand read.jffs2 0x30007fc0 kernel;bootm 0x30007fc0
nand read.jffs2 0x30007fc0 kernel把内核读到0x30007fc0
从哪里读? --kernel分区在配置文件里面写死了smdk2410.h
读到哪里? --0x30007fc0
Nand命令分析:
"nand read[.jffs2] - addr off|partition size\n"
bootm 0x30007fc0从bootm 0x30007fc0启动它
Bootm命令分析:
do_bootm
image_header_t *hdr = &header;
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address */
uint32_t ih_ep; /* Entry Point Address */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
//如果当前地址不是入口地址,移到加载地址0x30008000,由于头部占用64字节,所以此时地址是0x30007fc0
do_bootm_linux (cmdtp, flag, argc, argv,
addr, len_ptr, verify); //启动内核
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
setup_end_tag (bd);
printf ("\nStarting kernel ...\n\n");
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
处理uboot传入的参数
①判断是否支持这个CPU
②判断是否支持这个单板
③建立页表
④使能MMU
⑤跳到start_kernel函数
内核执行的第一个代码文件:/arch/arm/kernel/head.S 和 /arch/arm/boot/bootp/init.S
Start_kernel分析
1.输出内核版本信息
2.Setup_arch($command_line)
3.Setup_command_line(command_line)
调用关系:
Start_kernel
printk(linux_banner); //输出版本信息
setup_arch(&command_line); //匹配ID,arch-type
setup_processor();
list = lookup_processor_type(processor_id);
mdesc = setup_machine(machine_arch_type);
parse_cmdline(cmdline_p, from); //解析命令
setup_command_line(command_line);
strcpy (saved_command_line, boot_command_line);
Rest_init
Kernel_thread() //线程
// kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
kernel_init()
Prepare_namespace()
Mount_root()
Init_post()
Run_init_process() //执行文件系统第一个程序
...
init过程
1.读取配置文件
2.解析配置文件
3.执行客户程序
配置文件:1.指定程序;2.何时执行
busybox调用过程
busybox -> init_main
parse_inittab
file = fopen(INITTAB, "r");//打开配置文件/etc/inittab
(没有配置文件时使用默认配置文件)
new_init_action()
#1.创建init_action结构,填充
#2.把这个结构放入init_action_list链表里面
run_actions(SYSINIT);
waitfor(a, 0); //执行程序,等待执行完毕
run(a); //系统调用,创建precess子进程
waitpid(runpid, &status, 0) //等待结束
delete_init_action(a);//在init_action_list里面删掉应用程序
run_actions(WAIT);
waitfor(a, 0);
delete_init_action(a);
run_actions(ONCE);
run(a);//不会等待子进程结束
delete_init_action(a);//在init_action_list里面删
while (1) {
run_actions(RESPAWN);
run(a)
run_actions(ASKFIRST);
run(a)
打印:"\nPlease press Enter to activate this console. ";
等待回车
创建子进程
wpid = wait(NULL);//等待子进程退出
while (wpid > 0) {
a->pid = 0;//退出后,设置pid=0
}
}
init_action_list里面有id,action,process等等
static void new_init_action(int action, const char *command, const char *cons)
struct init_action *new_action, *a, *last;
init_action里面有:
struct init_action *next;
int action;
pid_t pid; //进程号
char command[INIT_BUFFS_SIZE]; //应用程序
char terminal[CONSOLE_NAME_SIZE]; //终端
new_init_action做了什么
1.创建init_action结构,填充
2.把这个结构放入init_action_list链表里面
现在假设没有配置文件,根据
/* No inittab file -- set up some default behavior */
#endif
/* Reboot on Ctrl-Alt-Del */
new_init_action(CTRLALTDEL, "reboot", "");
/* Umount all filesystems on halt/reboot */
new_init_action(SHUTDOWN, "umount -a -r", "");
/* Swapoff on halt/reboot */
if (ENABLE_SWAPONOFF) new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
/* sysinit */
new_init_action(SYSINIT, INIT_SCRIPT, "");
来反推出默认配置项
#inittab格式:(文件在example/inittab文件)
#<id>:<runlevels>:<action>:<process>
#<id> :/dev/id,最终用作终端:stdin,stdout,stderrer:printf,scanf,err
#<runlevels> :忽略
#<action> :何时执行的
<action>: Valid actions include: sysinit, respawn, askfirst, wait, once,
restart, ctrlaltdel, and shutdown.
#<process> :应用程序或者脚本
new_init_action(ASKFIRST, -/bin/sh, /dev/tty2);
new_init_action(CTRLALTDEL, "reboot", "");
id=null
runlevels=null
action=ctrlaltdel
那么上面就是:
::ctrlaltdel:reboot
同理可知其他的配置项是:
::shutdown:umount -a -r
::restart:init
::askfirst:-/bin/sh
tty2:askfirst:-/bin/sh
tty3:askfirst:-/bin/sh
tty4:askfirst:-/bin/sh
::sysinit:/etc/init.d/rcS
其实:
main里面
signal(SIGHUP, exec_signal);
signal(SIGQUIT, exec_signal);
signal(SIGUSR1, shutdown_signal);
signal(SIGUSR2, shutdown_signal);
signal(SIGINT, ctrlaltdel_signal);
signal(SIGTERM, shutdown_signal);
signal(SIGCONT, cont_handler);
signal(SIGSTOP, stop_handler);
signal(SIGTSTP, stop_handler);
当用户按下 ctrl + alt +del时会产生一个信号,然后执行ctrlaltdel_signal函数
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
{
run_actions(CTRLALTDEL);
}
总结:应用程序所需要的文件
1./dev/console /dev/null
2.配置文件/etc/inittab
3.配置文件里面指定的应用程序
4.库文件
5.init本身,即busybox
以上就是最小根文件系统所需要的项
read INSTALL rirst.
总结:应用程序所需要的文件
1./dev/console /dev/null
2.配置文件/etc/inittab
3.配置文件里面指定的应用程序
4.库文件
5.init本身,即busybox
以上就是最小根文件系统所需要的项
编译busybox
先阅读install文件可知道如何编译它
make menuconfig
make
make install(这里安装要注意)
安装到我们指定的某个文件里面
使用make CONFIG_PREFIX=/path/from/root install
编译需要在makefile里面加上cross_compiler里面加上arm-linux-
一、创建console
现在上面编译后busybox目录是work/FL_fs/first_fs
在此目录下:ls /dev/console /dev/null -l
crw------- 1 root root 5, 1 2015-01-05 20:57 /dev/console
crw-rw-rw- 1 root root 1, 3 2015-01-05 20:30 /dev/null
那么根据它来创建console null等设备
#mkdir dev
#cd dev
#mknod console c 5 1
#mknod null c 1 3
#ls -l
显示:
crw-r--r-- 1 root root 5, 1 2015-05-06 20:39 console
crw-r--r-- 1 root root 1, 3 2015-05-06 20:40 null
表示创建成功
二、配置项
#cd work/FL_fs/first_fs
#mkdir etc
#vim etc/inittab
输入:
console::askfirst:-/bin/sh
三、安装c库
#cd lib
#cp /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib *.so* /work/FL_fs/first_fs/lib -d
end
最小根文件系统已经完成。
此时需要制作镜像文件以烧写到硬件。
yaffs2和jffs文件系统
1.制作yaffs2
#cd work/system
yaffs_source_util_large_smmall_page_nand.tar.bz2
#tar xjf yaffs_source_util_large_small_page_nand.tar.bz2
#cd Developement_util_ok
#cd yaffs2
#cd utils
#make
#cp mkyaffs2image /usr/local/bin
#chmod +x /usr/local/bin/mkyaffs2image
#cd /work/FL_fs
#mkyaffs2image first_fs first_fs.yaffs2
此后烧写到开发板里面。
可以启动,并能使用ls,cd等命令。
如何改进?
#cd ../first_fs
#mkdir proc
单板上有虚拟系统
#mkdir proc
#mount -t proc none /proc
#ps
可以看到哪些程序及其内容
PID TTY TIME CMD
383 pts/2 00:00:00 ps
30564 pts/2 00:00:00 su
30573 pts/2 00:00:00 bash
383 进程号
#cd 383
如果不想 手动挂载 #mount -t proc none /proc
也可以写到配置文件里面
#vim inittab
++ ::sysinit:/etc/init.d/rcS 需要脚本
#mkdir etc/init.d
#vim rcS
##mount -t proc none /proc
#chmod +x /etc/init.d/rcS
就可以了
上面#mount -t proc none /proc
也可以使用#mount -a
#mount -a是什么意思?
读出/etc/fstab内容来挂载
怎么使用呢?
#mount -a会依赖fstab文件
fstab文件内容有哪些呢?
#device mount-point type options dump fsck
proc /proc proc defaults 0 0
那么,在/etc/fstab里面输入:
proc /proc default 0 0
然后在脚本rcS里面使用#mount -a
然后再制作yaffs镜像文件就和第一种方法是一样的。
可以使用#cat /proc/mounts查看挂载了哪些文件系统
继续
上面的dev是手工创建
那么怎么自动创建dev下面的设备节点呢
方法:使用mdev
怎么使用mdev? 在/
可以查看mdev.txt
[1] mount -t sysfs sysfs /sys
[2] echo /bin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s
[4] mount -t tmpfs mdev /dev
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts
这6步就可以了。
所以:
1、#cd ../first_fs
#mkdir sys
[1] mount -t sysfs sysfs /sys
[4] mount -t tmpfs mdev /dev
应该怎么做?
#vim /etc/fstab
#device mount-point type options dump fsck
proc /proc proc defaults 0 0
sysfs /sys sysfs default 0 0
tmpfs /dev tmpfs default 0 0
[2] echo /bin/mdev > /proc/sys/kernel/hotplug
[3] mdev -s
[5] mkdir /dev/pts
[6] mount -t devpts devpts /dev/pts
怎么做呢?
#cd /etc/init.d/rcS
mount -a
mkdir /dev/pts/
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s