/*
** 对u-boot-1.1.6中 start_armboot()函数的理解。
** 第一阶段全部用汇编语言实现。进入函数start_armboot表示进入u-boot的第二阶段,这个阶段全部用c语言实现。
** 所以在进入第二阶段前,一定要把堆栈设置好。
** 调用过程:start.S -> start_armboot() -> ......
*/
/*
** 注解:定义一个新的函数类型,函数的输入参数类型为void,返回参数类型为Int。
*/
typedef int (init_fnc_t) (void);
int print_cpuinfo (void); /* test-only */
/*
** 注解:定义一个函数指针数组,首先这是一个数组,数组的元素是指针,指针指向的是init_fnc_t函数类型。
** 所以,归纳的话就是定义了一个函数指针数组。数组名init_sequence表示整个数组内存的开始地址,
** init_sequence[0]表示的是数组的第一个元素cpu_init,这样init_sequence[0]()就调用了cpu_init(),
** 或者解引用的方式(*init_sequence)() ,感觉还是数组下标的方式可读性好些,但是这里用的是解引用形式。
*/
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */ //directory:board/smdk2410/sdmk2410.c
interrupt_init, /* set up exceptions */ //cpu/arm920t/s3c24x0/interrupts.c初始化一个定时器.
env_init, /* initialize environment,初始化环境变量,一开始环境放在哪里?如果是nor flash启动就是在nor flash 中 */
init_baudrate, /* initialze baudrate settings 初始化串口波特率*/
serial_init, /* serial communications setup,初始化串口 */ //directory:cpu/arm920t/s3c24x0/serial.c
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
dram_init, /* configure available RAM banks,配置SDRAM的范围 */ //directory:board/smdk2410/sdmk2410.c
display_dram_config,
NULL,
};
void start_armboot (void)
{
/*
** 定义了init_fnc_t类型的函数指针init_fnc_ptr。
*/
init_fnc_t **init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
/* Pointer is writable since we allocated a register for it */
/*
** 其中_armboot_start = TEXT_BASE, 在board/smdk2410/config.mk中定义;
** 其中CFG_MALLOC_LEN 在include/configs/smdk2410.h中定义;
** 划分一块内存,内存的大小为结构体gd_t的大小,内存的开始地址根据_armboot_start,CFG_MALLOC_LEN,
** 和结构体gd_t的长度确定. 然后用指针gd指向这个内存块的开始位置。
*/
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/*
** 阻止编译器优化,GCC >= 3.4以上版本需要这样做。
** memory:强制gcc编译器假设RAM所有内存单元均被汇编指令修改,
** 这样cpu中的registers和cache中已缓存的内存单元中的数据将作废。
** cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpu将
** registers,cache中的数据用于去优化指令,而避免去访问内存。
*/
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
/*
** 初始化内存块清零.
*/
memset ((void*)gd, 0, sizeof (gd_t));
/*
** 划分一块内存,内存的大小为结构体bd_t的大小,内存的开始地址根据gd和结构体bd_t的长度确定.
** 然后用指针gd->bd指向这个内存块的开始位置。
** 初始化这块内存的值为0.
*/
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
/*
** 变量monitor_flash_len表示的就是所谓monitor code 的大小。
** 其实就是u-boot代码relocate到SDRAM的内存大小。
** u-boot镜像大小。
*/
monitor_flash_len = _bss_start - _armboot_start;
/*
** init_fnc_ptr = init_sequence,把数组的开始地址赋值给指针init_fnc_ptr,这样init_fnc_ptr指向了数组的开始地址;
** 那么*init_fnc_ptr表示的就是数组内部的元素,最后一个元素是NULL,退出for循环;
** 解引用(*init_fnc_ptr)()表示的就是调用函数,返回int类型的值,如果为0表示初始化正常,否则调用函数hang()报错。
*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*
** 初始化nor flash。
*/
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */
/* armboot_start is defined in the board-specific linker script */
/*
** 初始化 malloc 内存块,清零。
*/
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/*
** 初始化NAND flash。
*/
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND,drivers/nand/nand.c */
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* initialize environment */
/*
** 把环境变量从flash读取到SDRAM。
*/
env_relocate ();
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
/*
** 这一段初始化网络设备的一些参数,比如:IP地址,MAC地址等。
*/
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* MAC Address */
{
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;
}
#ifdef CONFIG_HAS_ETH1
i = getenv_r ("eth1addr", tmp, sizeof (tmp));
s = (i > 0) ? tmp : NULL;
for (reg = 0; reg < 6; ++reg) {
gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
if (s)
s = (*e) ? e + 1 : e;
}
#endif
}
/*
** 一些外设的初始化,比如I2C,lcd,keyboard,等。
** 根据自己的需要初始化这些外设,大多数情况,u-boot中不会用到这些外设。
** 通过board/smdk2410/smdk2410.h中定义的宏开关这些外设。
*/
devices_init (); /* get the devices list going. */
#ifdef CONFIG_CMC_PU2
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
jumptable_init ();
/*
** 控制台初始化在这里执行。
*/
console_init_r (); /* fully init console as a device */
#if defined(CONFIG_MISC_INIT_R)
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* enable exceptions */
/*
** 使能中断IRQ.
*/
enable_interrupts ();
/* Perform network card initialisation if necessary */
/*
** 初始化网卡外设cs8900
*/
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
if (getenv ("ethaddr")) {
smc_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
#endif /* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}
/*
** u-boot中使用的全局变量,定义在这个结构体。
*/
typedef struct global_data {
bd_t *bd;
unsigned long flags; /* u-boot镜像是否重定位到SDRAM,1-是,0-否。*/
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? 环境变量内存块校验结果,1-正常,0-异常 */
unsigned long fb_base; /* base address of frame buffer,lcd 的帧缓存基地址 */
#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 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 = 193 */
/*
** 参数bi_boot_params是标记列表的基地址0x30000100,标记列表是u-boot传递给kernel的参数
** 在board_init()函数中给这个参数赋值;
** 通过函数指针theKernel(0,***,***)启动内核且把参数传递给kernel.
*/
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;