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);

}