uboot 在start.S中完成了第一阶段启动后,由汇编语言调用C语言函数start_armboot进入第二阶段的启动,在海思给的Uboot代码中,该函数定义在/u-boot-2010.06/arch/arm/lib/board.c。它主要完成了:
- 全局变量的初始化
- 初始化硬件及软件数据结构
- 初始化uboot堆管理器
- flash驱动初始化
- 环境变量重定向
- 输入输出初始化
- 其它初始化
- uboot命令处理
说明:对于官方默认没有配置的功能都直接跳过,不做详细的介绍。
1.全局变量的初始化
-
void start_armboot (void)
-
{
-
init_fnc_t **init_fnc_ptr;
//①
-
char *s;
-
#ifdef CONFIG_HAS_SLAVE
-
char *e;
-
#endif
-
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
-
unsigned
long addr;
-
#endif
-
-
/* Pointer is writable since we allocated a register for it */
-
gd = (
gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN -
sizeof(
gd_t));
//②
-
/* 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));
-
-
gd->flags |= GD_FLG_RELOC;
//④
-
-
monitor_flash_len = _bss_start - _armboot_start;
//⑤
- ①typedef int (init_fnc_t) (void); 是一个函数,不是函数指针
- 在hi3521a中 CONFIG_HAS_SLAVE,CONFIG_VFD,CONFIG_LCD 都没有初始化
- ②_armboot_start 是在\u-boot-2010.06\arch\arm\cpu\hi3521a\start.S 的汇编语言中被定义,表示uboot代码段在ddr中的开始地址,其值为0x80800000
- ddr可以使用的地址是从80000000开始,也就是说uboot的代码是被拷贝到ddr开始往后的8M空间
- 在这里CONFIG_SYS_MALLOC_LEN=0x60000=(384K)
- gd指向的地址为:0x80800000 - 0x60000 - 0x20 = 0x8079FFE0
- gd->bd指向的地址为:0x8079FFE0 - 0x1c = 0x8079FFC4
- 接下来是将gd和gd->bd指向的内存空间清零。其实gd是一个全局的变量,在uboot中很多地方会使用到,gd->bd是一个板子的信息结构体。详细介绍如下:
- 两个关键结构体介绍:
- ③gd_t 是一个全局的结构体,在uboot的很多地方都有使用它所在的位置是:\u-boot-2010它所在的位置是:\u-boot-2010.06\arch\arm\include\asm\global_data.h
结构体的内容为:
-
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_VFD
-
unsigned
char vfd_type;
/* display type */
-
#endif
-
#ifdef CONFIG_FSL_ESDHC
-
unsigned
long sdhc_clk;
-
#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 */
-
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;
- 定义波特率,IP地址,环境变量指针,机器码,启动参数,内存配置。
- 机器码:机器码是设备的唯一编号,在Uboot和kernel中都有软件维护这个机器码,如果机器码不匹配将不会被启动
- 启动参数:这里是uboot传递给kernel启动参数的内存地址。uboot传参数到kernel的过程是这样的:uboot将事先准备好的kernel启动参数存放到bi_boot_params指向的这个地址(实际上是将bi_boot_params这个值存放到了r0 r1 r2寄存器中去),kernel起来后,去读取寄存器r0,r1,r2中的值,得到bi_boot_params这个参数的值,也就是启动参数的地址,然后再去这个地址将内核中的启动参数读取出来。
- 内存配置:CONFIG_NR_DRAM_BANKS是在\u-boot-2010.06\include\configs\hi3521.h中定义,表示ddr的个数,海思系列的这个值都是1,表示只有一个ddr;在bi_dram中定义了ddr的开始地址和长度
- ④GD_FLG_RELOC;是全局变量的一个状态,表示为代码已经从flash中拷贝到了内存中去。它定义在:\u-boot-2010.06\arch\arm\include\asm\global_data.h
- ⑤_bss_start是bss的开始地址,_armboot_start是Uboot的开始代码地址,这里monitor_flash_len得到的是整个Uboot的长度
2.初始化硬件及软件数据结构
-
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
-
if ((*init_fnc_ptr)() !=
0) {
-
hang ();
-
}
-
}
- init_sequence 是一个函数指针数组,里面存了很多的函数指针,init_fnc_ptr是一个二重指针,*init_fnc_ptr表示init_sequence中的函数指针指向的地址,
- 如果为空NULL,结束for循环。init_sequence函数指针里面定义了各种各样的函数初始化函数
-
init_fnc_t *init_sequence[] = {
-
#if defined(CONFIG_ARCH_CPU_INIT)
-
arch_cpu_init, /* basic arch cpu dependent setup */
-
#endif
-
timer_init, /* initialize timer before usb init */
-
board_init, /* basic board dependent setup */ //①
-
#if defined(CONFIG_USE_IRQ)
-
interrupt_init, /*
set up exceptions */
-
#endif
-
// timer_init, /* initialize timer */
-
#ifdef CONFIG_FSL_ESDHC
-
get_clocks,
-
#endif
-
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 */ //⑦
-
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
-
arm_pci_init,
-
#endif
-
/* display_dram_config */
-
NULL,
-
};
- 这里面的所有编译宏在海思设备中都没有定义,没有使用到
- 实际使用到的是
- timer_init:
- 时钟初始化,定义在\u-boot-2010.06\arch\arm\cpu\hi3521a\hi3521a\timer.c
- 时钟参数的配置在y\u-boot-2010.06\include\configs\hi3521a.h
①board_init:(这里很重要)
-
int board_init(void)
-
{
-
unsigned
long reg;
-
/* set uart clk from XTAL OSC 24M */
-
reg = readl(CRG_REG_BASE + PERI_CRG33);
-
reg &= ~UART_CKSEL_MASK;
-
reg |= UART_CKSEL_24M;
-
writel(reg, CRG_REG_BASE + PERI_CRG33);
-
-
DECLARE_GLOBAL_DATA_PTR;
-
-
gd->bd->bi_arch_number = MACH_TYPE_HI3521A;
-
gd->bd->bi_boot_params = CFG_BOOT_PARAMS;
-
gd->flags =
0;
-
-
boot_flag_init();
-
-
return
0;
-
}
- 开发板的一些参数定义,
- 该函数定义定义在:\u-boot-2010.06\board\hi3521a\board.c
- UART时钟频率设置
- DECLARE_GLOBAL_DATA_PTR宏申明了全局变量 register volatile gd_t *gd asm ("r8")这里全局变量名字叫gd,这个全局变量是一个指针类型,占4字节。用volatile修饰表示可变的,用register修饰表示这个变量要尽量放到寄存器中,后面的asm("r8")是gcc支持的一种语法,意思就是要把gd放到寄存器r8中。
- MACH_TYPE_HI3521A是设备的机器码,值为8000,海思所有的设备都是这个值
- CFG_BOOT_PARAMSuboot传递给kernel的启动参数存放的地址值为:MEM_BASE_DDR=DDR_MEM_BASE=0x80000000;CFG_BOOT_PARAMS=MEM_BASE_DDR+0x0100=0x80000100
boot_flag_init()启动类型判断
-
void boot_flag_init(void)
-
{
-
unsigned
int regval, device_type;
-
-
/* get boot device type */
-
regval = __raw_readl(SYS_CTRL_REG_BASE + REG_SYSSTAT);
-
device_type = (regval >>
8) &
0x1;
-
-
switch (device_type) {
-
/* spi nor device */
-
case
0:
-
boot_media = BOOT_MEDIA_SPIFLASH;
-
break;
-
/* spi nand device */
-
case
1:
-
boot_media = BOOT_MEDIA_NAND;
-
break;
-
default:
-
boot_media = BOOT_MEDIA_SPIFLASH;
-
break;
-
}
-
}
- SYS_CTRL_REG_BASE=0x12050000
- REG_SYSSTAT=0x008C
- SYSSTAT 为系统状态寄存器,它的第8位表示SPI FLASH 器件类型
- 从这里可以判断外接的是spi nandflash 还是spi norflash
- 如果是spi nandflash 将它标记为nandflash启动
- 可以使用get_boot_media函数来获取外接的flash启动类型
②env_init
- 定义在\u-boot-2010.06\common\env_common_func.c
- 针对不同的存储介质设置环境变量的操作接口
③init_baudrate
- 波特率设置,先去环境变量中查询是否有设置波特率,
- 如果没有设置则使用配置文件中的CONFIG_BAUDRATE波特率
- 海思的串口设置在\u-boot-2010.06\drivers\serial\serial_pl01x.c文件
④serial_init
- 海思hi3521a在配置文件中有配置宏CONFIG_PL011_SERIAL,所以串口的初始化函数实际调用的是\u-boot-2010.06\drivers\serial\serial_pl01x.c中的函数
⑤console_init_f
- 定义使用端口,_f表示是第一阶段的初始化
⑥display_banner
- 在终端打印出uboot的版本号,代码段的开始位置,ssb的开始和结束位置
- code: 80800000 -> BSS: -> 8089EE00
- _armboot_start = 80800000
- _bss_start = 0x8084A384
- _bss_end = 0x8089EE00
⑦dram_init
- 定义在\u-boot-2010.06\board\hi3521a\board.c
- 配置ddr的个数和地址
- CFG_DDR_PHYS_OFFSET 的物理地址
- CFG_DDR_SIZE ddr的大小默认大小为:512 * 1024 * 1024UL 512Mb=64MB
3.初始化uboot堆管理器
-
/* armboot_start is defined
in the board-specific linker script */
-
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
-
CONFIG_SYS_MALLOC_LEN);
- 堆空间的分配:
- _armboot_start=80800000;
- CONFIG_ENV_SIZE=0x40000
- CONFIG_SYS_MALLOC_LEN=CONFIG_ENV_SIZE + 128*1024= 256K+128K = 384K
- mem_malloc_start=80800000-384K
- mem_malloc_end = 80800000
- mem_malloc_brk = 80800000
4.flash驱动初始化
-
#ifdef CONFIG_CMD_SF
-
#if (defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556)
-
if(get_boot_media() == BOOT_MEDIA_SPIFLASH) {
-
#endif
-
spi_flash_probe(
0,
0,
0,
0);
//①
-
#if (defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556)
-
}
-
#endif
-
#endif
-
-
/* it is not needed in A7 in A17-A7 */
-
#ifdef CONFIG_CMD_NAND
-
#if (defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556)
-
if(get_boot_media() == BOOT_MEDIA_NAND) {
-
#endif
-
nand_init();
/* go init the NAND */
//②
-
#if (defined CONFIG_ARCH_HI3559 || defined CONFIG_ARCH_HI3556)
-
}
-
#endif
-
#endif
- ①spi_flash_probe函数定义在\u-boot-2010.06\drivers\mtd\spi\spi_compatible.c
- 在海思设备中,配置的是CONFIG_HIFMC_SPI_NOR 和CONFIG_HIFMC_SPI_NAND
- 所以在这里调用的是hifmc100_spi_nor_probe(&spiinfo_ex)这个函数
- 从终端可以打印出我设备的一些信息:
-
Check Flash Memory Controller v100 ... Found
-
SPI Nor(cs 0) ID: 0xc2 0x20 0x19
-
Block:64KB Chip:32MB Name:
"MX25L(256/257)XX"
-
SPI Nor total size: 32MB
- ②nand_init()函数,定义在\u-boot-2010.06\drivers\mtd\nand\nand.c
- 进行SPI Nand flash 初始化,我设备上的spi nand flash 信息如下:
-
SPI Nand(cs 1) ID: 0xc2 0x12 Name:
"MX35LF1GE4AB"
-
Block:128KB Page:2KB Chip:128MB*1 OOB:64B ECC:4bit/512
-
ECC provided by Flash Memory Controller
-
SPI Nand total size: 128MB
5.环境变量重定向
env_relocate
- 环境变量重定向,关于环境变量,可以参考博客《uboot环境变量》,这里主要实现的功能是将环境变量从flash中复制到DDR中去,后面环境变量的操作都是在内存中操作,直到使用了saveenv才会将内存中的环境变量写回到flash中实现环境变量的更新。
6.输入输出初始化
-
#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 */
-
gd->bd->bi_ip_addr = getenv_IPaddr (
"ipaddr");
//①
-
-
stdio_init ();
/* get the devices list going. */
//②
-
-
jumptable_init ();
//③
-
-
#if defined(CONFIG_API)
-
/* Initialize API */
-
api_init ();
-
#endif
-
-
console_init_r ();
/* fully init console as a device */
//④
-
-
#if defined(CONFIG_ARCH_MISC_INIT)
-
/* miscellaneous arch dependent initialisations */
-
arch_misc_init ();
-
#endif
-
#if defined(CONFIG_MISC_INIT_R)
-
/* miscellaneous platform dependent initialisations */
-
misc_init_r ();
//⑤
-
#endif
-
-
/* enable exceptions */
-
enable_interrupts ();
//⑥
- ①从环境变量中提取IP地址信息
- ②初始化标准输入输出"stdin", "stdout", "stderr"
- ③跳转表初始化,没有查到有哪里会使用
- ④终端设备的第二阶段初始化
- ⑤杂项初始化,这里其它的功能都没有定义,只是设置了一个环境变量verify,设它的值为n
- ⑥中断初始化,这个函数定义在\test\uboot_study\u-boot-2010.06\arch\arm\lib\interrupts.c ,在海思的uboot中CONFIG_USE_IRQ宏没有被定义,所以中断在整个Uboot过程中都是关闭的,enable_interrupts函数的实现是是个空函数。
7.其它初始化
-
/* Perform network card initialisation if necessary */
-
#ifdef CONFIG_DRIVER_TI_EMAC
-
/* XXX: this needs to be moved to board init */
-
extern void davinci_eth_set_mac_addr (const u_int8_t *addr);
-
if (getenv (
"ethaddr")) {
-
uchar enetaddr[
6];
-
eth_getenv_enetaddr(
"ethaddr", enetaddr);
-
davinci_eth_set_mac_addr(enetaddr);
-
}
-
#endif
-
-
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
-
/* XXX: this needs to be moved to board init */
-
if (getenv (
"ethaddr")) {
//①
-
uchar enetaddr[
6];
-
eth_getenv_enetaddr(
"ethaddr", enetaddr);
-
smc_set_mac_addr(enetaddr);
-
}
-
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
-
-
/* 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
-
-
#ifdef CONFIG_BITBANGMII
-
bb_miiphy_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_BOOTROM_SUPPORT) && (!defined(CONFIG_ARCH_HI3559) && !defined(CONFIG_ARCH_HI3556))
-
extern void download_boot(const int (*handle)(void));
-
download_boot(
NULL);
-
#endif
-
#ifdef CONFIG_HAS_SLAVE
-
e = getenv(
"slave_autostart");
-
if (e && (*e ==
'1'))
-
slave_start();
-
#endif
-
-
#ifdef CONFIG_DDR_TRAINING_V300
-
check_ddr_training();
-
#endif /* CONFIG_DDR_TRAINING_V300 */
-
product_control();
//⑤
-
-
#ifdef CONFIG_SNAPSHOT_BOOT
-
/* example */
-
/* set_mtdparts_info("mtdparts=hinand:1M(boot),3M(kernel),64M(rootfs),2M(param),16M(hibernate)"); */
-
-
extern void comp_save_snapshot_image(void);
-
comp_save_snapshot_image();
-
#endif
- ①从环境变量中获取网卡的MAC地址ethaddr,然后将它解析
- ②loadaddr这个环境变量默认是没有被定义
- ③bootfile是指启动kernel文件的类型,在海思设备,默认是设置为uImage。
- ④初始化网卡设备
- ⑤这个是对ddr做等长匹配,对于hi3251a,这个工作在第一阶段的start.S文件中就已近做过了,在实际上这个函数相当于空。
8.uboot命令处理
-
/* main_loop() can return to retry autoboot, if so just run it again. */
-
for (;;) {
-
main_loop ();
-
}
在这里的最后一步就是进入了一个死循环main_loop(),等待用户命令的输入。如果uboot超时没有接收到用户输入命令,则执行环境变量bootcmd中的命令,使用bootcmd环境变量中的命令来引导内核的启动。关于uboot命令解析,可以查看博客《bootm启动命令解析》,其它uboot内容可以查看博客《序言与目录》。
转载:https://blog.csdn.net/li_wen01/article/details/103347598
查看评论