2016年(152)
分类: LINUX
2016-06-05 15:17:30
原文地址:uboot 启动过程中各函数功能详细分析 作者:wotaiqile
uboot启动过程中各函数功能详细分析
本文主要分析流程中,各函数的功能。按启动顺序罗列一下启动函数执行细节。我们首先从函数start_armboot流程进行分析:
1)DECLARE_GLOBAL_DA
#define DECLARE_GLOBAL_DA
/* 声明一个寄存器变量gd占用r8。这个宏在所有需要引用全局数据指针gd_t *gd的源码中都有声明
* 这个声明也避免编译器把r8分配给其他的变量,所以gd就是r8,这个指针变量不占用内存。
*/
/* 定义gd为gd_t类型指针,存储在寄存器r8中 */
/* register:表示变量对于执行速度非常重要,因此应该放在机器的寄存器中(寄存器独立于内存,通常在处理器芯片上) */
/* volatile:用于指定变量的值可以由外部过程异步修改,例如中断例程 */
2)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 位于lib_arm/board.c
/* 对全局数据区进行地址分配,_armboot_start为0x33f80000,CFG_MALLOC_LEN是:堆的大小+环境数据区大小。
* 可以在/config/smdk2410.h中找到CFG_MALLOC_LEN,其大小定义为192K
*/
3)gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 位于lib_arm/board.c
/*
* 分配板子数据区bd首地址
* 这里,我们可以结合start.s中栈的分配来理解:
*
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
//设置SDRAM的基地址_TEXT_BASE = 0x33F80000
//下面这两行代码可以参考工作日志上的图来理解
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DA
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
由这些汇编代码,我们可以得出如下内存分配结构:
显示缓冲区 (.bss_end~0x34000000)
uboot(bss,da
heap(for malloc)
gd(global da
bd(board da
stack
... ...
nor flash (0~2M)
有网友这样分析:
gd和bd共占128个字节的大小。
在给malloc区和bd数据结构分配后,如果定义了IRQ,则给栈分配IRQ+FIQ占8K大小的区。CONFIG_STACKSIZE_IRQ=4K。
如果没有定义,则至少保存12个字节。在smdk2410.h有定义。
从0x33f00000到0x34000000的1M地址,是留给uboot使用的, 即uboot占用高端内存区。
下面是从网络上获得的信息(作者说是从代码中得出的结论,需要进一步验证)。DW_STACK_START,建立堆栈,
栈起点0x33f00000,大小为0x80000 32K大小的栈。UBOOT_RAM_BASE 与_TEXT_BASE(0x33f80000)相同,
_arm_boot_start也为0x33f80000。
*/
4)下面,我们来分析初始化列表init_sequence[]中的函数:
A. cpu_init(),定义于cpu/arm920t/cpu.c
分配IRQ、FIQ栈底地址,由于没有定义CONFIG_USE_IRQ,所以相当于空实现
B. board_init:板级初始化,定义于/board/smdk2410/smdk2410.c
设置PLL时钟,GPIO,使能I/D cache
设置bd信息:
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* arch number of SMDK2410-Board 板子的ID*/
gd->bd->bi_boot_params = 0x30000100; /* adress of boot parameters 内核启动参数存放地址 */
C. interrupt_init;定义于cpu/arm920t/s3c24x0/interrupt.c
用于初始化smdk2410的PWM timer 4,使其能够自动装载计数值,恒定地产生时间中断信号,但是中断被屏蔽了,用不上。
D. env_init;定义于以下几个文件中(为什么):
Env_dataflash.c
Env_eeprom.c
Env_flash.c
Env_nand.c
Env_nowhere.c
Env_nvram.c
功能:指定环境区的地址。
default_enviroment是默认的环境参数设置。
gd->env_addr = (ulong)&default_enviroment[0];
gd->env_valid = 0;
E. init_baudrate: 初始化全局数据区中波特率的值
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);
}
F.serial_init;串口通讯设置,定义于:
Serial.c定义于:
u-boot-1.1.6\cpu\arm920t\s3c24x0):int serial_init (void)
功能:根据bd中波特率值和pclk,设置串口寄存器。
G. console_init_f;控制台前期初始化common/console.c
由于标准设备还没有初始化(gd->flags&GD_FLG_DEVINIT=0), 这时控制台使用串口作为控制台。函数只有一句:gd->have_console=1;
H. dram_init;初始化内存RAM的信息。位于board/smdk2410/smdk2410.c
其实就是给gd->bd中的内存信息表赋值。
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;
}
5)下面,我们来看flash_init;本函数定义在board/smdk2410/flash.c
flash.c这个文件与具体平台密切相关,smdk2410使用的flash与s3c2410不一样(一样),所以移植的时候必须重写这个程序。
那么,flash_init()到底要做什么操作呢?
首先有一个变量,flash_info_t flash_info[CFG_MAX_FLASH_BANKS];这个变量用来记录flash的信息。
flash_info_t结构体的定义:
未找到?????????
把视频帧缓冲区设置在bss_end后面。
未找到代码????????????
/* armboot_start is defined in the board-specific linker script */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //位于board.c
设置heap区,供malloc使用,下面的变量和函数定义在lib_arm/board.c。
malloc可用内存由mem_malloc_start,mem_malloc_end指定,而当前分配的位置则是mem_malloc_brk。
mem_malloc_init负责初始化这三个变量。malloc则通过sbrk函数来使用和管理这片内存。
/*
* 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);
}
void *sbrk (ptrdiff_t increment)
{
ulong old = mem_malloc_brk;
ulong new = old + increment;
if ((new < mem_malloc_start) || (new > mem_malloc_end)) {
return (NULL);
}
mem_malloc_brk = new;
return ((void *) old);
}
这是flash_init的定义:
ulong flash_init (void)
{
int i, j;
ulong size = 0;
for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
ulong flashbase = 0;
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_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");
for (j = 0; j < flash_info[i].sector_count; j++) {
if (j <= 3) {
/* 1st on
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_protect (FLAG_PROTECT_SET,
CFG_FLASH_BASE,
CFG_FLASH_BASE + monitor_flash_len - 1,
&flash_info[0]);
flash_protect (FLAG_PROTECT_SET,
CFG_ENV_ADDR,
CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]);
return size;
}
6)env_relocate()环境参数区重定位
由于初始化了heap区,所以可以通过malloc重新分配一块环境参数区,但是没有必要,因为默认的环境参数已经重新定位到RAM中去了。
......................
7)IP,MAC地址的初始化,主要是从环境中读,然后赋给gd->bd对应域就OK。
8)devices_init();定义于common/devices.c
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
/* 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); //初始化i2c接口,i2c接口没有注册到devlist中去
#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 ();v //这里其实是定义了一个串口设备,并且注册到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);
}
devices_init()这个函数执行完之后,创建了devlist,但是只有一个串口设备注册在内。显然devlist中的设备都是可以作为console的。
9)jumptable_init();用于初始化gd->jt,这里请注意:u-boot-1.1.6 的jumptable只起到登记函数地址的作用,并没有其他的作用。
10)console_init_r();位于/common/console.c这是后期控制台初始化
主要过程:查看环境参数stdin,stdout,stderr中对标准IO的指定的设备名称,再按照环境指定的名称呢个搜索devlist,将搜索到的设备指针赋给标准IO数组stdio_devices[]。
置gd->flag标志GD_FLG_DEVINIT。这个标志影响putc,getc函数的实现,
//未定义标志时直接由串口serial_getc和serial_putc实现,定义以后通过标准设备数组stdio_devices[]
中的putc和getc来实现。
void putc (const char c)
{
#ifdef CONFIG_SILENT_CONSOLE
if (gd->flags & GD_FLG_SILENT) //GD_FLG_SILENT无输出标志
return;
#endif
if (gd->flags & GD_FLG_DEVINIT) {
/* Send to the standard output */
fputc (stdout, c);
} else {
/* Send directly to the handler */
serial_putc (c); //未初始化时直接从串口输出
}
}
void fputs (int file, const char *s)
{
if (file < MAX_FILES)
stdio_devices[file]->puts (s);
}
使用devlist,std_device[]的原因:
为了更灵活地实现标准IO重定向,任何可以作为标准IO的设备,如USB键盘,LCD屏,串口等都可以对应一个device_t的结构体变量,
只需要实现getc和putc等函数,就能加入到devlist列表中去,也就可以被assign为标准IO设备std_device中去。如函数
/////////
int console_assign (int file, char *devname); /* Assign the console 重定向标准输入输出*/
这个函数功能就是把名为devname的设备重定向为标准IO文件file(stdin,stdout,stderr)。其执行过程是在devlist中查找devname的设备,
返回这个设备的device_t指针,并把指针值赋给std_device[file]。
////////
11)enable_interrupts(),/cpu/arm920t/interrupt.c 使能中断。由于CONFIG_USE_IRQ没有定义,空实现。
#ifdef CONFIG_USE_IRQ
/* enable IRQ interrupts */
void enable_interrupts (void)
{
unsigned long temp;
__asm__ __volatile__("mrs %0, cpsr\n"
"bic %0, %0, #0x80\n"
"msr cpsr_c, %0"
: "=r" (temp)
:
: "memory");
}
void enable_interrupts (void)
{
return;
}
12)设置CS8900的MAC地址 (board.c)
/* Perform network card initialisation if necessary */
#ifdef CONFIG_DRIVER_CS8900
cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
13)初始化以太网(board.c)
eth_initialize(gd->bd); //bd中IP,MAC已经初始化
14)main_loop();定义于/common/main.c
至此,所有初始化工作已经工作完毕。main_loop在标准转入设备中接收命令行,然后分析,查找,执行。
from:
http://zqwt.012.blog.163.com/blog/static/1204468420103139147872/