固件在完成上述相关CPU、设备环境初始化后,将完成它最后的使命就是引导内核,在initmips最后条用main,对你没有看错,就是那个main。
1、main函数
1> 获取环境变量ShowBootMenu,如设置了bootlist,则进入函数load_menu_list函数,在usb设备(U盘)、CD-ROM、sata盘中查找boot.cfg文件(把欲引导的内核的目录和传给内核的参数以一定的格式放在 boot.cfg 的文件),如找到,则执行do_cmd(load),引导内核完成启动。
2> 如没有找到boot.cfg文件,则获取环境变量al,运行autorun加载/dev/fs/iso9600@cd0/boot/vmlinux或者/dev/fs/ext2@wd0/boot/vmlinux下放置的内核,如下面没有内核,则进入pmon命令行。
3> 如进入命令行,则检测输入命令并判断是否为定义好的cmd命令,执行前面初始化好的cmd处理函数。
2、引导内核过程
do_cmd(“load /dev/fs/ext2@wd0/boot/vmlinux’)和do_cmd(“g console=tty root=/dev/hda1 no_auto_cmd’)简单的说,第一个调用会把那个 vmlinux 文件放到内存上某个位置,g 会执行那个文件。
CmdList的初始化???
bl -d ide /dev/fs/ext2@wd0/boot/boot.cfg =====> cmd_menu_list处理函数
cmd_menu_list =>show_main=>load_list_menu
show_main函数解析bl -d ide /dev/fs/ext2@wd0/boot/boot.cfg获取path值:
path:/dev/fs/ext2@wd0/boot/boot.cfg,调用load_list_menu。
在load_list_menu函数中,通过路径打开传入的cfg文件,并在menu_list_read函数中,将cfg文件中的信息保存,(1)将title、内核、参数、initrd以及root信息保存在全局结构体数组menu_items中。(2)将全局选项(如下menu_options)保存到全局变量menu_option中。
MenuOptions menu_options[] = {
{"showmenu", 0, 0, "1"}, //是否给用户显示菜单
{"default", 0, 0, "0"}, //默认执行的菜单
{"timeout", 0, 0, "5"},//菜单原则事件
{"password", 0, 1, ""},
{"md5_enable", 0, 0, "0"}, /* 1-md5 */
};
typedef struct _Menu_Item_
{
char title [MENU_TITLE_BUF_LEN + 1]; //Title of menu item, display on screen.
char * kernel ; //kernel file to load.
char * args ; //arguments for kernel.
char * initrd ; //initrd file for kernel, maybe empty.
char * root; //ROOT device from args.
}Menu_Item;
typedef struct menu_option
{
char option [OPTION_LEN + 1];
int set_type; /* 0-unset£¬1-set */
int use_default; /* use defualt value, 0-if value don't used, use default, 1- must set */
char value [GLOBAL_VALUE_LEN + 1];
}MenuOptions;
show_main函数继续执行,从全局选项(如下menu_options)中获取默认启动的菜单条目以及菜单超时的默认时间。清屏并执行draw_main函数。
draw_main函数在屏幕上显示可选择的启动菜单以及提示信息。以后等待用户输入。当输入c时,退出菜单回到pmon命令行。当获取到上下键选择菜单,则根据选择得到启动的内核对应的条目。
最后调用do_cmd_boot_load函数加载启动内核。
do_cmd_boot_load=>boot_load=>boot_load_from_menu分别通过load_kernel_from_menu以及load_initrd_from_menu加载内核以及initrd。最后通过boot_run_from_menu函数运行内核。
1、在load_kernel_from_menu函数中,根据菜单选择的内核条目,传入对应的menu_items结构体数组成员。之后调用boot_kernel函数。
在boot_kernel函数中,根据传入的内核路径打开内核文件描述符。通过函数exec函数调用ep = (*p->loader) (fd, buf, n, flags))。这里的函数指针为Load_elf()函数在../pmon/loaders/exec_elf.c中
static void init_exec()
{
/*
* Install ram based file system.
*/
exec_init(&srec_exec);
}
函数初始化。数据结构:
typedef struct ExecType {
char *execname;
long (*loader) __P((int , char *, int *, int ));
int flags;
SLIST_ENTRY(ExecType) i_next;
} ExecType;
通过函数load_srec函数加载内核。内核入口Entry address is 80c02dd0。
- load_initrd_from_menu函数调用boot_initrd函数通过exec将initrd加载地址为0x84000000的内存中。
- 最后调用boot_run_from_menu函数,构造启动引导命令:g root=/dev/sda1 console=tty no_console_suspend console=ttyS0,115200 rw。执行do_cmd,跳转到相应注册到pmon的命令处理函数,这里跳转到cmd_go函数中来引导内核。
在cmd_go函数中,clientav指向命令行参数。调用initstack函数,进行一些重要的初始化。
在initstack函数中。计算传参长度以及bp长度设置栈大小。初始化struct boot_params(2k没有用)。调用setup_dtb(ac, av);解析dtb。
在setup_dtb函数中,检查DTB的地址以及checksum是否正常。将DTB数据拷贝到前面设置好的堆栈中。更新设备树中关于/chosen节点的数据,将命令行参数将其覆盖。
initstack函数返回,打印32个通用寄存器的值。设置状态寄存器以及硬件相关设置。更新配置pcie。执行goclient函数将控制权交给内核。至此pmon执行完毕。