先了解个大体流程 :
开始
在上一篇文章中,我
们介绍了u-boot启动的时候汇编语言的部分,当时我们进行了一些简单的初始化,并且为C语言的执行建立的环境(堆栈),现在我们看看当从汇编语言转到C语言的时候执行的第一个函数( start_armboot (),在lib_arm\board.c中),该函数进行了一系列的外设初始化,然后调用main_loop (),根据配置来选择是直接加载Linux内核还是进入等待命令模式。
1、在介绍start_armboot ()函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构:
1)bd_t 结构体:保存与板子相关的配置参数
bd_t在include/asm-arm/u-boot.h中定义如下:
********************************************************************
* NOTE: This header file defines an interface to U-Boot. Including
* this (unmodified) header file in another file is considered normal
* use of U-Boot, and does *not* fall under the heading of "derived
* work".
********************************************************************
*/
#ifndef _U_BOOT_H_
#define _U_BOOT_H_ 1
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate 串口通讯波特率 */
unsigned long bi_ip_addr; /* IP Address IP地址 */
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 RAM配置信息 */
{
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;
#define bi_env_data bi_env->data
#define bi_env_crc bi_env->crc
#endif /* _U_BOOT_H_ */
2)gd_t 结构体:U-Boot使用了一个结构体gd_t来存储全局数据区的数据,
这个结构体在include/asm-arm/global_data.h中定义如下:
#ifndef __ASM_GBL_DATA_H
#define __ASM_GBL_DATA_H
/*
* The following data structure is placed in some memory wich is
* available very early after boot (like DPRAM on MPC8xx/MPC82xx, or
* some locked parts of the data cache) to allow for a minimum set of
* global variables during system initialization (until we have set
* up the memory controller so that we can use RAM).
*
* Keep it *SMALL* and remember to set CFG_GBL_DATA_SIZE > sizeof(gd_t)
*/
typedef struct global_data {
bd_t *bd; //board data pointor板子数据指针
unsigned long flags; //指示标志,如设备已经初始化标志等。
unsigned long baudrate; //串口波特率
unsigned long have_console; / *serial_init() was called /* 串口初始化标志*/
unsigned long reloc_off; /* Relocation Offset */ /* 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0
unsigned long env_addr; /* Address of Environment struct */ /* 环境参数地址*/
unsigned long env_valid; /* Checksum of Environment valid? */ /* 环境参数CRC检验有效标志 */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD //我们一般没有配置这个,这个是frame buffer的首地址
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 */ /* 跳转表,1.1.6中用来函数调用地址登记 */
} gd_t;
/*
* Global Data Flags
*/
#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */
#define GD_FLG_DEVINIT 0x00002 /* Devices have been initialized */
#define GD_FLG_SILENT 0x00004 /* Silent mode */
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
#endif /* __ASM_GBL_DATA_
看点:
1. 在global_data.h中U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址
2. #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。
这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。
3. 根据U-Boot内存使用图中可以计算gd的值:
gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)
u-boot重定位后的内存分布:
对于smdk2410,RAM范围从0x30000000~0x34000000. u-boot占用高端内存区。从高地址到低地址内存分配如下:
显示缓冲区 (.bss_end~34000000)
u-boot(bss,data,text) (33f00000~.bss_end)
heap(for malloc)
gd(global data)
bd(board data)
stack
....
nor flash (0~2M)
3). init_sequence数组, 初始化函数列表(以数组的形式)
相关代码在lib-arm/board.c中定义
typedef int (init_fnc_t) (void); /*定义一个函数指针类型*/init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup CPU的相关配置,如初始化IRQ/FIQ模式的栈cpu/arm920t/cpu.c */
board_init, /* basic board dependent setup开发板相关配置,是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,
还设置了U-Boot机器码和内核启动参数地址,它是开发板相关的函数,
比如2410是在:board/smdk2410/smdk2410.c中实现 */
interrupt_init, /* set up exceptions 初始化中断,在cpu/arm920t/s3c24x0/interrupts.c实现 */
env_init, /* initialize environment 初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c*/
init_baudrate, /* initialze baudrate settings 初始化波特率lib_arm/board.c*/
serial_init, /* serial communications setup 串口初始化串口初始化后我们就可以打印信息了cpu/arm920t/s3c24x0/serial.c*/
console_init_f, /* stage 1 init of console 控制台初始化第一阶段common/console.c
*/
display_banner, /* say that we are here 通知代码已经运行到该处,打印U-Boot版本、编译的时间-- lib_arm/board.c */
#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 配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小board/smdk2410/smdk2410.c */
display_dram_config, /*显示RAM大小--lib_arm/board.c*/
NULL,
};
4)环境变量指针 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义在common/environment.c中。
参数解释:
bootdelay 定义执行自动启动的等候秒数
baudrate 定义串口控制台的波特率
netmask 定义以太网接口的掩码
ethaddr 定义以太网接口的MAC地址
bootfile 定义缺省的下载文件
bootargs 定义传递给Linux内核的命令行参数
bootcmd 定义自动启动时执行的几条命令
serverip 定义tftp服务器端的IP地址
ipaddr 定义本地的IP地址
stdin 定义标准输入设备,一般是串口
stdout 定义标准输出设备,一般是串口
stderr 定义标准出错信息输出设备,一般是串口
4)设备相关:
标准IO设备数组evice_t *stdio_devices[] = { NULL, NULL, NULL };
设备列表 list_t devlist = 0;
device_t的定义:include/devices.h中:
typedef struct {
int flags; /* Device flags: input/output/system */
int ext; /* Supported extensions */
char name[16]; /* Device name */
/* GENERAL functions */
int (*start) (void); /* To start the device */
int (*stop) (void); /* To stop the device */
/* 输出函数 */
void (*putc) (const char c); /* To put a char */
void (*puts) (const char *s); /* To put a string (accelerator) */
/* 输入函数 */
int (*tstc) (void); /* To test if a char is ready... */
int (*getc) (void); /* To get that char */
/* Other functions */
void *priv; /* Private extensions */
} device_t;
u-boot把可以用为控制台输入输出的设备添加到设备列表devlist,并把当前用作标准IO的设备指针加入stdio_devices数组中。
在调用标准IO函数如printf()时将调用stdio_devices数组对应设备的IO函数如putc()。
5)命令相关的数据结构,后面介绍。
6)与具体设备有关的数据结构,
如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];记录nor flash的信息。
nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash块设备信息
上边的函数我们会在后面详细解释,下面看start_armboot 函数的框架
2. start_armboot ()函数详细解释:在lib_arm\board.c中
void start_armboot (void)
{
init_fnc_t **init_fnc_ptr; /*它是指向指针的指针变量,指针变量的类型为init_fnc_t,这是一个使用typedef定义的函数类型, 其中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 */
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); /*_armboot_start: .word _start 计算全局数据结构的地址gd(gd是一个全局静态变量)*/
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
memset ((void*)gd, 0, sizeof (gd_t)); //初始化全局数据区数据结构为0 gd在哪里定义的 还有gd是个结构体变量啊??
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //为bd_t分配空间,并赋值到gd
memset (gd->bd, 0, sizeof (bd_t)); //初始化 bd 表为0;
monitor_flash_len = _bss_start - _armboot_start; /*monitor_falsh_len定义在 /lib_arm/Board.c _bss_start(BSS段基址) = 代码段长度+数据段长度*/
/*下面是 逐个调用init_sequence数组中的初始化函数,具体函数分析在后面,现只是看框架 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang (); //函数功能是打印提示### ERROR ### Please RESET the board ###,然后就无限循环,等待重启
}
}
/* CFG_NO_FLASH 表示没有flash,如果没定义该常量则表示板子上有flash,此时调用flash_init()对其进行初始化.这里是nor flash初始化 */
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init (); //详见后分析
display_flash_config (size); ////打印flash的信息,仅是其大小
#endif /* CFG_NO_FLASH */
#ifdef CONFIG_VFD //smdk2410没定义
# 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) //为LCD分配RAM(内存)空间 */
/* 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 */
#endif /* CONFIG_LCD */
/* 初始化堆空间 */
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //malloc函数使用缓冲区的初始化
//_ armboot_start=TEXT_BASE,,mem_malloc_init对这段存储区清零
#if (CONFIG_COMMANDS & CFG_CMD_NAND) //如果定义了命令和NAND命令,则初始化nand
puts ("NAND: ");
nand_init(); /* go init the NAND /*探测NAND flash并根据NAND flash的类型填充相应的数据结构,全局结构体变量nand_dev_desc[0](类型struct nand_chip) */
#endif
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
/* 重新定位环境变量, */
/* initialize environment配置环境变量,重新定位,代码在common\env_common.c中 */
env_relocate ();
#ifdef CONFIG_VFD
/* must do this after the framebuffer is allocated */
drv_vfd_init();
#endif /* CONFIG_VFD */
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); //从环境变量中获取IP地址 , ipaddr在smdk2440.h中的CONFIG_IPADDR中出现,应该是该常量
/* MAC Address 从环境变量获取网卡的MAC地址并填充gd结构体变量 */
{
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
}
/* 设备初始化 */
devices_init (); /* get the devices list going. 对设备初始化,在common/devices.c文件中,会根据配置来完成各个设备的初始化,
其中会调用函数drv_system_init(),这个 函数对串口设备初始化,然后注册串口设备*/
#ifdef CONFIG_CMC_PU2 //没定义
load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
//跳转表初始化
jumptable_init (); //跳转表初始化,函数在common\exports.c文件中 对gd中的jt(函数跳转表)数组进行初始化,其中保存着一些函数的入口地址
/* 完整地初始化控制台设备 */
console_init_r (); /* fully init console as a device 函数在common\console.c */
#if defined(CONFIG_MISC_INIT_R) //没有定义
/* miscellaneous platform dependent initialisations */
misc_init_r ();
#endif
/* 使能中断处理 */
/* enable exceptions 使能中断,cpu/arm920t/interrupts.c smdk2410是个空函数void enable_interrupts (void){return;}*/
enable_interrupts ();
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900 /*smdk2410没定义*/
cs8900_get_enetaddr (gd->bd->bi_enetaddr); //获取网卡的MAC地址
#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); // 读取环境变量loadaddr到全局变量load_addr中
}
#if (CONFIG_COMMANDS & CFG_CMD_NET)
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile)); //读取环境变量bootfile到全局变量BootFile中
}
#endif /* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT //没定义
board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI) // smdk2410没定义
puts ("Net: ");
#endif
eth_initialize(gd->bd);
#endif
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop (); //直接进入main_loop 该函数在common\main.c中,至此,硬件初始化完成
}
/* NOTREACHED - no way out of command loop except booting */
} //end start_armboot()
3. init_sequence数组中各个函数的具体分析
cpu_init :
功能:初始化IRQ/FIQ模式的栈
位置:cpu/arm920t/cpu.c */
int cpu_init (void)
{
/*
* setup up stacks if necessary
*/
#ifdef CONFIG_USE_IRQ
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0;
}
board_init
功能:是对板子的初始化,设置MPLL,改变系统时钟,以及一些GPIO寄存器的值,使能I/D cache,还设置了U-Boot机器码和内核启动参数地址,它是开发板相关的函数
位置:board/smdk2410/smdk2410.c
int board_init (void)//获取power和clock及GPIO方面的寄存器地址,稍后的操作会对这些寄存器操作,需要看到的是,象S3C24X0_CLOCK_POWER里面的field对象都是按照实际寄存器的地址来安排的
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
//降低PLL的lock time的值
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
//配置MPLL UPLL
/* configure MPLL */
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
/* some delay between MPLL and UPLL */
delay (4000);
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
/* some delay between MPLL and UPLL */
delay (8000);
/* 配置每个GPIO的功能,输入输出,等参数 */
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
/* SMDK2410开发板的机器码即板子的ID,没啥意义 */ */
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
/* 内核启动参数地址,运行时在linux内核之下/
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
//使能指令cache和数据cache
icache_enable();
dcache_enable();
return 0;
}
interrupt_init
功能:初始化2410的PWM timer 4,使其能自动装载计数值用于产生10ms定时中断,但是中断被屏蔽了用不上
位置:cpu/arm920t/s3c24x0/interrupts.c
int interrupt_init (void)
{
S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS(); //返回定时器配置寄存器地址0x51000000,即TCFG0寄存器地址
/*这里使用定时器4,定时器4有只有一个内部定器没有输出管脚*/
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00;
if (timer_load_val == 0)
{
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK()/(2 * 16 * 100); //PCLK(返回PCLK频率
}
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val; //设置计数缓存寄存器初始值
/*设置定时器4手动更新,自动加载模式,并关闭定时器4*/
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
/* 启动Timer 4 */
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
timestamp = 0;
return (0);
}
env_init
位置:common/env_flash.c 或common/env_nand.c
功能:指定环境区的地址 初始化环境变量,检查flash上的环境变量是否有效common/env_flash.c 或common/env_nand.c
int env_init(void)
{
int crc1_ok = 0, crc2_ok = 0;
uchar flag1 = flash_addr->flags;
uchar flag2 = flash_addr_new->flags;
ulong addr_default = (ulong)&default_environment[0]; //default_environment是默认的环境参数设置
ulong addr1 = (ulong)&(flash_addr->data);
ulong addr2 = (ulong)&(flash_addr_new->data);
#ifdef CONFIG_OMAP2420H4
int flash_probe(void);
if(flash_probe() == 0)
goto bad_flash;
#endif
crc1_ok = (crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc);
crc2_ok = (crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc);
if (crc1_ok && ! crc2_ok) {
gd->env_addr = addr1;
gd->env_valid = 1;
} else if (! crc1_ok && crc2_ok) {
gd->env_addr = addr2;
gd->env_valid = 1;
} else if (! crc1_ok && ! crc2_ok) {
gd->env_addr = addr_default;
gd->env_valid = 0;
} else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) {
gd->env_addr = addr1;
gd->env_valid = 1;
} else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) {
gd->env_addr = addr2;
gd->env_valid = 1;
} else if (flag1 == flag2) {
gd->env_addr = addr1;
gd->env_valid = 2;
} else if (flag1 == 0xFF) {
gd->env_addr = addr1;
gd->env_valid = 2;
} else if (flag2 == 0xFF) {
gd->env_addr = addr2;
gd->env_valid = 2;
}
#ifdef CONFIG_OMAP2420H4
bad_flash:
#endif
return (0);
}
init_baudrate
功能://从环境变量中获取波特率值,初始化全局数据区波特率
位置:lib_arm/board.c
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp));
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10)
: CONFIG_BAUDRATE; //从上面刚初始化好的环境变量列表里找波特率值,如果没有就赋初始值为CONFIG_BAUDRATE
return (0);
}
serial_init
位置:cpu/arm920t/s3c24x0/serial.c*/
功能:根据gd中波特率值和pclk,配置串口控制器,配置好后就可以打印信息了
int serial_init (void)
{
serial_setbrg ();
return (0);
}
void serial_setbrg (void)
{
S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
int i;
unsigned int reg = 0;
/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
reg = get_PCLK() / (16 * gd->baudrate) - 1;
/* FIFO enable, Tx/Rx FIFO clear */
uart->UFCON = 0x07;
uart->UMCON = 0x0;
/* Normal,No parity,1 stop,8 bit */
uart->ULCON = 0x3;
/*
* tx=level,rx=edge,disable timeout int.,enable rx error int.,
* normal,interrupt or polling
*/
uart->UCON = 0x245;
uart->UBRDIV = reg;
#ifdef CONFIG_HWFLOW
uart->UMCON = 0x1; /* RTS up */
#endif
for (i = 0; i < 100; i++);
}
console_init_f
位置:common/console.c
功能: 控制台初始化第一阶段,由于标准设备还没有初始化这时控制台使用串口作为控制台
/* Called before relocation - use serial functions */
int console_init_f (void)
{
gd->have_console = 1;
#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT; //设置控制台模式
#endif
return (0);
}
display_banner, /* say that we are here 通知代码已经运行到该处,打印U-Boot版本、编译的时间--
位置:lib_arm/board.c */
功能:打印U-Boot版本、编译的时间等信息,通知使用者代码已经运行到该处
static int display_banner (void)
{
printf ("\n\n%s\n\n", version_string); //打印U-BOOT的版本信息
debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
_armboot_start, _bss_start, _bss_end); //打印U-BOOT代码位置
#ifdef CONFIG_MODEM_SUPPORT
debug ("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
return (0);
}
#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,
位置:board/smdk2410/smdk2410.c
功能:给gd->bd中内存信息表赋值,配置可用的内存区,检测系统内存映射,设置内存的起始地址和大小
int dram_init (void)
{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1; //RAM起始地址 PHYS_SDRAM_1 PHYS_SDRAM_1_SIZE这两个宏定义在相应的地板头文件里,include/configs/smdk2410.h
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; //RAM大小
return 0;
}
display_dram_config
位置:lib_arm/board.c
功能:打印RAM的信息
init_sequence数组中各个函数的具体分析现已结束
这样整个初始化函数表的函数都看完了,总结一下主要做了如下过程:
1. cpu, borad, interrupt的初始化,包括cache等,这些都于特定板子的配置有关。
2. 环境变量的初始化,
3. 串口,控制台,RAM的初始化,
4. 在控制台上实时的显示系统配置等相关参数。
最后需要说明的是,大部分的配置参数都是预先在include/configs/board_name.h下定义的,因此如果我们要移植我们自己的板子的话,这个文件必不可少,它描述了我们板子的配置情况如CPU型号,RAM大小等。
4. start_armboot函数中 init_sequence数组之后的后续函数具体分析flash_init
位置:board/smdk2410/flash.c
功能:nor flash数据结构初始化 ,记录下flash的大小,数量,sector的大小数量等,并对flash上重要的数据进行保护
ulong flash_init (void)
{
int i, j;
ulong size = 0;
for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
ulong flashbase = 0;
/*保存flash ID*/
flash_info[i].flash_id =
#if defined(CONFIG_AMD_LV400)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV400B & FLASH_TYPEMASK);
#elif defined(CONFIG_AMD_LV800)
(AMD_MANUFACT & FLASH_VENDMASK) |
(AMD_ID_LV800B & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif
/*保存每个flash blank的大小,sector数量,起始地址等信息*/
flash_info[i].size = FLASH_BANK_SIZE;
flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
memset (flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
if (i == 0)
flashbase = PHYS_FLASH_1;
else
panic ("configured too many flash banks!\n");
/*为每个sector分配不同的大小,作为不同的用途*/
for (j = 0; j < flash_info[i].sector_count; j++) {
if (j <= 3) {
/* 1st one is 16 KB */
if (j == 0) {
flash_info[i].start[j] =
flashbase + 0;
}
/* 2nd and 3rd are both 8 KB */
if ((j == 1) || (j == 2)) {
flash_info[i].start[j] =
flashbase + 0x4000 + (j -
1) *
0x2000;
}
/* 4th 32 KB */
if (j == 3) {
flash_info[i].start[j] =
flashbase + 0x8000;
}
} else {
flash_info[i].start[j] =
flashbase + (j - 3) * MAIN_SECT_SIZE;
}
}
size += flash_info[i].size;
}
/*对flash上保存有RO, RW的地方进行保护,monitor_flash_len = RO + RW的长度*/
flash_protect (FLAG_PROTECT_SET,
CFG_FLASH_BASE,
CFG_FLASH_BASE + monitor_flash_len - 1,
&flash_info[0]);
//对flash上保存有环境变量的地方进行保护
flash_protect (FLAG_PROTECT_SET,
CFG_ENV_ADDR,
CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);
return size;
}
display_flash_config
位置:lib_arm/board.c:
功能:打印flash的大小信息
#ifndef CFG_NO_FLASH
static void display_flash_config (ulong size)
{
puts ("Flash: ");
print_size (size, "\n");
}
#endif
mem_malloc_ini
位置:lib_arm/board.c:
功能:保存malloc区域的起始地址,结束地址,并清0这块区域,这块区域在RAM中的具体位置可查看前面的图。
/*
* Begin and End of memory area for malloc(), and current "brk"
*/
static ulong mem_malloc_start = 0;
static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;
static
void mem_malloc_init (ulong dest_addr)
{
mem_malloc_start = dest_addr;
mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
mem_malloc_brk = mem_malloc_start;
memset ((void *) mem_malloc_start, 0,
mem_malloc_end - mem_malloc_start);
}
env_relocate ();
位置:common\env_common.c中
功能:配置环境变量,重新定位 ,该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成功,并且crc校验正确的话,就使用NAND flash中读取出来的环境变量,否则使用默认的环境变量
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);
#ifdef CONFIG_AMIGAONEG3SE
enable_nvram();
#endif
#ifdef ENV_IS_EMBEDDED
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);
DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#else
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE);
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
/*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory;
if (gd->env_valid == 0) {
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
SHOW_BOOT_PROGRESS (-1);
#endif
if (sizeof(default_environment) > ENV_SIZE)
{
puts ("*** Error - default environment is too large\n\n");
return;
}
memset (env_ptr, 0, sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = 1;
}
else {
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}
devices_init:
位置: common/devices.c:
功能:根据板子的配置初始化各种设备,并调用device_register()注册到系统中去,在这个函数中仅对串口设备初始化,然后注册串口设备
int devices_init (void)
{
#ifndef CONFIG_ARM /* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
/* relocate device name pointers */
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif
创建一个保存device的列表
/* Initialize the list */
devlist = ListCreate (sizeof (device_t));
if (devlist == NULL) {
eputs ("Cannot initialize the list of devices!\n");
return -1;
}
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) //没定义
i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE);
#endif
#ifdef CONFIG_LCD //没定义
drv_lcd_init ();
#endif
#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) //没定义
drv_video_init ();
#endif
#ifdef CONFIG_KEYBOARD //没定义
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER //没定义
drv_logbuff_init ();
#endif
drv_system_init (); //这里其实是定义了一个串口设备,并且注册到devlist中
#ifdef CONFIG_SERIAL_MULTI //没定义
serial_devices_init ();
#endif
#ifdef CONFIG_USB_TTY
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE //没定义
drv_nc_init ();
#endif
return (0);
}
以 drv_system_init为例解释一下,定义也在common/devices.c,其他代码类似。
该函数注册了一个串口设备和一个空设备(根据配置而定)。其他的设备初始化函数以此大同小异,主要就是初始化好相关设备的设备信息,并注册到系统中去,详细代码大家可以自己去分析。
static void drv_system_init (void)
{
device_t dev;
memset (&dev, 0, sizeof (dev));
strcpy (dev.name, "serial"); /*串口设备*/
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; /*设备属性*/
#ifdef CONFIG_SERIAL_SOFTWARE_FIFO
/*注册设备操作函数集(输入函数,输出函数等)*/
dev.putc = serial_buffered_putc;
dev.puts = serial_buffered_puts;
dev.getc = serial_buffered_getc;
dev.tstc = serial_buffered_tstc;
#else
dev.putc = serial_putc;
dev.puts = serial_puts;
dev.getc = serial_getc;
dev.tstc = serial_tstc;
#endif
device_register (&dev); /*把该设备注册进系统中去,即把dev添加进上面创建的设备列表中去*/
#ifdef CFG_DEVICE_NULLDEV
memset (&dev, 0, sizeof (dev));
strcpy (dev.name, "nulldev");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
dev.putc = nulldev_putc;
dev.puts = nulldev_puts;
dev.getc = nulldev_input;
dev.tstc = nulldev_input;
device_register (&dev);
#endif
}
jumptable_init ();
功能:初始化gd->jt, 1.1.6版本的jumptable只起登记函数地址的作用,并没有其他作用。
位置:common/exports.c
void jumptable_init (void)
{
int i;
/*分配一块buffer用于存放跳转函数地址*/
gd->jt = (void **) malloc (XF_MAX * sizeof (void *));
for (i = 0; i < XF_MAX; i++)
gd->jt[i] = (void *) dummy; /*默认跳转函数地址,是一个空函数的地址*/
/*为每种功能定义各自的跳转函数*/
gd->jt[XF_get_version] = (void *) get_version;
gd->jt[XF_malloc] = (void *) malloc;
gd->jt[XF_free] = (void *) free;
gd->jt[XF_getenv] = (void *) getenv;
gd->jt[XF_setenv] = (void *) setenv;
gd->jt[XF_get_timer] = (void *) get_timer;
gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;
gd->jt[XF_udelay] = (void *) udelay;
#if defined(CONFIG_I386) || defined(CONFIG_PPC)
gd->jt[XF_install_hdlr] = (void *) irq_install_handler;
gd->jt[XF_free_hdlr] = (void *) irq_free_handler;
#endif /* I386 || PPC */
#if (CONFIG_COMMANDS & CFG_CMD_I2C)
gd->jt[XF_i2c_write] = (void *) i2c_write;
gd->jt[XF_i2c_read] = (void *) i2c_read;
#endif /* CFG_CMD_I2C */
}
/* 完整地初始化控制台设备 */
console_init_r (); /* fully init console as a device
位置:函数在common\console.c */
功能:初始化控制台设备二阶段
int console_init_r (void)
{
char *stdinname, *stdoutname, *stderrname;
device_t *inputdev = NULL, *outputdev = NULL, *errdev = NULL;
#ifdef CFG_CONSOLE_ENV_OVERWRITE
int i;
#endif /* CFG_CONSOLE_ENV_OVERWRITE */
/* set default handlers at first */
gd->jt[XF_getc] = serial_getc;
gd->jt[XF_tstc] = serial_tstc;
gd->jt[XF_putc] = serial_putc;
gd->jt[XF_puts] = serial_puts;
gd->jt[XF_printf] = serial_printf;
/* stdin stdout and stderr are in environment */
/* scan for it */
stdinname = getenv ("stdin");
stdoutname = getenv ("stdout");
stderrname = getenv ("stderr");
if (OVERWRITE_CONSOLE == 0) { /* if not overwritten by config switch */
inputdev = search_device (DEV_FLAGS_INPUT, stdinname);
outputdev = search_device (DEV_FLAGS_OUTPUT, stdoutname);
errdev = search_device (DEV_FLAGS_OUTPUT, stderrname);
}
/* if the devices are overwritten or not found, use default device */
if (inputdev == NULL) {
inputdev = search_device (DEV_FLAGS_INPUT, "serial"); /*找到输入设备,参考drv_system_init */
}
if (outputdev == NULL) {
outputdev = search_device (DEV_FLAGS_OUTPUT, "serial"); / *找到输出设备,参考drv_system_init */
}
if (errdev == NULL) {
errdev = search_device (DEV_FLAGS_OUTPUT, "serial"); /*找到出错输出设备*/
}
/*由drv_system_init可知,我们可以找到输入,输出设备,而且都是同一个serial设备*/
/* Initializes output console first */
if (outputdev != NULL) {
console_setfile (stdout, outputdev); /*设置标准输出设备*/
}
if (errdev != NULL) {
console_setfile (stderr, errdev); /*设置标准的错误输出设备*/
}
if (inputdev != NULL) {
console_setfile (stdin, inputdev); /*设置标准输入设备*/
}
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
#ifndef CFG_CONSOLE_INFO_QUIET
/*打印相关信息*/
/* Print information */
puts ("In: ");
if (stdio_devices[stdin] == NULL) {
puts ("No input devices available!\n");
} else {
printf ("%s\n", stdio_devices[stdin]->name);
}
puts ("Out: ");
if (stdio_devices[stdout] == NULL) {
puts ("No output devices available!\n");
} else {
printf ("%s\n", stdio_devices[stdout]->name);
}
puts ("Err: ");
if (stdio_devices[stderr] == NULL) {
puts ("No error devices available!\n");
} else {
printf ("%s\n", stdio_devices[stderr]->name);
}
#endif /* CFG_CONSOLE_INFO_QUIET */
#ifdef CFG_CONSOLE_ENV_OVERWRITE
/* set the environment variables (will overwrite previous env settings) */
for (i = 0; i < 3; i++) {
setenv (stdio_names[i], stdio_devices[i]->name);
}
#endif /* CFG_CONSOLE_ENV_OVERWRITE */
#if 0
/* If nothing usable installed, use only the initial console */
if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
return (0);
#endif
return (0);
}
参考:http://blog.chinaunix.net/uid-14114479-id-3145513.html
http://blog.csdn.net/liukun321/article/details/5680504
还有一些没法找到名字的 在此也感谢
大家发现什么问题 或者描述的不恰当的地方,欢迎评论