Porting U-boot for SPEAr310(arm926) --- 3
2、U-boot启动过程 前辈们已有不少介绍,俺在这集中下! ? 从board/spear/spear310/u-boot.lds可以发现,cpu/arm926ejs/start.s的_start 为u-boot程序入口,start_armboot为C程序入口,以下为U-Boot的一个存储器映射图: C函数入口start_armboot对应的源文件是lib_arm/board.c,这一文件对所有的ARM处理器都是通用的,因此在移植的时候不用修改,分析start_armboot(): 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) /* 本次移植暂不配置VFD和LCD,后面也将不考虑的部分略去 */ /* 初始化全局数据结构体指针gd */ gd = (gd_t*)(_armboot_start -CFG_MALLOC_LEN - sizeof(gd_t)); ....../* memset在lib_generic/string.c中定义*/ memset ((void*)gd,sizeof(gd_t)); /*用0填充全局数据表*gd */ gd->bd = (bd_t*)((char*)gd -sizeof(bd_t)); memset (gd->bd,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 FLASHbanks */ size = flash_init (); /*drivers/cfi_flash.c或自定义 */ display_flash_config (size); #endif /* CFG_NO_FLASH */ ? /*armboot_start is defined inthe 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; ?????????????????? } ? #ifdef CONFIG_HAS_ETH1 ?????????????????? i = getenv_r ("eth1addr",sizeof(tmp)); ?????????????????? s = (i > 0) ? tmp : NULL; ? ?????????????????? for (reg = 0; reg < 6; ++reg) { ??????????????????????????? gd->bd->bi_enet1addr[reg] = s ?simple_strtoul (s,16) : 0; ??????????????????????????? if (s) ???????????????????????????????????? s = (*e) ? e + 1 : e; ?????????????????? } #endif ???????? } ? devices_init (); /* get thedevices list going. */ ....... jumptable_init (); console_init_r (); /* fullyinit console as a device */ enable_interrupts (); /* enableexceptions */ ...... ???????? /* 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 ...... #if defined(CONFIG_CMD_NET) #if defined(CONFIG_NET_MULTI) ???????? puts ("Net:??"); #endif ???????? eth_initialize(gd->bd); #endif ? /*main_loop() can return toretry autoboot,if so just run it again.*/ for (;;) { main_loop (); } } gd_t是全局数据表类型,在include/asm-arm/global_data.h中定义如下: /* Keep it *SMALL* and remember toset 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; /* baseaddress of frame buffer */ ....... void **jt; /* jump table */ } gd_t; ? DECLARE_GLOBAL_DATA_PTR; /* 在include/asm-arm/global_data.h中定义的一个全局寄存器变量的声明: * #defineDECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8") * 用于存放全局数据结构体gd_t的地址。 */ 其中,bd_t在include/asm-arm/u-boot.h中定义如下: typedef struct bd_info { int bi_baudrate; /* serialconsole baudrate */ unsigned long bi_ip_addr; /* IPAddress */ unsigned char bi_enetaddr[6];/* Ethernet adress */ struct environment_s *bi_env; ulong bi_arch_number; /* uniqueid for this board */ ulong bi_boot_params; /* wherethis 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 cpudependent setup -- cpu/pxa/cpu.c */ board_init,/* basic boardsetup --board/lubbock/lubbock.c */ interrupt_init,/* set upexceptions -- cpu/pxa/interrupts.c */ env_init,/* initializeenvironment -- common/env_flash.c */ init_baudrate,/* initialzebaudrate settings--lib_arm/board.c */ serial_init,/* serialcommunications setup--cpu/pxa/serial.c */ console_init_f,/* stage 1 initof console -- common/console.c */ display_banner,/* say that weare here -- lib_arm/board.c */ #ifdefined(CONFIG_DISPLAY_BOARDINFO) checkboard,/* display boardinfo */ #endif dram_init,/* configureavailable RAM banks --board/lubbock/lubbock.c */ display_dram_config,/*lib_arm/board.c */ NULL, }; 在执行这个函数序列的过程中,任何一个函数异常返回都会导致u-boot“死锁”或说“挂起”在hang()函数的死循环当中。 若一切顺利,接下来就调用flash_init()函数初始化FLASH。在spear310的u-boot实现当中,目标板头文件(include/configs/spear310.h)预编译宏#define CONFIG_SPEAR_EMI和CFG_FLASH_CFI_DRIVER控制编译PNor flash驱动,该函数在drivers/mtd/cfi_flash.c;通过预编译宏#define CONFIG_SPEAR_SMI”控制编译SNor flash驱动,该函数在drivers/mtd/spr_smi.c。在移植U-Boot时,可以根据实际情况选择使用U-Boot自带的FLASH驱动还是自己编写新的驱动。 接下来是Nand Flash初始化,现目标板未使用Nand Flash,对此暂不讨论。 接下来调用env_relocate()函数初始化环境变量,该函数在common/env_common.c文件中定义。在同一文件中可以发现还定义了一个字符数组default_environment[],用于描述缺省的环境变量,这些都要在include/configs/spear310.h头文件中进行设置,包括启动命令CONFIG_BOOTCOMMAND,波特率CONFIG_BAUDRATE,IP地址CONFIG_IPADDR等等。 然后是获取自设置的目标板的网络地址,包括IP地址和MAC地址。 再然后是调用common/devices.c中定义的devices_init()函数来创建设备列表,并初始化相应的设备,主要是”stdin”,”stdout”,”stderr”以及自定义的设备如I2C,LCD等。这些相关代码是与平台无关的,因此从移植的角度考虑,不必作细致的研究与分析。 接着调用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; ???????? gd->jt[XF_get_timer] = (void *) get_timer; ???????? gd->jt[XF_simple_strtoul] =(void *) simple_strtoul; ???????? gd->jt[XF_udelay] = (void *) udelay; ???????? gd->jt[XF_simple_strtol] = (void *) simple_strtol; ???????? gd->jt[XF_strcmp] = (void *) strcmp; #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 defined(CONFIG_CMD_I2C) ???????? gd->jt[XF_i2c_write]= (void *) i2c_write; ???????? gd->jt[XF_i2c_read]= (void *) i2c_read; #endif } 然后是调用common/console.c中定义的函数console_init_r()初始化串口控制台,这同样是平台无关的代码,所以不必关心。 这时U-Boot的基本功能已经初始化完毕,便可开中断,并进行附加功能的配置与初始化,包括网卡驱动配置。 最后需要注意的一个很重要的文件是lib_arm/armlinux.c,它实现的功能包括设置内核启动参数,并负责将这些参数传递给内核,最后跳转到Linux内核入口函数,将控制权交给内核。 具体传递哪些参数,是通过在include/configs/spear310.h中指定条件编译选项来控制的,对应于lib_arm/armlinux.c中的参数列表中各个参数的定义及含义,以及参数列表的初始化过程,可以参考Booting ARMLinux一文。内核是如何找到这个参数列表在内存中的位置,以接收这些参数的呢?实际上,参数列表(tag list)在内存中的起始地址会保存在通用寄存器R2中,并传递给内核。而按照习惯或说惯例,通常tag list的首地址(物理地址)会设置为RAM起始地址+ 0x100偏移量,因此R2的值实际上是确定不变的。另外,还要正确设置R0和R1的值,在呼叫内核时,R0的值应为0,R1中则应保存机器类型(machine type)编号。R0,R1和R2都会作为参数传递给内核。 在上面的代码中,定义了一个函数指针theKernel,通过倒数第二条语句将内核入口地址赋给theKernel(hdr是include/image.h中定义的一个image_header结构体类型的数据,hdr->ih_ep中保存了内核入口地址,ntohl的功能是字节顺序的大小端转换,相关代码可以参考tools/mkimage.c),最后,根据APCS规则,将0,bd->bi_arch_number,bd->bi_boot_params 依次作为参数通过R0,R1和R2传递给theKernel函数,并进入内核启动部分。 至此,我们已经从源代码入手简要分析了U-Boot的启动流程,在这个过程中,我们可以理解和认识:为什么要添加这些文件;哪些文件是平台相关的并且必须要根据平台特性进行修改的;哪些文件是平台无关的,是不需要修改的,只需在头文件中作适当配置即可。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |