前言
start_armboot我们一般认为是uboot的第二阶段,这个结算代码执行都是在ddr中的
相关数据结构
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
这个宏用来定义gd_t类型的gd指针,后面的asm (“r8”)代表指定arm里的r8寄存器存储。gd_t里面存储着很多全局变量供整个uboot使用,里面还有个bd_t存放了开发板的硬件相关的参数,这里用宏定义来声明这个变量,需要用到gd_t的地方就使用宏DECLARE_GLOBAL_DATA_PTR。
这个是在asm-arm/gloal_data.h文件里定义的全局变量gd_t,里面还用到了一个在asm-arm/uboot.h里定义的bd_t。
/*全局配置信息*/
typedefstruct global_data {
bd_t *bd;
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;
phys_size_t ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_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 */
ulong bi_boot_params;/* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];//CONFIG_NR_DRAM_BANKS 2
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)
reloc_init, /* Set the relocation done flag, mustdo this AFTER cpu_init(), but as soonas possible */
#endif
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
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,
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
这个函数指针数组中存放的都是一些板级硬件初始化函数入口地址,最后一个是NULL即void *0,在后续需要执行这些硬件初始化的时候,只需要遍历这个数组就ok,最后一个元素是NULL,这个技巧kernel里面经常会有。
init_sequence函数就是把进行初始化的函数都集中在一起,这里面进行的初始化操作有:
cpu初始化 (实际为空)
板级初始化 网卡预配置,记录机器码和SDRAM信息
时钟初始化
环境变量初始化 这里还没有将环境变量从SD卡迁移到内存,基本都被判定为无效
串口波特率初始化
串口初始化 (实际为空)
控制台初始化第一阶段
打印banner uboot版本信息和日期时间
打印cpu信息 各个时钟域的时钟频率
检查板子信息并打印
初始化i2c (无效,未配置宏)
初始化DRAM 配置有几块DRAM和DRAM大小
打印DRAM配置信息
代码分析
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr; //函数指针数组指针
char *s;
int mmc_exist = 0;
#if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD)
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
ulong gd_base;
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
gd = (gd_t*)gd_base;//计算gd的基地址
/* compiler optimization barrier needed for GCC >= 3.4 */
__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));
monitor_flash_len = _bss_start - _armboot_start;
//板级硬件信息初始化
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifndef CFG_NO_FLASH
/* 配置可用的flash */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* 堆内存初始化*/
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
#if defined(CONFIG_X210)
#if defined(CONFIG_GENERIC_MMC)
puts ("SD/MMC: ");
mmc_exist = mmc_initialize(gd->bd);//初始化mmc
if (mmc_exist != 0)
{
puts ("0 MB\n");
}
#endif
#endif /* CONFIG_X210 */
/* initialize environment */
env_relocate ();
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
#ifdef CONFIG_SERIAL_MULTI
serial_initialize();
#endif
/* IP Address 获取环境变量的 ipaddr*/
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC Address 设置到DM9000中去 */
{
int i;
ulong reg;
char *s, *e;
char tmp[64];
i = getenv_r ("ethaddr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
}
devices_init (); /* get the devices list going. */
jumptable_init ();
#if !defined(CONFIG_SMDK6442)
console_init_r (); /* 真正的初始化控制台的代码fully init console as a device */
#endif
/* enable exceptions */
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
debug ("Reset Ethernet PHY\n");
reset_phy();
#endif
#endif
#if defined(CONFIG_CMD_IDE)
puts("IDE: ");
ide_init();
#endif
/****************显示屏显示logolxg added**************/
#ifdef CONFIG_MPAD
x210_preboot_init();
#endif
/****************end**********************/
/* check menukey to update from sd是否从sd卡更新系统 */
if(check_menu_update_from_sd()==0)//update mode
{
puts ("[LEFT DOWN] update mode\n");
run_command("fdisk -c 0",0);
update_all();
}
else
puts ("[LEFT UP] boot mode\n");
/* main_loop循环,在其中bootdelay延时,并最后do_bootm,或者delay被打断然后进入命令行模式,之后分析cmd实现方式 */
for (;;) {
main_loop ();
}
}