您的位置:首页 > 其它

uboot 启动过程中各函数功能详细分析

2014-02-26 21:43 357 查看
uboot启动过程中各函数功能详细分析

本文主要分析流程中,各函数的功能。按启动顺序罗列一下启动函数执行细节。我们首先从函数start_armboot流程进行分析:

1)DECLARE_GLOBAL_DATA_PTR; 这个宏在include/global_data.h中

#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")

/* 声明一个寄存器变量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为0x3f000000,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_DATA_SIZE /* bdinfo */

#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,data,text) (0x33f00000~.bss_end)

heap(for malloc)

gd(global data)

bd(board data)

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 (d:\重要资料(请不要删!)\嵌入式集成开发平台研究\armlinux研究\uboot研究\u-boot-1.1.6\u-boot-1.1.6\common):int serial_init (void)

Serial.c (d:\重要资料(请不要删!)\嵌入式集成开发平台研究\armlinux研究\uboot研究\u-boot-1.1.6\u-boot-1.1.6\cpu\arm920t\at91rm9200):int serial_init (void)

Serial.c (d:\重要资料(请不要删!)\嵌入式集成开发平台研究\armlinux研究\uboot研究\u-boot-1.1.6\u-boot-1.1.6\cpu\arm920t\imx):int serial_init (void)

Serial.c (d:\重要资料(请不要删!)\嵌入式集成开发平台研究\armlinux研究\uboot研究\u-boot-1.1.6\u-boot-1.1.6\cpu\arm920t\ks8695):int serial_init(void)

Serial.c (d:\重要资料(请不要删!)\嵌入式集成开发平台研究\armlinux研究\uboot研究\u-boot-1.1.6\u-boot-1.1.6\cpu\arm920t\s3c24x0):int serial_init (void)

有5个serial.c,到底是哪个?

功能:根据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 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_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在标准转入设备中接收命令行,然后分析,查找,执行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: