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;
这两个类型变量记录了刚启动时的信息,还将记录作为引导内核和文件系统的参数,如 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 */
可以看到 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 (;;);
}
在我们平台比较重要的初始化函数有 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); }
在环境变量 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"
};
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 */
}
[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); }
[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; } } }
附:启动命令解析
在 uboot 进入主循环后默认会进入 nand 启动模式,会依次执行 3 个命令:
[cpp]
view plaincopyprint?
bootcmd=nand read 89807e00 600000 200000; hdcvt 89807e00; bootm 89807fc0
该命令用于进行各种 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)
[cpp]
view plaincopyprint?
nand read 89807e00 600000 200000;
字节(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
2、hdcvt 命令
自动启动中用到的第二个命令是:
[cpp]
view plaincopyprint?
hdcvt 89807e00
[cpp]
view plaincopyprint?
Usage: hdcvt usage: hdcvt addr_from [addr_to] [body_addr]
[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;
bootm 用于启动 linux,参数为镜像的加载地址:
[cpp]
view plaincopyprint?
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 ...
一、基础数据结构
第二阶段主要用到了两个数据结构即 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 89807fc01、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_header 和 struct 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 ...
相关文章推荐
- u-boot第二阶段启动流程分析
- (四) u-boot 启动分析_第二阶段
- u-boot源码分析 --- 启动第二阶段004
- U-Boot启动第二阶段代码分析
- u-boot启动第二阶段以及界面命令分析
- u-boot源码分析 --- 启动第二阶段005
- u-boot2013.01 smdk2410 启动第二阶段分析
- U-Boot启动第二阶段代码分析
- u-boot源码分析 --- 启动第二阶段006
- U-Boot启动第二阶段代码分析
- u-boot源码分析 --- 启动第二阶段001
- u-boot源码分析 --- 启动第二阶段 ,基于2410 启动代码 分析
- u-boot源码分析 --- 启动第二阶段002
- uboot 启动流程分析(二) — 第二阶段
- u-boot源码分析 --- 启动第二阶段005
- u-boot源码分析 --- 启动第二阶段007
- u-boot源码分析 --- 启动第二阶段006
- u-boot启动过程源码分析之第二阶段(S3C24XX系列)
- U-Boot 启动过程和源码分析(第二阶段)-main_loop分析
- u-boot源码分析 --- 启动第二阶段007