mini2440系统移植篇之u
1. 第二阶段C语言
1.1. 初始化
由第一阶段汇编得知,最后跳到C语言的start_armboot,这个函数在lib_arm/board.c。
1.1.1. 全局结构体
程序一开始申请两个全局结构体gd和bd。gd是全局相关的变量,主要有波特率,环境变量地址,帧缓冲地址,还有一个flag。
typedef struct global_data {bd_t *bd;unsigned long flags;unsigned long baudrate;unsigned long have_console; /* serial_init() was called */unsigned long env_addr; /* Address of Environment struct */unsigned long env_valid; /* Checksum of Environment valid? */unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFDunsigned char vfd_type; /* display type */
#endifvoid **jt; /* jump table */
} gd_t;/** Global Data Flags*/
#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */
#define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */
#define GD_FLG_SILENT 0x00004 /* Silent mode */
#define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */
#define GD_FLG_POSTSTOP 0x00010 /* POST seqeunce aborted */
#define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */
#define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out) */bd主要是存储开发板信息,重要的有机器码arch_number,启动参数boot_params和内存配置,这些信息等到启动内核时,会传递给内核。typedef struct bd_info {int bi_baudrate; /* serial console baudrate */unsigned long bi_ip_addr; /* IP Address */struct environment_s *bi_env;ulong bi_arch_number; /* unique id for this board */ulong bi_boot_params; /* where this board expects params */struct /* RAM configuration */{ulong start;ulong size;} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
bd主要是存储开发板信息,重要的有机器码arch_number,启动参数boot_params和内存配置,这些信息等到启动内核时,会传递给内核。
typedef struct bd_info {int bi_baudrate; /* serial console baudrate */unsigned long bi_ip_addr; /* IP Address */struct environment_s *bi_env;ulong bi_arch_number; /* unique id for this board */ulong bi_boot_params; /* where this board expects params */struct /* RAM configuration */{ulong start;ulong size;} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
1.1.2. 硬件初始化
接下来是一个函数指针表,进行各种硬件初始化,其中比较重要的是板子初始化board_init、串口初始化serial_init。只要display_banner能打出信息,说明串口可以用了,这是为以后可以调试奠定了基础。除了这个函数指针数组,接下来还会有flash、网卡、环境变量等初始化。
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)arch_cpu_init, /* basic arch cpu dependent setup */
#endifboard_init, /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)interrupt_init, /* set up exceptions */
#endiftimer_init, /* initialize timer */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 */
#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)init_func_i2c,
#endifdram_init, /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)arm_pci_init,
#endifdisplay_dram_config,NULL,
};for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}
1.2. 主循环
完成了相关的初始化后,u-boot进入一个死循环。
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
这个死循环获取环境变量bootdelay的值,然后进行倒计时,在这段时间内控制台有按键响应,则进入等待命令输入模式,超时根据bootcmd启动内核。我在这里加了一个run_command("menu",0),即进入命令模式后,执行一个菜单选项,用于一键升级内核-u-boot等操作,方便开发。
main_loop()
{
…
配置自动完成
获取bootdelay值
获取bootcmd值
没有按键按下,执行bootcmd启动内核,否则继续向下执行
run_command("menu",0); //==交互菜单
for()
{
len = readline (CONFIG_SYS_PROMPT); //==读输入
run_command (lastcommand, flag);
}
}
1.2.1. 加载内核
加载启动内核来至于run_command(bootcmd),首先是加载,把内核从flash拷贝到内存的指定位置。
加载内核
{
内核加载地址 image_header loader
内核运行地址 image_header ep
从Flash拷贝kernel到内存
nand read 内存地址(0x30007fc0) Flash偏移 大小
启动内核
bootm 内存地址(0x30008000)
}
1.2.2. 启动内核
启动内核,根据内核的编译地址和实际地址,决定要不要移动内核,启动内核之前,要设置给内核的参数,使用tag结构体。常用的保护5个tag,开头setup_start_tag结尾setup_end_tag ,内存setup_memory_tags,命令行setup_commandline_tag (bd, commandline),setup_initrd_tag (bd, images->rd_start, images->rd_end)。最后启动内核是一个函数指针theKernel = (void (*)(int, int, uint))images->ep;//image_header的入口地址,第一个参数固定是0,第2个是机器码,保存在上面提到的bd全局结构体,第三个是内核参数tag。bootloader巧妙地利用函数指针及传参规范将R0:0x0,R1:机器号,R2:参数地址传递给内核.
do_bootm
{
bootm_start
{
boot_get_kernel
{
memmove
}
xxx
do_bootm_linux
}
}
do_bootm_linux
{
theKernel = (void (*)(int, int, uint))images->ep;//image_header的入口地址
获取machid
在hyq2440.c board_init设置 gd->bd->bi_boot_params = 0x30000100;
这个就是tag起始地址
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline); //根据bootargs设置tag
setup_initrd_tag (bd, images->rd_start, images->rd_end);
setup_end_tag (bd);
theKernel (0, machid, bd->bi_boot_params);
}
mini2440系统移植篇之u
1. 第二阶段C语言
1.1. 初始化
由第一阶段汇编得知,最后跳到C语言的start_armboot,这个函数在lib_arm/board.c。
1.1.1. 全局结构体
程序一开始申请两个全局结构体gd和bd。gd是全局相关的变量,主要有波特率,环境变量地址,帧缓冲地址,还有一个flag。
typedef struct global_data {bd_t *bd;unsigned long flags;unsigned long baudrate;unsigned long have_console; /* serial_init() was called */unsigned long env_addr; /* Address of Environment struct */unsigned long env_valid; /* Checksum of Environment valid? */unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFDunsigned char vfd_type; /* display type */
#endifvoid **jt; /* jump table */
} gd_t;/** Global Data Flags*/
#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */
#define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */
#define GD_FLG_SILENT 0x00004 /* Silent mode */
#define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */
#define GD_FLG_POSTSTOP 0x00010 /* POST seqeunce aborted */
#define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */
#define GD_FLG_DISABLE_CONSOLE 0x00040 /* Disable console (in & out) */bd主要是存储开发板信息,重要的有机器码arch_number,启动参数boot_params和内存配置,这些信息等到启动内核时,会传递给内核。typedef struct bd_info {int bi_baudrate; /* serial console baudrate */unsigned long bi_ip_addr; /* IP Address */struct environment_s *bi_env;ulong bi_arch_number; /* unique id for this board */ulong bi_boot_params; /* where this board expects params */struct /* RAM configuration */{ulong start;ulong size;} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
bd主要是存储开发板信息,重要的有机器码arch_number,启动参数boot_params和内存配置,这些信息等到启动内核时,会传递给内核。
typedef struct bd_info {int bi_baudrate; /* serial console baudrate */unsigned long bi_ip_addr; /* IP Address */struct environment_s *bi_env;ulong bi_arch_number; /* unique id for this board */ulong bi_boot_params; /* where this board expects params */struct /* RAM configuration */{ulong start;ulong size;} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
1.1.2. 硬件初始化
接下来是一个函数指针表,进行各种硬件初始化,其中比较重要的是板子初始化board_init、串口初始化serial_init。只要display_banner能打出信息,说明串口可以用了,这是为以后可以调试奠定了基础。除了这个函数指针数组,接下来还会有flash、网卡、环境变量等初始化。
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)arch_cpu_init, /* basic arch cpu dependent setup */
#endifboard_init, /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)interrupt_init, /* set up exceptions */
#endiftimer_init, /* initialize timer */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 */
#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard, /* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)init_func_i2c,
#endifdram_init, /* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)arm_pci_init,
#endifdisplay_dram_config,NULL,
};for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}
1.2. 主循环
完成了相关的初始化后,u-boot进入一个死循环。
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
这个死循环获取环境变量bootdelay的值,然后进行倒计时,在这段时间内控制台有按键响应,则进入等待命令输入模式,超时根据bootcmd启动内核。我在这里加了一个run_command("menu",0),即进入命令模式后,执行一个菜单选项,用于一键升级内核-u-boot等操作,方便开发。
main_loop()
{
…
配置自动完成
获取bootdelay值
获取bootcmd值
没有按键按下,执行bootcmd启动内核,否则继续向下执行
run_command("menu",0); //==交互菜单
for()
{
len = readline (CONFIG_SYS_PROMPT); //==读输入
run_command (lastcommand, flag);
}
}
1.2.1. 加载内核
加载启动内核来至于run_command(bootcmd),首先是加载,把内核从flash拷贝到内存的指定位置。
加载内核
{
内核加载地址 image_header loader
内核运行地址 image_header ep
从Flash拷贝kernel到内存
nand read 内存地址(0x30007fc0) Flash偏移 大小
启动内核
bootm 内存地址(0x30008000)
}
1.2.2. 启动内核
启动内核,根据内核的编译地址和实际地址,决定要不要移动内核,启动内核之前,要设置给内核的参数,使用tag结构体。常用的保护5个tag,开头setup_start_tag结尾setup_end_tag ,内存setup_memory_tags,命令行setup_commandline_tag (bd, commandline),setup_initrd_tag (bd, images->rd_start, images->rd_end)。最后启动内核是一个函数指针theKernel = (void (*)(int, int, uint))images->ep;//image_header的入口地址,第一个参数固定是0,第2个是机器码,保存在上面提到的bd全局结构体,第三个是内核参数tag。bootloader巧妙地利用函数指针及传参规范将R0:0x0,R1:机器号,R2:参数地址传递给内核.
do_bootm
{
bootm_start
{
boot_get_kernel
{
memmove
}
xxx
do_bootm_linux
}
}
do_bootm_linux
{
theKernel = (void (*)(int, int, uint))images->ep;//image_header的入口地址
获取machid
在hyq2440.c board_init设置 gd->bd->bi_boot_params = 0x30000100;
这个就是tag起始地址
setup_start_tag (bd);
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline); //根据bootargs设置tag
setup_initrd_tag (bd, images->rd_start, images->rd_end);
setup_end_tag (bd);
theKernel (0, machid, bd->bi_boot_params);
}
发布评论