您的位置:首页 > 其它

uboot 启动流程分析(二) — 第二阶段

2014-01-25 11:07 471 查看
在 uboot 第一阶段启动完成后将会调用 start_armboot 开始第二阶段的启动流程,这个阶段的代码由 c 语言编写,分析如下:

一、基础数据结构

第二阶段主要用到了两个数据结构即 gd_t bd_t,其定义如下:

[cpp]
view plaincopyprint?

/* 全局数据结构 */
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; /* 环境参数地址 */
unsigned long env_valid; /* 环境参数 CRC 校验有效标志 */
unsigned long fb_base; /* fb 起始地址 */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* 显示器类型(VFD代指真空荧光屏) */
#endif
#if 0
unsigned long cpu_clk; /* cpu 频率*/
unsigned long bus_clk; /* bus 频率 */
phys_size_t ram_size; /* ram 大小 */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* 跳转函数表 */
} gd_t;

/* Global Data Flags */
#define GD_FLG_RELOC 0x00001 /* 代码已经转移到 RAM */

#define GD_FLG_DEVINIT 0x00002 /* 设备已经完成初始化 */

#define GD_FLG_SILENT 0x00004 /* 静音模式 */

#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) */

/* 定义一个寄存器变量,占用寄存器r8,作为 gd_t 的全局指针 */
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

/* 板级信息结构 */
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; /* 板子的 id */
ulong bi_boot_params; /* 板子的启动参数 */
struct /* RAM 配置 */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

/**************************************************************************
*
* 每个环境变量以形如"name=value"的字符串存储并以'\0'结尾,环境变量的尾部以两个'\0'结束。
* 新增的环境变量都依次添加在尾部,如果删除一个环境变量需要将其后面的向前移动
* 如果替换一个环境变量需要先删除再新增。
*
* 环境变量采用 32 bit CRC 校验.
*
**************************************************************************
*/

/* 我们平台定义了8kB大小的环境变量存储区,地址空间位于4M ~ 6M */
#define CONFIG_ENV_IS_IN_NAND
#define CONFIG_ENV_OFFSET 0x00400000 /* 4M ~ 6M */

#define CONFIG_ENV_SIZE 8192 /* 8KB */

#define CONFIG_ENV_RANGE 0x00200000

#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)

/* 环境变量结构 */
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE]; /* Environment data */
} env_t;

/* 全局数据结构 */
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;       /* 环境参数地址 */
unsigned long   env_valid;      /* 环境参数 CRC 校验有效标志 */
unsigned long   fb_base;        /* fb 起始地址 */
#ifdef CONFIG_VFD
unsigned char   vfd_type;       /* 显示器类型(VFD代指真空荧光屏) */
#endif
#if 0
unsigned long   cpu_clk;        /* cpu 频率*/
unsigned long   bus_clk;        /* bus 频率 */
phys_size_t     ram_size;       /* ram 大小 */
unsigned long   reset_status;   /* reset status register at boot */
#endif
void            **jt;           /* 跳转函数表 */
} gd_t;

/* Global Data Flags */
#define	GD_FLG_RELOC	0x00001		/* 代码已经转移到 RAM */
#define	GD_FLG_DEVINIT	0x00002		/* 设备已经完成初始化 */
#define	GD_FLG_SILENT	0x00004		/* 静音模式 */
#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)	 */
/* 定义一个寄存器变量,占用寄存器r8,作为 gd_t 的全局指针 */
#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

/* 板级信息结构 */
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;  /* 板子的 id */
ulong                 bi_boot_params;  /* 板子的启动参数 */
struct                                 /* RAM 配置 */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

/**************************************************************************
*
* 每个环境变量以形如"name=value"的字符串存储并以'\0'结尾,环境变量的尾部以两个'\0'结束。
* 新增的环境变量都依次添加在尾部,如果删除一个环境变量需要将其后面的向前移动
* 如果替换一个环境变量需要先删除再新增。
*
* 环境变量采用 32 bit CRC 校验.
*
**************************************************************************
*/

/* 我们平台定义了8kB大小的环境变量存储区,地址空间位于4M ~ 6M */
#define CONFIG_ENV_IS_IN_NAND
#define CONFIG_ENV_OFFSET  0x00400000   /* 4M ~ 6M */
#define CONFIG_ENV_SIZE    8192         /* 8KB */
#define CONFIG_ENV_RANGE   0x00200000

#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)
/* 环境变量结构 */
typedef	struct environment_s {
uint32_t  crc;                 /* CRC32 over data bytes */
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
unsigned char flags;           /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE];  /* Environment data */
} env_t;


这两个类型变量记录了刚启动时的信息,还将记录作为引导内核和文件系统的参数,如 bootargs 等,并且将来还会在启动内核时,由 uboot 交由 kernel 时会有所用。

二、启动流程

1、init_sequence

start_armboot 首先为全局数据结构和板级信息结构分配内存,代码如下:

[cpp]
view plaincopyprint?

#define CONFIG_UNCONTINUOUS_MEM

#define CONFIG_SYS_MALLOC_END (MDDR_BASE_ADDR + 0x00600000)

#define CONFIG_SYS_MALLOC_LEN 0x00100000 /* 1MB */

gd = (gd_t*)(CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

__asm__ __volatile__("": : :"memory");

memset ((void*)gd, 0, sizeof (gd_t)); /* 将全局数据清零 */
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); /* 取得板级信息数据结构的起始地址 */
memset (gd->bd, 0, sizeof (bd_t)); /* 将板级信息清零 */

gd->flags |= GD_FLG_RELOC; /* 标记为代码已经转移到 RAM */

#define CONFIG_UNCONTINUOUS_MEM
#define CONFIG_SYS_MALLOC_END  (MDDR_BASE_ADDR + 0x00600000)
#define CONFIG_SYS_MALLOC_LEN  0x00100000  /* 1MB */

gd = (gd_t*)(CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

__asm__ __volatile__("": : :"memory");

memset ((void*)gd, 0, sizeof (gd_t));        /* 将全局数据清零 */
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));  /* 取得板级信息数据结构的起始地址 */
memset (gd->bd, 0, sizeof (bd_t));           /* 将板级信息清零 */

gd->flags |= GD_FLG_RELOC;  /* 标记为代码已经转移到 RAM */

可以看到 bd_t 、gd_t 以及 MALLOC 区域是紧挨着的。然后依次调用 init_sequence数组中的函数指针完成各部分的初始化,代码如下:

[cpp]
view plaincopyprint?

typedef int (init_fnc_t) (void);

int print_cpuinfo (void);

init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* 与处理器架构相关的初始化 */
#endif
board_init, /* 板级特殊设备初始化 */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* 初始化中断 */
#endif
timer_init, /* 初始化定时器 */
env_init, /* 初始化环境变量 */
init_baudrate, /* 初始化波特率 */
serial_init, /* 初始化串口 */
console_init_f, /* 控制台初始化第一阶段 */
display_banner, /* 打印uboot版本信息 */
#if defined(CONFIG_DISPLAY_CPUINFO)

print_cpuinfo, /* 打印cpu信息及各总线频率 */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* 打印板级信息 */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

init_func_i2c, /* 初始化i2c */
#endif
dram_init, /* 配置有效的内存区 */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)

arm_pci_init,
#endif
display_dram_config, /* 打印内存区配置信息 */
NULL,
};

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

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

typedef int (init_fnc_t) (void);

int print_cpuinfo (void);

init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init,          /* 与处理器架构相关的初始化 */
#endif
board_init,             /* 板级特殊设备初始化 */
#if defined(CONFIG_USE_IRQ)
interrupt_init,         /* 初始化中断 */
#endif
timer_init,             /* 初始化定时器 */
env_init,               /* 初始化环境变量 */
init_baudrate,          /* 初始化波特率 */
serial_init,            /* 初始化串口 */
console_init_f,         /* 控制台初始化第一阶段 */
display_banner,         /* 打印uboot版本信息 */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo,          /* 打印cpu信息及各总线频率 */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard,             /* 打印板级信息 */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,          /* 初始化i2c */
#endif
dram_init,              /* 配置有效的内存区 */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
arm_pci_init,
#endif
display_dram_config,    /* 打印内存区配置信息 */
NULL,
};

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

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

在我们平台比较重要的初始化函数有 board_init 以及 env_init,代码如下:

[cpp]
view plaincopyprint?

/* 板级特殊设备初始化 */ int board_init(void) { uint32_t val; uint32_t adc_vol; uint32_t adc_per_vol; uint32_t adc_per_vol_res; mmu_cache_on(memory_map); /* 初始化mmu */ clock_init(); /* 初始化时钟 */ calibrate_delay(); /* 延时校准 */ /*enable power */ xx_request_gpio(GPIO_PMU_WAKEUP); xx_set_gpio_direction(GPIO_PMU_WAKEUP, 0); xx_gpio_set(GPIO_PMU_WAKEUP,0); xx_request_gpio(GPIO_PMU_MODE); xx_set_gpio_direction(GPIO_PMU_MODE, 0); xx_gpio_set(GPIO_PMU_MODE,0); i2c_init(); adc_init(); pmu_init(); keypad_init(); /* arch number of board */ gd->bd->bi_arch_number = MACH_TYPE_XXX; /* adress of boot parameters */ gd->bd->bi_boot_params = CONFIG_ATAG_ADDR; return 0; } /* 初始化环境变量 */ int env_init(void) { gd->env_addr = (ulong)&default_environment[0]; /* 设定默认的环境变量 */ gd->env_valid = 1; return (0); }
/* 板级特殊设备初始化 */
int board_init(void)
{
uint32_t val;
uint32_t adc_vol;
uint32_t adc_per_vol;
uint32_t adc_per_vol_res;

mmu_cache_on(memory_map);  /* 初始化mmu */
clock_init();              /* 初始化时钟 */

calibrate_delay();         /* 延时校准 */

/*enable power */
xx_request_gpio(GPIO_PMU_WAKEUP);
xx_set_gpio_direction(GPIO_PMU_WAKEUP, 0);
xx_gpio_set(GPIO_PMU_WAKEUP,0);

xx_request_gpio(GPIO_PMU_MODE);
xx_set_gpio_direction(GPIO_PMU_MODE, 0);
xx_gpio_set(GPIO_PMU_MODE,0);

i2c_init();

adc_init();

pmu_init();

keypad_init();

/* arch number of board */
gd->bd->bi_arch_number = MACH_TYPE_XXX;
/* adress of boot parameters */
gd->bd->bi_boot_params = CONFIG_ATAG_ADDR;

return 0;
}

/* 初始化环境变量 */
int env_init(void)
{
gd->env_addr  = (ulong)&default_environment[0];  /* 设定默认的环境变量 */
gd->env_valid = 1;

return (0);
}

在环境变量 default_environment 中我们设置了很多参数,列表如下:

[cpp]
view plaincopyprint?

uchar default_environment[] = {
/* #define CONFIG_UBOOT_OFFSET 0x00200000 */
"uboot-nandoff="MK_STR(CONFIG_UBOOT_OFFSET) "\0"
/* #define CONFIG_UBOOT_LADDR 0x88007e00 */
"uboot-laddr=" MK_STR(CONFIG_UBOOT_LADDR) "\0"

/* #define CONFIG_BOOTARGS_SD "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 \
mtdparts=atxx_nd:32M(boot),2M(ttyMTD),-(system) quiet" */
"bootargs_sd=" CONFIG_BOOTARGS_SD "\0"
/* #define CONFIG_BOOTCOMMAND_SD "fatload mmc 1 0x89807e00 kboot.img; hdcvt 89807e00; bootm 89807fc0" */
"bootcmd_sd=" CONFIG_BOOTCOMMAND_SD "\0"

/* 各总线时钟频率
* #define CONFIG_CLK_ARM 1001000000
* #define CONFIG_CLK_AXI 312000000
* #define CONFIG_CLK_APP 104000000
* #define CONFIG_CLK_MDDR 201000000
* #define CONFIG_CLK_GCLK 312000000
* #define CONFIG_CLK_VPCLK 156000000
* #define CONFIG_CLK_VSCLK 403000000
*/
"clk-arm=" MK_STR(CONFIG_CLK_ARM) "\0"
"clk-axi=" MK_STR(CONFIG_CLK_AXI) "\0"
"clk-app=" MK_STR(CONFIG_CLK_APP) "\0"
"clk-mddr=" MK_STR(CONFIG_CLK_MDDR) "\0"
"clk-gclk=" MK_STR(CONFIG_CLK_GCLK) "\0"
"clk-vpclk=" MK_STR(CONFIG_CLK_VPCLK) "\0"
"clk-vsclk=" MK_STR(CONFIG_CLK_VSCLK) "\0"

/* #define CONFIG_BOOTARGS "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 mtdparts=\
atxx_nd:32M(boot),2M(ttyMTD),-(system) init=/init ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs ro" */
"bootargs=" CONFIG_BOOTARGS "\0"
/* #define CONFIG_BOOTCOMMAND "nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0" */
"bootcmd=" CONFIG_BOOTCOMMAND "\0"
"clocks_in_mhz=1\0"
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};

uchar default_environment[] = {
/* #define CONFIG_UBOOT_OFFSET  0x00200000 */
"uboot-nandoff="MK_STR(CONFIG_UBOOT_OFFSET)	"\0"
/* #define CONFIG_UBOOT_LADDR   0x88007e00 */
"uboot-laddr="	MK_STR(CONFIG_UBOOT_LADDR)	"\0"

/* #define CONFIG_BOOTARGS_SD    "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 \
mtdparts=atxx_nd:32M(boot),2M(ttyMTD),-(system) quiet" */
"bootargs_sd="	CONFIG_BOOTARGS_SD		"\0"
/* #define CONFIG_BOOTCOMMAND_SD "fatload mmc 1 0x89807e00 kboot.img; hdcvt 89807e00; bootm 89807fc0" */
"bootcmd_sd="	CONFIG_BOOTCOMMAND_SD		"\0"

/* 各总线时钟频率
* #define CONFIG_CLK_ARM    1001000000
* #define CONFIG_CLK_AXI    312000000
* #define CONFIG_CLK_APP    104000000
* #define CONFIG_CLK_MDDR   201000000
* #define CONFIG_CLK_GCLK   312000000
* #define CONFIG_CLK_VPCLK  156000000
* #define CONFIG_CLK_VSCLK  403000000
*/
"clk-arm="     MK_STR(CONFIG_CLK_ARM)   "\0"
"clk-axi="     MK_STR(CONFIG_CLK_AXI)   "\0"
"clk-app="     MK_STR(CONFIG_CLK_APP)   "\0"
"clk-mddr="    MK_STR(CONFIG_CLK_MDDR)  "\0"
"clk-gclk="    MK_STR(CONFIG_CLK_GCLK)  "\0"
"clk-vpclk="   MK_STR(CONFIG_CLK_VPCLK) "\0"
"clk-vsclk="   MK_STR(CONFIG_CLK_VSCLK) "\0"

/* #define CONFIG_BOOTARGS  "console=ttyS0,921600n8 console=ttyMTD androidboot.console=ttyS0 mtdparts=\
atxx_nd:32M(boot),2M(ttyMTD),-(system) init=/init ubi.mtd=2 root=ubi0:rootfs rootfstype=ubifs ro" */
"bootargs="	CONFIG_BOOTARGS			"\0"
/* #define CONFIG_BOOTCOMMAND  "nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0" */
"bootcmd="	CONFIG_BOOTCOMMAND		"\0"
"clocks_in_mhz=1\0"
#ifdef  CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"\0"
};
我们可以在 uboot 命令行模式下输入 printenv 命令查看当前的环境变量值。

2、start_armboot

start_armboot 在接下来的流程中还做了如下操作:

[cpp]
view plaincopyprint?

void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
unsigned long addr;
mem_malloc_init (CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN);

...
/* board init may have inited fb_base */
if (!gd->fb_base) {
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = CONFIG_SYS_MALLOC_END;
lcd_setmem (addr);
gd->fb_base = addr;
}

nand_init(); /* 初始化 NAND */

env_relocate (); /* 重定位环境变量,将其从 NAND 拷贝到内存中 */

serial_initialize(); /* 初始化串口 */

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); /* IP Address */

stdio_init (); /* 初始化外设 */

jumptable_init (); /* 初始化跳转函数表 */

console_init_r (); /* 控制台初始化第二阶段 */

misc_init_r (); /* 杂项设备初始化, eg:battery */

enable_interrupts (); /* enable exceptions */

/* #define CONFIG_SYS_LOAD_ADDR (MDDR_BASE_ADDR + 0x00807e00) */
/* 如果存在则从环境变量中读取装载地址,其默认为 ulong load_addr = CONFIG_SYS_LOAD_ADDR; */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}

/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop (); /* 进入主循环 common/main.c */
}

/* NOTREACHED - no way out of command loop except booting */
}

void start_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
unsigned long addr;
mem_malloc_init (CONFIG_SYS_MALLOC_END - CONFIG_SYS_MALLOC_LEN, CONFIG_SYS_MALLOC_LEN);

...
/* board init may have inited fb_base */
if (!gd->fb_base) {
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = CONFIG_SYS_MALLOC_END;
lcd_setmem (addr);
gd->fb_base = addr;
}

nand_init();          /* 初始化 NAND */

env_relocate ();      /* 重定位环境变量,将其从 NAND 拷贝到内存中 */

serial_initialize();  /* 初始化串口 */

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");  /* IP Address */

stdio_init ();	      /* 初始化外设 */

jumptable_init ();    /* 初始化跳转函数表 */

console_init_r ();    /* 控制台初始化第二阶段 */

misc_init_r ();       /* 杂项设备初始化, eg:battery */

enable_interrupts (); /* enable exceptions */

/* #define CONFIG_SYS_LOAD_ADDR  (MDDR_BASE_ADDR + 0x00807e00) */
/* 如果存在则从环境变量中读取装载地址,其默认为 ulong load_addr = CONFIG_SYS_LOAD_ADDR; */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}

/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop (); /* 进入主循环 common/main.c */
}

/* NOTREACHED - no way out of command loop except booting */
}
其中工作比较多的是 stdio_init 完成了部分外设的初始化:

[cpp]
view plaincopyprint?

int stdio_init (void) { /* 初始化设备链表 */ INIT_LIST_HEAD(&(devs.list)); drv_lcd_init (); /* 初始化 lcd,显示 Logo */ drv_system_init (); /* 初始化stdio设备系统 */ serial_stdio_init (); /* 初始化串口 */ return (0); }

int stdio_init (void)
{
/* 初始化设备链表 */
INIT_LIST_HEAD(&(devs.list));

drv_lcd_init ();       /* 初始化 lcd,显示 Logo */
drv_system_init ();    /* 初始化stdio设备系统 */
serial_stdio_init ();  /* 初始化串口 */

return (0);
}
start_armboot 最终进入 main_loop 函数,首先判断用户选择的启动模式,如果是命令模式则等待输入命令然后执行,代码如下:

[cpp]
view plaincopyprint?

void main_loop (void) { static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; char *s; int bootdelay; s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; /* 获取超时信息 */ s = getenv ("bootcmd"); /* 获取启动命令 */ /* abortboot会判断用户选择的启动模式,如果是命令模式就会返回1,如果是其他模式就不再返回 */ if (bootdelay >= 0 && s && !abortboot (bootdelay)) { s = getenv ("bootcmd"); run_command (s, 0); } for (;;) { len = readline (CONFIG_SYS_PROMPT); /* 读取输入 */ flag = 0; if (len > 0) strcpy (lastcommand, console_buffer); /* 将输入保存到历史记录中 */ else if (len == 0) flag |= CMD_FLAG_REPEAT; /* 如果没有输入则重复上次 */ if (len == -1) puts ("<INTERRUPT>\n"); else rc = run_command (lastcommand, flag); /* 执行命令 */ if (rc <= 0) { /* 执行错误的命令从历史记录中删除 */ lastcommand[0] = 0; } } }

void main_loop (void)
{
static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };
int len;
int rc = 1;
int flag;
char *s;
int bootdelay;

s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;  /* 获取超时信息 */
s = getenv ("bootcmd");  /* 获取启动命令 */

/* abortboot会判断用户选择的启动模式,如果是命令模式就会返回1,如果是其他模式就不再返回 */
if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
s = getenv ("bootcmd");
run_command (s, 0);
}

for (;;) {
len = readline (CONFIG_SYS_PROMPT);        /* 读取输入 */

flag = 0;
if (len > 0)
strcpy (lastcommand, console_buffer);  /* 将输入保存到历史记录中 */
else if (len == 0)
flag |= CMD_FLAG_REPEAT;               /* 如果没有输入则重复上次 */

if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag);  /* 执行命令 */

if (rc <= 0) {  /* 执行错误的命令从历史记录中删除 */
lastcommand[0] = 0;
}
}
}

附:启动命令解析

在 uboot 进入主循环后默认会进入 nand 启动模式,会依次执行 3 个命令:

[cpp]
view plaincopyprint?

bootcmd=nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0

bootcmd=nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0
1、nand 命令

该命令用于进行各种 nand 操作,用法如下:

[cpp]
view plaincopyprint?

nand - NAND sub-system Usage: nand info - show available NAND devices nand device [dev] - show or set current device nand read - addr off|partition size nand write - addr off|partition size read/write 'size' bytes starting at offset 'off' to/from memory address 'addr', skipping bad blocks. nand bad - show bad blocks nand dump[.oob] off - dump page nand scrub - really clean NAND erasing bad blocks (UNSAFE) nand markbad off [...] - mark bad block(s) at offset (UNSAFE) nand biterr off - make a bit error at offset (UNSAFE)

nand - NAND sub-system

Usage:
nand info - show available NAND devices
nand device [dev] - show or set current device
nand read - addr off|partition size
nand write - addr off|partition size
read/write 'size' bytes starting at offset 'off'
to/from memory address 'addr', skipping bad blocks.
nand bad - show bad blocks
nand dump[.oob] off - dump page
nand scrub - really clean NAND erasing bad blocks (UNSAFE)
nand markbad off [...] - mark bad block(s) at offset (UNSAFE)
nand biterr off - make a bit error at offset (UNSAFE)
在启动命令中用到了

[cpp]
view plaincopyprint?

nand read 89807e00 600000 200000;

nand read 89807e00 600000 200000;
即从 nand 偏移为 0x600000 的地方读取长度为 0x200000
字节(2M)的数据到内存 0x89807e00 地址处。对于这几个参数的解释需要了解 内存地址空间 nand 地址空间 是怎么分配的:

[cpp]
view plaincopyprint?

/* we have 1 bank of DRAM */
#define CONFIG_NR_DRAM_BANKS 1
#define MDDR_BASE_ADDR 0x88000000 /* 物理内存的起始地址 */

#define CONFIG_SYS_MALLOC_END (MDDR_BASE_ADDR + 0x00600000) /* MALLOC 区结束地址 */

#define CONFIG_SYS_MALLOC_LEN 0x00100000 /* MALLOC 区长度 1MB */

/* u-boot run address */
#define CONFIG_SYS_UBOOT_BASE (MDDR_BASE_ADDR + 0x00008000) /* uboot 加载的起始地址 */

#define CONFIG_UBOOT_LADDR 0x88007e00

/* default load address for reading (i.e. kernel zImage with header) */
#define CONFIG_SYS_LOAD_ADDR (MDDR_BASE_ADDR + 0x00807e00) /* kernel 加载的起始地址 */

#define CONFIG_SYS_KERN_ADDR (MDDR_BASE_ADDR + 0x00808000) /* kernel 入口地址 */

#define CONFIG_ATAG_ADDR (MDDR_BASE_ADDR + 0x01800100) /* kernel 启动参数地址 */

/* xloader 存储的起始地址为 0,长度为 2M */
#define CONFIG_XLOADER_OFFSET 0x00000000 /* 0M ~ 2M */

#define CONFIG_XLOADER_MSIZE 0x00200000

/* uboot 存储的起始地址为 2M,长度为 2M */
#define CONFIG_UBOOT_OFFSET 0x00200000 /* 2M ~ 4M */

#define CONFIG_UBOOT_MSIZE 0x00200000

/* 环境变量存储的起始地址为 4M,默认长度为 8k */
#define CONFIG_ENV_OFFSET 0x00400000 /* 4M ~ 6M */

#define CONFIG_ENV_SIZE 8192 /* 8KB */

#define CONFIG_ENV_RANGE 0x00200000

/* kernel 存储的起始地址为 6M,长度为 3M */
#define CONFIG_KERNEL_OFFSET 0x00600000 /* 6M ~ 9M */

#define CONFIG_KERNEL_MSIZE 0x00300000

/* we have 1 bank of DRAM */
#define CONFIG_NR_DRAM_BANKS            1
#define MDDR_BASE_ADDR                  0x88000000      /* 物理内存的起始地址 */

#define CONFIG_SYS_MALLOC_END           (MDDR_BASE_ADDR + 0x00600000)   /* MALLOC 区结束地址 */
#define CONFIG_SYS_MALLOC_LEN           0x00100000                      /* MALLOC 区长度 1MB */

/* u-boot run address */
#define	CONFIG_SYS_UBOOT_BASE           (MDDR_BASE_ADDR + 0x00008000)   /* uboot 加载的起始地址 */
#define	CONFIG_UBOOT_LADDR              0x88007e00

/* default load address for reading (i.e. kernel zImage with header) */
#define CONFIG_SYS_LOAD_ADDR            (MDDR_BASE_ADDR + 0x00807e00)   /* kernel 加载的起始地址 */
#define CONFIG_SYS_KERN_ADDR            (MDDR_BASE_ADDR + 0x00808000)   /* kernel 入口地址 */
#define CONFIG_ATAG_ADDR                (MDDR_BASE_ADDR + 0x01800100)   /* kernel 启动参数地址 */

/* xloader 存储的起始地址为 0,长度为 2M */
#define	CONFIG_XLOADER_OFFSET           0x00000000    /* 0M ~ 2M */
#define	CONFIG_XLOADER_MSIZE            0x00200000

/* uboot 存储的起始地址为 2M,长度为 2M */
#define	CONFIG_UBOOT_OFFSET             0x00200000    /* 2M ~ 4M */
#define	CONFIG_UBOOT_MSIZE              0x00200000

/* 环境变量存储的起始地址为 4M,默认长度为 8k */
#define CONFIG_ENV_OFFSET               0x00400000    /* 4M ~ 6M */
#define CONFIG_ENV_SIZE                 8192          /* 8KB */
#define CONFIG_ENV_RANGE                0x00200000

/* kernel 存储的起始地址为 6M,长度为 3M */
#define	CONFIG_KERNEL_OFFSET            0x00600000    /* 6M ~ 9M */
#define	CONFIG_KERNEL_MSIZE             0x00300000
在烧录镜像到 nand 的时候程序会按照上表将 xloader、uboot、kernel 分别烧录到其对应空间,这段 nand 空间是以 1M 为单位对齐的。

2、hdcvt 命令

自动启动中用到的第二个命令是:

[cpp]
view plaincopyprint?

hdcvt 89807e00

hdcvt 89807e00
该命令将位于内存 0x89807e00 的平台镜像头转换为标准的镜像头,用法介绍如下:

[cpp]
view plaincopyprint?

Usage: hdcvt usage: hdcvt addr_from [addr_to] [body_addr]

Usage:
hdcvt usage: hdcvt addr_from [addr_to] [body_addr]
生成的标准镜像头存储起始地址为:addr_to = addr_from - sizeof(xx_image_header_t) - sizeof(image_header_t)。这里涉及到两个新的结构体struct xx_image_headerstruct image_header:前者是平台的镜像头,后者为标准镜像头,定义如下:

[cpp]
view plaincopyprint?

/* 平台镜像头,长度512Byte,填充在kernel的加载地址与入口地址之间的区域:0x89807e00 ~ 0x89808000 */
typedef struct xx_image_header {
unsigned char iv[IV_SIZE];
unsigned int boot_signature;
unsigned int load_address; /* 加载地址 = 镜像入口 - 标准镜像头长度(64Byte) */
unsigned int run_address; /* 镜像入口 */
unsigned int firm_size; /* 镜像长度 */
unsigned int nand_offset;
unsigned int image_type; /* 镜像类型 */
unsigned char board_name[16];
unsigned char reserved[40];
unsigned char certificate[CERT_SIZE];
unsigned char signature[SIGE_SIZE];
} xx_image_header_t;

/* 标准镜像头,长度64Byte,填充在kernel入口地址之前:0x89807fc0 ~ 0x89808000 */
typedef struct image_header {
uint32_t ih_magic; /* 魔数,用于检测是否存在镜像头*/
uint32_t ih_hcrc; /* 镜像头校验和*/
uint32_t ih_time; /* 镜像创建时间 */
uint32_t ih_size; /* 镜像大小 */
uint32_t ih_load; /* 镜像加载地址 */
uint32_t ih_ep; /* 镜像入口地址 */
uint32_t ih_dcrc; /* 镜像校验和 */
uint8_t ih_os; /* 系统类型 */
uint8_t ih_arch; /* 处理器类型 */
uint8_t ih_type; /* 镜像类型 */
uint8_t ih_comp; /* 压缩类型 */
uint8_t ih_name[IH_NMLEN]; /* 镜像名称 */
} image_header_t;

/* 平台镜像头,长度512Byte,填充在kernel的加载地址与入口地址之间的区域:0x89807e00 ~ 0x89808000 */
typedef struct xx_image_header {
unsigned char		iv[IV_SIZE];
unsigned int		boot_signature;
unsigned int		load_address;             /* 加载地址 = 镜像入口 - 标准镜像头长度(64Byte) */
unsigned int		run_address;              /* 镜像入口 */
unsigned int		firm_size;                /* 镜像长度 */
unsigned int		nand_offset;
unsigned int		image_type;               /* 镜像类型 */
unsigned char		board_name[16];
unsigned char		reserved[40];
unsigned char 		certificate[CERT_SIZE];
unsigned char 		signature[SIGE_SIZE];
} xx_image_header_t;

/* 标准镜像头,长度64Byte,填充在kernel入口地址之前:0x89807fc0 ~ 0x89808000 */
typedef struct image_header {
uint32_t	ih_magic;	/* 魔数,用于检测是否存在镜像头*/
uint32_t	ih_hcrc;	/* 镜像头校验和*/
uint32_t	ih_time;	/* 镜像创建时间 */
uint32_t	ih_size;	/* 镜像大小 */
uint32_t	ih_load;	/* 镜像加载地址 */
uint32_t	ih_ep;		/* 镜像入口地址 */
uint32_t	ih_dcrc;	/* 镜像校验和 */
uint8_t		ih_os;		/* 系统类型 */
uint8_t		ih_arch;	/* 处理器类型 */
uint8_t		ih_type;	/* 镜像类型 */
uint8_t		ih_comp;	/* 压缩类型 */
uint8_t		ih_name[IH_NMLEN];	/* 镜像名称 */
} image_header_t;
3、bootm 命令

bootm 用于启动 linux,参数为镜像的加载地址:

[cpp]
view plaincopyprint?

bootm 89807fc0

bootm 89807fc0

可以看到加载地址为 0x89807fc0,这个地址在镜像头中保存,计算方法:ih_load = ih_ep - sizeof(struct image_header) 即:0x89807fc0 = 0x89808000 - 0x40。该命令执行时串口信息如下:

[cpp]
view plaincopyprint?

Booting kernel from Legacy Image at 89807fc0 ...
Image Name: Linux-2.6.32.9
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 1964376 Bytes = 1.9 MB
Load Address: 89807fc0
Entry Point: 89808000
Verifying Checksum ... OK
XIP Kernel Image ... OK
OK

Starting kernel ...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: