您的位置:首页 > 编程语言

uboot第二阶段代码详细分析

2014-06-22 20:09 477 查看
转载:http://blog.chinaunix.net/uid-23670869-id-2391644.html

Stage2 C语言代码部分

lib_arm/board.c中的start arm boot是C语言开始的函数也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数只要完成如下操作:

(1)调用一系列的初始化函数。

(2)初始化Flash设备。

(3)初始化系统内存分配函数。

(4)如果目标系统拥有NAND设备,则初始化NAND设备。

(5)如果目标系统有显示设备,则初始化该类设备。

(6)初始化相关网络设备,填写IP、MAC地址等。

(7)进去命令循环(即整个boot的工作循环),接受用户从串口输入的命令,然后进行相应的工作。
对于内核启动来说,这部分主要是完成全局数据gd的初始化,以便内核访问

start_armboot是U-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程:

void start_armboot (void)

{

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 */

//获取全局gd指针

gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));

/* compiler optimization barrier needed for GCC >= 3.4 */

__asm__ __volatile__("": : :"memory");

//清空该结构体

memset ((void*)gd, 0, sizeof (gd_t));

//获取bd_info结构体指针

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;

//调用初始化函数,用来初始化gd结构体

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

if ((*init_fnc_ptr)() != 0) {

hang ();

}

}

#ifndef CFG_NO_FLASH

/* configure available FLASH banks */

//board/smdk2410/flash.c配置flash

//从其实现来看,好像只是配置nor flash

size = flash_init ();//初始化flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS],nor flash基地址和扇区大小

//显示flash信息

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 */

//显示器为LCD,同上

#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 */

//初始化CFG_MALLOC_LEN大小空间

/* armboot_start is defined in the board-specific linker script */

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

//初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可

//在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令

#if (CONFIG_CMD_NAND)

puts ("NAND: ");

nand_init(); //board/smdk2410/smdk2410.c,获取nand的基地址和 大小信息

#endif

#ifdef CONFIG_HAS_DATAFLASH

AT91F_DataflashInit();

dataflash_print_info();

#endif

/* initialize environment */

//初始化环境参数

env_relocate ();

//framebuffer初始化

#ifdef CONFIG_VFD

/* must do this after the framebuffer is allocated */

drv_vfd_init();

#endif /* CONFIG_VFD */

//通过命令行参数传递获取ip地址

/* IP Address */

gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

//调用相应驱动函数对硬件设备进行初始化

stdio_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 */

//启用中断

enable_interrupts ();

/* Perform network card initialisation if necessary */

//初始化网卡

#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 ();

}//end start_armboot

//下面详细分析下init_sequence

typedef int (init_fnc_t) (void);
//注意这种用法,linux内核中也经常使用

int print_cpuinfo (void);

init_fnc_t *init_sequence[] = {

#if defined(CONFIG_ARCH_CPU_INIT)

arch_cpu_init, /* basic arch cpu dependent setup //cpu/arm920t/cpu.c中定义,该函数为空,因为没有采用IRQ或FIQ模式*/

#endif

board_init, /* basic board dependent ,初始化时钟频率,配置IO口,初始化全局数据bd(如平台号,传递内核参数的地址) ,setup //board/smdk2410/smdk2410.c */

#if defined(CONFIG_USE_IRQ)

interrupt_init, /* set up exceptions */

#endif

timer_init, /* initialize timer 使用定时器4 cpu/arm920t/s3c24x0/timer.c */

#ifdef CONFIG_FSL_ESDHC

get_clocks,

#endif

env_init, /* initialize environment common/Env_nand.c
校验环境变量,并获取环境变量的地址 */


init_baudrate, /* initialze baudrate settings 获取波特率环境变量,初始化全局数据的中波特率 /lib_arm/board.c */

serial_init, /* serial communications setup driver/serial/serial_s3c24x0.c 初始化串口0,用于打印信息,该平台只支持配置串口1[b]来打印信息[/b]
*/

console_init_f, /* stage 1 init of console */

display_banner, /* say that we are here 打印uboot代码段和数据段地址信息 */

#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 ,获取ram的地址和大小,banks board/sunsang/smdk2410/smdk2410.c*/

#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)

arm_pci_init,

#endif

//打印BANK的相关信息

display_dram_config, //lib_arm/board.c

NULL,

};

int board_init (void)

{

;将时间相关的寄存器定义为结构体S3C24X0_CLOCK_POWER,S3C24X0_GPIO也是一样

S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

;设置cpu时钟

/* to reduce PLL lock time, adjust the LOCKTIME register */

clk_power->LOCKTIME = 0xFFFFFF;

/* configure MPLL */

//M_MDIV=0xA1,M_PDIV=0x3,M_SDIV=0x1

//这样系统时钟为202.80M

clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

/* some delay between MPLL and UPLL */

delay (4000);

;USB时钟为48M

/* 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;

;初始化bd结构体中的bi_arch_number和bi_boot_params

/* arch number of SMDK2410-Board
平台号,定义在include/asm-arm/mach-types.h */

gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

/* adress of boot parameters,内核启动时,传递给内核的参数 */

gd->bd->bi_boot_params = 0x30000100;

;启用指令和数据cache

;通过对协处理器的操作了实现cache的使能

icache_enable();

dcache_enable();

return 0;

} //end board_init

int timer_init(void)

{

;获取计时控制寄存器

struct s3c24x0_timers *timers = s3c24x0_get_base_timers();

ulong tmr;

;使用PWM定时器4

/* use PWM Timer 4 because it has no output */

/* prescaler for Timer 4 is 16 */

writel(0x0f00, &timers->TCFG0);

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);

timer_clk = get_PCLK() / (2 * 16);

}

/* load value for 10 ms timeout */

lastdec = timer_load_val;

writel(timer_load_val, &timers->TCNTB4);

/* auto load, manual update of Timer 4 */

tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;

writel(tmr, &timers->TCON);

/* auto load, start Timer 4 启用定时器4做为系统定时器 */

tmr = (tmr & ~0x0700000) | 0x0500000;

writel(tmr, &timers->TCON);

timestamp = 0;

return (0);

} //end timer_init

static int env_init (void)

{

//这个是自己修改的,和源码有所不同

if(pbootflag==1) //by lht
这种情况是从nor启动

{

env_name_spec = "NOR";

env_ptr=(env_t *)CONFIG_ENV_ADDR;

//#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x0F0000) /* addr of environment */

return nor_env_init();

}

else //
从nand
启动

{

#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)

int crc1_ok = 0, crc2_ok = 0;

env_t *tmp_env1;

#ifdef CONFIG_ENV_OFFSET_REDUND

env_t *tmp_env2;

tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);

crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);

#endif

tmp_env1 = env_ptr;

crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);

if (!crc1_ok && !crc2_ok) {

gd->env_addr = 0;

gd->env_valid = 0;

return 0;

} else if (crc1_ok && !crc2_ok) {

gd->env_valid = 1;

}

#ifdef CONFIG_ENV_OFFSET_REDUND

else if (!crc1_ok && crc2_ok) {

gd->env_valid = 2;

} else {

/* both ok - check serial */

if(tmp_env1->flags == 255 && tmp_env2->flags == 0)

gd->env_valid = 2;

else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)

gd->env_valid = 1;

else if(tmp_env1->flags > tmp_env2->flags)

gd->env_valid = 1;

else if(tmp_env2->flags > tmp_env1->flags)

gd->env_valid = 2;

else /* flags are equal - almost impossible */

gd->env_valid = 1;

}

if (gd->env_valid == 2)

env_ptr = tmp_env2;

else

#endif

if (gd->env_valid == 1)

env_ptr = tmp_env1;

gd->env_addr = (ulong)env_ptr->data;

#else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */

gd->env_addr = (ulong)&default_environment[0]; //使用默认环境变量,定义在common/env_common.c

gd->env_valid = 1;

#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */

}

return (0);

} //end env_init

//如果参数中设置了波特率则利用参数用设置的波特率,否则利用默认的CONFIG_BAUDRATE(115200)

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;

return (0);

} //end init_baudrate

//初始化ram信息,设置起始地址和大小,从include/configs/smdk2410.h中获取这些信息
//,这里只是对gd中的 bi_dram结构中的两个成员赋值,

[b]//
[/b]也即BANK的起始地址和大小

int dram_init (void)

{

gd->bd->bi_dram[0].start = PHYS_SDRAM_1;

gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
return 0;

} // end dram_init

//显示ram信息,其中的宏也是从include/configs/smdk2410.h中读取

static int display_dram_config (void)

{

int i;

#ifdef DEBUG

puts ("RAM Configuration:n");

for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {

printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);

print_size (gd->bd->bi_dram[i].size, "n");

}

#else

ulong size = 0;

for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {

size += gd->bd->bi_dram[i].size;

}

puts("DRAM: ");

print_size(size, "n");

#endif

return (0);

} //end display_dram_config

//以上都是一些初始化的函数,可以看出以上这些函数都是为了初始化一个全局的结构体变量gd而执行的,

//该变量地址由寄存器r8指向,该结构体定义了开发板的相关硬件配置,在include/asm-arm/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; /* 环境变量的起始地址 */

unsigned long env_valid; /* 校验环境变量有效性的标志位 */

unsigned long fb_base; /* base address of frame buffer 帧缓冲基地址*/

#ifdef CONFIG_VFD

unsigned char vfd_type; /* display type lcd显示设备类型 */

#endif

#ifdef CONFIG_FSL_ESDHC

unsigned long sdhc_clk;

#endif

void **jt; /* jump table */

} gd_t;

typedef struct bd_info {

int bi_baudrate; /* serial console baudrate */

unsigned long bi_ip_addr; /* IP Address IP地址*/

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];

} bd_t;

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 */

/**

#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)

#define CONFIG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */

#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x030000) /* addr of

environment */

} env_t;

环境变量指针 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 定义标准出错信息输出设备,一般是串口

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: