Chinaunix首页 | 论坛 | 博客
  • 博客访问: 276661
  • 博文数量: 91
  • 博客积分: 2105
  • 博客等级: 大尉
  • 技术积分: 1050
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-14 19:30
文章分类
文章存档

2011年(11)

2010年(64)

2009年(16)

我的朋友

分类: LINUX

2010-09-02 10:50:13

接下来进入到Bootloader Stage 2C语言代码部分,入口是start_armboot,对应的源文件是lib_arm/board.c,这一文件对所有的ARM处理器都是通用的,因此在移植的时候不用修改。相关源代码如下:

DECLARE_GLOBAL_DATA_PTR

/* include/asm-arm/global_data.h中定义的一个全局寄存器变量的声明:

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

用于存放全局数据结构体gd_t的地址。

*/

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)

/* 本次移植暂不配置VFDLCD,后面也将不考虑的部分略去 */

/* 初始化全局数据结构体指针gd */

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

....../* memsetlib_generic/string.c中定义*/

memset ((void*)gd, 0, sizeof (gd_t)); /*0填充全局数据表*gd */

gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

memset (gd->bd, 0, sizeof (bd_t)); /*0填充(初始化) *gd->bd */

monitor_flash_len = _bss_start - _armboot_start;

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

size = flash_init (); /* drivers/cfi_flash.c或自定义 */

display_flash_config (size);

#endif /* CFG_NO_FLASH */

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

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);

......

/* initialize environment */

env_relocate ();

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

}

}

devices_init (); /* get the devices list going. */

.......

jumptable_init ();

console_init_r (); /* fully init console as a device */

enable_interrupts (); /* enable exceptions */

/* Perform network card initialisation if necessary */

#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 defined(CONFIG_CMD_NET)

if ((s = getenv ("bootfile")) != NULL)

copy_filename (BootFile, s, sizeof (BootFile));

#endif

#ifdef BOARD_LATE_INIT

board_late_init ();

#endif

......

/*main_loop() can return to retry autoboot, if so just run it again.*/

for (;;) {

main_loop ();

}

}

gd_t是全局数据表类型,在include/asm-arm/global_data.h中定义如下:

/*

Keep it *SMALL* and remember to set CFG_GBL_DATA_SIZE > sizeof(gd_t)

*/

typedef struct global_data {

bd_t *bd;

unsigned long flags;

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

unsigned long fb_base; /* base address of frame buffer */

.......

void **jt; /* jump table */

} gd_t;

其中,bd_tinclude/asm-arm/u-boot.h中定义如下:

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

ulong bi_boot_params; /* where this board expects params*/

struct /* RAM configuration */

{

ulong start;

ulong size;

}bi_dram[CONFIG_NR_DRAM_BANKS];

} bd_t;

jt是函数数组指针,随后将在jumptable_init()函数中初始化。

lib_arm/board.c的源码不难分析出系统的启动流程:首先初始化全局数据表,然后顺序执行函数指针数组init_sequence中的一系列初始化函数——由其在本文件中的相关定义可得知初始化流程:

typedef int (init_fnc_t) (void);

init_fnc_t *init_sequence[] = {

cpu_init, /* basic cpu dependent setup -- cpu/pxa/cpu.c */

board_init, /* basic board setup --board/lubbock/lubbock.c */

interrupt_init, /* set up exceptions -- cpu/pxa/interrupts.c */

env_init, /* initialize environment -- common/env_flash.c */

init_baudrate, /* initialze baudrate settings--lib_arm/board.c */

serial_init, /* serial communications setup--cpu/pxa/serial.c */

console_init_f, /* stage 1 init of console -- common/console.c */

display_banner, /* say that we are here -- lib_arm/board.c */

#if defined(CONFIG_DISPLAY_BOARDINFO)

checkboard, /* display board info */

#endif

dram_init, /* configure available RAM banks --board/lubbock/lubbock.c */

display_dram_config, /* lib_arm/board.c */

NULL,

};

在执行这个函数序列的过程中,任何一个函数异常返回都会导致u-boot“死锁”或说“挂起”在hang()函数的死循环当中。

若一切顺利,接下来就调用flash_init()函数初始化CFI FLASH(针对NOR型闪存而言),该函数在drivers/cfi_flash.c中定义,不过,只有在目标板头文件中”#define CFG_FLASH_CFI_DRIVER”之后该驱动才会被编译;在lubbocku-boot实现当中,include/configs/lubbock.h中没有定义CFG_FLASH_CFI_DRIVER,而是在board/lubbock/ flash.c中实现了自己的FLASH驱动,包括flash_init()在内。在移植U-Boot时,可以根据实际情况选择使用U-Boot自带的FLASH驱动还是自己编写新的驱动。如果配置了NAND闪存,还会对其进行初始化;笔者的XSABSE270板没有焊接NAND FLASH,故对此不作讨论。

接下来调用env_relocate()函数初始化环境变量,该函数在common/env_common.c文件中定义。在同一文件中可以发现还定义了一个字符数组default_environment[],用于描述缺省的环境变量,这些都要在include/configs/lubbock.h头文件中进行设置,包括启动命令CONFIG_BOOTCOMMAND,波特率CONFIG_BAUDRATEIP地址CONFIG_IPADDR等等。

然后是获取自设置的目标板的网络地址,包括IP地址和MAC地址。

再然后是调用common/devices.c中定义的devices_init()函数来创建设备列表,并初始化相应的设备,主要是”stdin”,”stdout”,”stderr”以及自定义的设备如I2CLCD等。这些相关代码是与平台无关的,因此从移植的角度考虑,不必作细致的研究与分析。

接着调用common/exports.c中定义的jumptable_init()函数,初始化全局数据表中的跳转表gd->jt,跳转表是一个函数指针数组,定义了u-boot中基本的常用的函数库;而gd->jt是这个函数指针数组的首指针。部分代码如下:

void jumptable_init (void) {

int i;

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;

......

}

上面的XF_get_version, XF_malloc, XF_free等在include/exports.h的枚举变量中定义,因此,实际上是作为”Label式整型序号”使用,即XF_get_version=1, XF_malloc=2, XF_free=3 ...,相关代码如下:

enum { /* include/exports.h */

#define EXPORT_FUNC(x) XF_ ## x ,

#include <_exports.h>

#undef EXPORT_FUNC

XF_MAX

};

EXPORT_FUNC(get_version)

EXPORT_FUNC(getc)

EXPORT_FUNC(tstc)

EXPORT_FUNC(putc)

EXPORT_FUNC(puts)

EXPORT_FUNC(printf)

......... /* include/_exports.h */

由于这些也是平台无关的代码,因此在移植过程中也不必深究。

然后是调用common/console.c中定义的函数console_init_r()初始化串口控制台,这同样是平台无关的代码,所以不必关心。

这时U-Boot的基本功能已经初始化完毕,便可开中断,并进行附加功能的配置与初始化,包括网卡驱动配置,目标板使用LAN91C1111网卡,对应SMC91111网卡驱动,可以根据需要配置其他的网卡驱动如CS8900等,这些都在include/configs/lubbock.h中定义。

然后是调用board/lubbock/lubbock.c中定义的board_late_init()函数进行板级的后期初始化,实际上是配置stdoutstderr的硬件设备。相关源代码如下:

int board_late_init(void)

{

setenv("stdout", "serial");

setenv("stderr", "serial");

return 0;

}

最后需要注意的一个很重要的文件是lib_arm/armlinux.c,它实现的功能包括设置内核启动参数,并负责将这些参数传递给内核,最后跳转到Linux内核入口函数,将控制权交给内核。

具体传递哪些参数,是通过在include/configs/lubbock.c中指定条件编译选项来控制的,对应于lib_arm/armlinux.c中的部分源代码形式如下:

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \

defined (CONFIG_CMDLINE_TAG) || \

defined (CONFIG_INITRD_TAG) || \

defined (CONFIG_SERIAL_TAG) || \

defined (CONFIG_REVISION_TAG)

static void setup_start_tag (bd_t *bd);

# ifdef CONFIG_SETUP_MEMORY_TAGS

static void setup_memory_tags (bd_t *bd);

# endif

static void setup_commandline_tag (bd_t *bd, char *commandline);

# ifdef CONFIG_INITRD_TAG

static void setup_initrd_tag (bd_t *bd, ulong initrd_start,

ulong initrd_end);

# endif

static void setup_end_tag (bd_t *bd);

static struct tag *params;

#endif

......

void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],

ulong addr, ulong *len_ptr, int verify)

{

......

void (*theKernel)(int zero, int arch, uint params);

......

#ifdef CONFIG_CMDLINE_TAG

char *commandline = getenv ("bootargs");

#endif

theKernel = (void (*)(intint, uint))ntohl(hdr->ih_ep);

......

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

}

关于这个参数列表中各个参数的定义及含义,以及参数列表的初始化过程,可以参考一文。内核是如何找到这个参数列表在内存中的位置,以接收这些参数的呢?实际上,参数列表(tag list)在内存中的起始地址会保存在通用寄存器R2中,并传递给内核。而按照习惯或说惯例,通常tag list的首地址(物理地址)会设置为RAM起始地址+ 0x100偏移量,因此R2的值实际上是确定不变的。另外,还要正确设置R0R1的值,在呼叫内核时,R0的值应为0R1中则应保存机器类型(machine type)编号。R0,R1R2都会作为参数传递给内核。

在上面的代码中,定义了一个函数指针theKernel,通过倒数第二条语句将内核入口地址赋给theKernel(hdrinclude/image.h中定义的一个image_header结构体类型的数据,hdr->ih_ep中保存了内核入口地址,ntohl的功能是字节顺序的大小端转换,相关代码可以参考tools/mkimage.c),最后,根据APCS规则,将0, bd->bi_arch_number, bd->bi_boot_params 依次作为参数通过R0R1R2传递给theKernel函数,并进入内核启动部分。

至此,我们已经从源代码入手简要分析了U-Boot的启动流程,在这个过程中,我们对前一篇文章“添加新的目标板定义”也有了更进一步的理解和认识:为什么要添加这些文件;哪些文件是平台相关的并且必须要根据平台特性进行修改的;哪些文件是平台无关的,是不需要修改的,只需在头文件中作适当配置即可。

下一节,我们将给出移植U-BootXSBASE270开发板的实例。

阅读(593) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2011-04-13 16:36:34

学习了,多谢楼主分享哦!也欢迎广大linux爱好者来我的论坛一起讨论arm哦!www.lt-net.cn