您的位置:首页 > 其它

U-boot分析与移植(3)----U-boot stage2分析 .

2014-03-05 23:31 288 查看

一来到void start_armboot (void)函数,马上出现两个很重要的数据结构gd_t和bd_t

1、gd_t : global data数据结构定义,位于文件 include/asm-arm/global_data.h。其成员主要是一些全局的系统初始化参数。

[cpp]
view plaincopyprint?

typedef struct  global_data {  
    bd_t        *bd;      // struct board_info<SPAN style="FONT-FAMILY: 宋体">指针,保存板子信息</SPAN>
  
    unsigned long   flags;     // <SPAN style="FONT-FAMILY: 宋体">指示标志,如设备已经初始化标志等</SPAN>
  
    unsigned long   baudrate;  
    unsigned long   have_console;   /* serial_init() was called */  
    unsigned long   reloc_off;  /* Relocation Offset */  
    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_VFD   
    unsigned char   vfd_type;   /* display type */  
#endif   
#if 0   
    unsigned long   cpu_clk;    /* CPU clock in Hz!     */  
    unsigned long   bus_clk;  
    unsigned long   ram_size;   /* RAM size */  
    unsigned long   reset_status;   /* reset status register at boot */  
#endif   
    void        **jt;       /* jump table */  
} gd_t;  

typedef	struct	global_data {
bd_t		*bd;      // struct board_info指针,保存板子信息
unsigned long	flags;     // 指示标志,如设备已经初始化标志等
unsigned long	baudrate;
unsigned long	have_console;	/* serial_init() was called */
unsigned long	reloc_off;	/* Relocation Offset */
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_VFD
unsigned char	vfd_type;	/* display type */
#endif
#if 0
unsigned long	cpu_clk;	/* CPU clock in Hz!		*/
unsigned long	bus_clk;
unsigned long	ram_size;	/* RAM size */
unsigned long	reset_status;	/* reset status register at boot */
#endif
void		**jt;		/* jump table */
} gd_t;

2.、bd_t :board info数据结构定义,位于文件 include/asm-arm/u-boot.h。保存板子参数。

[cpp]
view plaincopyprint?

typedef struct bd_info {  
    int         bi_baudrate;    /* serial console baudrate */  
    unsigned long   bi_ip_addr; /* IP Address */  
    unsigned char   bi_enetaddr[6]; /* Ethernet adress */  
    struct environment_s           *bi_env;  
    ulong           bi_arch_number; /* unique id for this board  <SPAN style="FONT-FAMILY: 宋体">板子</SPAN><SPAN style="FONT-FAMILY: Times New Roman">ID</SPAN><SPAN style="FONT-FAMILY: 宋体">号</SPAN>*/  
    ulong           bi_boot_params; /* where this board expects params */  
    struct              /* RAM configuration */  
    {  
    ulong start;  
    ulong size;  
    }           bi_dram[CONFIG_NR_DRAM_BANKS];  
#ifdef CONFIG_HAS_ETH1
  
    /* second onboard ethernet port */  
    unsigned char   bi_enet1addr[6];  
#endif   
} bd_t;  

typedef struct bd_info {
int			bi_baudrate;	/* serial console baudrate */
unsigned long	bi_ip_addr;	/* IP Address */
unsigned char	bi_enetaddr[6]; /* Ethernet adress */
struct environment_s	       *bi_env;
ulong	        bi_arch_number;	/* unique id for this board  板子ID号*/
ulong	        bi_boot_params;	/* where this board expects params */
struct				/* RAM configuration */
{
ulong start;
ulong size;
} 			bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char   bi_enet1addr[6];
#endif
} bd_t;


分配一个存储全局数据的区域,地 址给指针 gd

[cpp]
view plaincopyprint?

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));  

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));


清0并分配空间

[cpp]
view plaincopyprint?

memset ((void*)gd, 0, sizeof (gd_t));    

memset ((void*)gd, 0, sizeof (gd_t));


在gd前面的位置给 gd->bd赋值地址

[cpp]
view plaincopyprint?

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));


清0并分配空间

[cpp]
view plaincopyprint?

memset (gd->bd, 0, sizeof (bd_t));  

memset (gd->bd, 0, sizeof (bd_t));


执行一系列初始化函数

[cpp]
view plaincopyprint?

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {  
  if ((*init_fnc_ptr)() != 0) {  
   hang ();  
  }  
 }  

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}


假如函数指针指向的函数返回值不为0,那么在hang()里就会死循环,初始化失败

[cpp]
view plaincopyprint?

void hang (void)  
{  
 puts ("### ERROR ### Please RESET the board ###\n");  
 for (;;);  
}  

void hang (void)
{
puts ("### ERROR ### Please RESET the board ###\n");
for (;;);
}


函数列表如下:

每个初始化函数正常情况下返回值是0

[cpp]
view plaincopyprint?

init_fnc_t *init_sequence[] = {  
 cpu_init,  /* 初始化irq/fiq模式的栈*/  
 board_init, /* 设置系统时钟*/  
 interrupt_init, /*初始化定时器*/  
 env_init,  /* 检查flash上的环境参数是否有效*/  
 init_baudrate, /* 初始化波特率*/  
 serial_init, /* 初始化串口*/  
 console_init_f, /*初始化串口控制台*/  
 display_banner, /* say that we are here */  

init_fnc_t *init_sequence[] = {
cpu_init,  /* 初始化irq/fiq模式的栈*/
board_init, /* 设置系统时钟*/
interrupt_init, /*初始化定时器*/
env_init,  /* 检查flash上的环境参数是否有效*/
init_baudrate, /* 初始化波特率*/
serial_init, /* 初始化串口*/
console_init_f, /*初始化串口控制台*/
display_banner, /* say that we are here */

接着进行一些NOR FLASH,LCD,串口,控制台,sd卡,网卡等初始化,不一一列举了。

终于来到重要的时刻了 - -#

进入一个死循环

[cpp]
view plaincopyprint?

for (;;)  
{  
 main_loop ();  
}  

for (;;)
{
main_loop ();
}


继续跟踪

发现在bootdelay时间内按下键进入命令行,用run_command来解析命令

[cpp]
view plaincopyprint?

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  
    s = getenv ("bootdelay");  
    bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;  
  
    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);  

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

如果CONFIG_BOOTDELAY已经定义,用s得到环境变量bootdelay,然后倒数启动内核

[cpp]
view plaincopyprint?

#ifdef CONFIG_BOOTCOUNT_LIMIT
  
    if (bootlimit && (bootcount > bootlimit)) {  
        printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",  
                (unsigned)bootlimit);  
        s = getenv ("altbootcmd");  
    }  
    else  
#endif /* CONFIG_BOOTCOUNT_LIMIT */
  
        s = getenv ("bootcmd");  

#ifdef CONFIG_BOOTCOUNT_LIMIT
if (bootlimit && (bootcount > bootlimit)) {
printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n",
(unsigned)bootlimit);
s = getenv ("altbootcmd");
}
else
#endif /* CONFIG_BOOTCOUNT_LIMIT */
s = getenv ("bootcmd");


CONFIG_BOOTCOUNT_LIMIT是设置u-boot启动次数的限制

最后s = getenv ("bootcmd");获得启动参数

[cpp]
view plaincopyprint?

run_command (s, 0);  

run_command (s, 0);

启动命令解析

在run_command 函数里最终执行命令

[cpp]
view plaincopyprint?

/* OK - call function to do the command */  
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {  
 rc = -1;  
}  

/* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}

这是一个命令结构体,原型如下:

[cpp]
view plaincopyprint?

struct cmd_tbl_s {  
    char        *name;      /* Command Name         */  
    int     maxargs;             /* 最大的参数个数 */  
    int     repeatable; /* 命令可否重复   */  
    int     (*cmd)(struct cmd_tbl_s *, int, int, char *[]);/*对应的函数指针*/  
    char        *usage;     /* Usage message    (short) */  

struct cmd_tbl_s {
char		*name;		/* Command Name			*/
int		maxargs;	         /* 最大的参数个数	*/
int		repeatable;	/* 命令可否重复	*/
int		(*cmd)(struct cmd_tbl_s *, int, int, char *[]);/*对应的函数指针*/
char		*usage;		/* Usage message	(short)	*/

正常情况下就会执行U_BOOT_CMD命令,U_BOOT_CMD宏定义一个命令,命令宏原型如下:

[cpp]
view plaincopyprint?

/*命令宏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}  

/*命令宏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}

假若上面是传入的是一个bootm命令启动内核,将会调用相应的

 U_BOOT_CMD里的do_bootm函数

[cpp]
view plaincopyprint?

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"  

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"

在do_bootm函数里,将用switch case检查内核zImage类型,解压方式,操作系统等,因为zImage是自解压的,不用解压

[cpp]
view plaincopyprint?

switch (hdr->ih_os) {  
    default:            /* handled by (original) Linux case */  
    case IH_OS_LINUX:  
  
        do_bootm_linux  (cmdtp, flag, argc, argv,  
                 addr, len_ptr, verify);  
        break;    

switch (hdr->ih_os) {
default:			/* handled by (original) Linux case */
case IH_OS_LINUX:

do_bootm_linux  (cmdtp, flag, argc, argv,
addr, len_ptr, verify);
break;

最后,将进入Armlinux.c的do_bootm_linux函数启动Linux内核

U_Boot也是通过标记列表向内核传递参数的

[cpp]
view plaincopyprint?

#ifdef CONFIG_CMDLINE_TAG
  
    char *commandline = getenv ("bootargs");  
#endif  

#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif

CONFIG_CMDLINE_TAG在smdk2410.h里已经定义了

theKernel指向内核 存放的地址,(对于ARM架构的CPU,通常是0x30008000),

/*声明内核的入口函数指针*/

[cpp]
view plaincopyprint?

void (*theKernel)(int zero, int arch, uint params);  

void (*theKernel)(int zero, int arch, uint params);

 

/*把内核入口地址赋值给theKernel,hdr是image_header_t结构体,指向uImage头部 ,ih_ep是内核的入口点(Entry Point)*/

[cpp]
view plaincopyprint?

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);   

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

/*最后是对内核入口函数的调用,bd->bi_arch_number是这个板子机器类型ID, bd->bi_boot_params是传给内核的参数,从标记列表地址开始*/

[cpp]
view plaincopyprint?

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);  

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);


[cpp]
view plaincopyprint?

引导Linux内核启动的必须要满足的几个条件:  

引导Linux内核启动的必须要满足的几个条件:


[cpp]
view plaincopyprint?

* CPU register settings //这里也就是我们的theKernel中的作用
  
          o r0 = 0.  
          o r1 = machine type number.  
          o r2 = physical address of tagged list in system RAM.  
    * CPU mode  
          o All forms of interrupts must be disabled (IRQs and FIQs.)  
          o The CPU must be in SVC mode. (A special exception exists for Angel.)  
    * Caches, MMUs  
          o The MMU must be off.  
          o Instruction cache may be on or off.  
          o Data cache must be off and must not contain any stale data.  
    * Devices  
          o DMA to/from devices should be quiesced.  
    * The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: