在介绍该函数之前,我们需要看一看几个数据结构,这些是u-boot中几个重要的数据结构: 1)、gd_t该数据结构保存了u-boot需要的配置信息(我暂时称它为全局信息表), 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? */ #ifdef CONFIG_VFD //我们一般没有配置这个,这个是frame buffer的首地址 unsigned long fb_base; /* base address of frame buffer,显存缓存区 基址*/ #endif #if 0 unsigned long cpu_clk; /* CPU clock in Hz! CPU的时钟频率*/ unsigned long bus_clk; //总线的时钟频率 unsigned long ram_size; /* RAM size, RAM的大小*/ unsigned long reset_status; /* reset status register at boot */ #endif void **jt; /* jump table ,保存着些函数的入口地址,在common/Exports.c中进行填充*/ } gd_t; 2)、bd_t 保存与板子相关的配置参数 typedef struct bd_info { int bi_baudrate; /* serial console baudrate ,串口波特率 */ unsigned long bi_ip_addr; /* IP Address ,IP地址*/ 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; //RAM的起始地址 ulong size; //RAM的大小 } bi_dram[CONFIG_NR_DRAM_BANKS]; } bd_t; 3). 初始化函数列表(以数组的形式) init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup || cpu/arm920t/cpu.c ,cpu的初始化,有待于分析*/ //这个是对板子的初始化, board_init, /* basic board dependent setup|| board/smdk2440/smdk2440.c */ interrupt_init, /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */ //串口初始化后我们就可以打印信息了 console_init_f, /* stage 1 init of console */ display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif dram_init, /* configure available RAM banks */ display_dram_config, NULL, }; //=========================================== //=========================================== int cpu_init (void) //cpu/arm920t/Cpu.c中的函数 { /* * setup up stacks if necessary */ //这里只是做了对中断栈和快速中断栈空间地址的定义 //IRQ_STACK_START 和 FIQ_STACK_START 的值在start.S的开始几行中有定义 //其中的那个 -4 操作是难道是为PC跳转留的一个地址??? #ifdef CONFIG_USE_IRQ IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; #endif return 0; } //=================================================== //=================================================== //---------mem_malloc_init---------------- // 参数: malloc内在区的起始地址 // 功能: 完成malloc函数所要用到的静 // 态变量的初始化. //返回值: 无 //---------------------------------------- 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, //把这段空间初始化为0 mem_malloc_end - mem_malloc_start); } ...... init_fnc_t *init_sequence[] = { //该函数只是做了对中断栈和快速中断栈空间地址的定义 cpu_init, /* basic cpu dependent setup || cpu/arm920t/cpu.c */ //完成各时钟和端口还有gd中的两个成员的初始化 board_init, /* basic board dependent setup|| board/smdk2440/smdk2440.c */ //PWM(Pulse Width Modulation 脉宽调制器) TIMER的初始化, interrupt_init, /* set up exceptions || cpu/arm920t,s3c24x0/interrupts.c */ //环境的初始化,没深入分析 env_init, /* initialize environment */ //初始化波特率,并写进gd的成员变量中 init_baudrate, /* initialze baudrate settings */ //串口初始化后我们就可以打印信息了 serial_init, /* serial communications setup || cpu/arm920t/s3c24x0/serial.c */ console_init_f, /* stage 1 init of console */ //打印一些信息 display_banner, /* say that we are here */ #if defined(CONFIG_DISPLAY_CPUINFO) print_cpuinfo, /* display cpu info (and speed) */ #endif #if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */ #endif //DRAM的初始化,这里只是对gd中的 bi_dram结构中的两个成员赋值, //也即BANK的起始地址和大小 dram_init, /* configure available RAM banks */ //打印BANK的相关信息 display_dram_config, NULL, //用以标识列表数组的结束 }; //------------start_armboot-------------------- //功能: 完成uboot第二阶级的一系列的 // 硬件初始化工作, 然后转入main函数. //备注: 该函数是C程序的入口函数,从汇编语 // 言跳转到此 . //--------------------------------------------- void start_armboot (void) { init_fnc_t **init_fnc_ptr; //init_fnc_t 是各初始化函数的数组 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_t: 定义在 /include/asm-arm/Global_data.h中,包含一些全局通用的变量. _armboot_start: 代码的起始地址,它定义在start.S中的前几行中,定义为 _start 当系统第一次加电时,指令是从0x0地址开始执行的,所以此时的 _start值 应为0x0;而当uboot经过代码重定位后,指令会从 _TEXT_BASE 处开始执行, 此时的 _start值就成了 _TEXT_BASE的值. CFG_MALLOC_LEN: 在/include/configs/smdk2440.h中有定义,该变量表示供 malloc函数使用的内存池空间,代码中定义值为:0x10000+128*1024 |-------|<--- _armboot_start基址 | 4 | |-------|<--- malloc函数池基址 | 3 | |-------|<--- (gd_t)gd(全局变量表)基址 | 2 | |-------|<--- (bt_t)bd(板卡信息表)基址 | 1 | ------- 4 就是为malloc函数预留的数据空间 3 是全信息表gd的数据区 2 是板卡信息表bd的数据区 网上找了个图片,更能反应这个空间的分配关系: */ //分配区域 3 给 gd ,gd是一个全局静态变量 gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); //把gd变量的内容填充为0 ,填充 3 区的数据为0 ,即初始化gd表.注意:这里并没有 //初始化bd表,在gd表中的bd成员只是一个指针,因为对初始化的是个指针地址 memset ((void*)gd, 0, sizeof (gd_t)); /* bd_t 结构体在/include/asm-arm/U-boot.h中定义, 定义板子的一些信息,包括: 波特率,IP地址, 以太网地址, 架构编码,启动参数 ,BANK的起始地址和大小等 */ //分配区域 2 给bd, bd的基址 = gd的基址 - bd的尺寸 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); //把区域 2 填充为 0 ,即初始化 bd 表 memset (gd->bd, 0, sizeof (bd_t)); /*monitor_falsh_len定义在 /lib_arm/Board.c 在bin文件中 BSS 段和 TEXT 段和 DATA 段存放的顺序同前向后依次是: TEXT(代码段 RO) DATA(已初始化数据段 RW) BSS(未初始化数据段 ZI) 所以 _bss_start 的基址等于 TEXT的长度加上DATA的长度. 即: _bss_start(BSS段基址) = 代码段长度+数据段长度 BSS(Block Started by Symbol)段是未被初始化的数据段,是存放程序中 未被初始化的全局变量的一块内存区域,初始化时应清零;该段只有 名称和大小却没有值;该段不包含任何数据,只是简单的维护开始和 结束的地址,以便内存区能在运行时被有效地清零,它在应用程序的 映像文件(ARM中也即bin文件)中并不存在. text :代码段,是包含程序代码的段 dat :已经初始化的数据段,保存已经初始化的全局变量. 在嵌入式系统中,bin文件(又称Image文件)中只包含text和data段, 而bss段不在其中,它是由系统初始化为零. */ //_armboot_start 在start.S中定义为_start,而_start为代码的起始地址 //只包含 RO(TEXT) 和 RW(DATA) 段.重定位前的值为0x0,此时指向flash, //重定位后则指向RAM中的某一地址 //由此可以知道: _bss_start - _armboot_start 的值即是在第一阶段从 //flash中重定位到RAM中的那部分代码的长度,也即可TEXT和DATA数据段, //这个值与start.S中的重定位那部分代码所计算的值是相等的 //所以,monitor_flash_len表示从flash中搬来的代码的长度 monitor_flash_len = _bss_start - _armboot_start; //_bss_start 在u-boot.lds中定位 //各设置的初始化.当返回值不为0时表示初始化失败 ,此时会调用 hang()函数 //打印一错误提示信息,然后进入死循环 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } } //CFG_NO_FLASH 表示没有flash,如果没定义该常量则表示板子上有flash,此时调用flash_init()对其进行初始化. #ifndef CFG_NO_FLASH /* configure available FLASH banks */ size = flash_init (); display_flash_config (size); //打印flash的信息,这里仅输出它的大小 #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 */ #ifdef CONFIG_LCD # ifndef PAGE_SIZE # define PAGE_SIZE 4096 # endif /* * reserve memory for LCD display (always full pages) *///为LCD分配RAM(内存)空间 /* 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 */ /* armboot_start is defined in the board-specific linker script */ //malloc函数使用缓冲区的初始化 mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //如果定义了命令和NAND命令,则初始化nand #if (CONFIG_COMMANDS & CFG_CMD_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif #ifdef CONFIG_HAS_DATAFLASH AT91F_DataflashInit(); dataflash_print_info(); #endif /* initialize environment 环境的初始化,代码在common\env_common.c中 */ env_relocate (); #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); #endif /* CONFIG_VFD */ /* IP Address 为全局变量的成员赋值:IP地址*/ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");//ipaddr在smdk2440.h中的CONFIG_IPADDR中出现,应该是该常量 /* MAC Address *///高处MAC地址 ,并赋给gd的成员变量 { 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", tmp, sizeof (tmp)); s = (i > 0) ? tmp : NULL; for (reg = 0; reg < 6; ++reg) { gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0; if (s) s = (*e) ? e + 1 : e; } #endif } //这个函数涉及好多,我没深入分析,若哪位分析了希望能分享一下:[email protected],将不胜感激 devices_init (); /* get the devices list going. */ #ifdef CONFIG_CMC_PU2 load_sernum_ethaddr (); #endif /* CONFIG_CMC_PU2 */ //初始化跳转表,对gd中的jt(函数跳转表)数组进行初始化,其中保存着一些函数的入口地址 jumptable_init (); console_init_r (); /* fully init console as a device 我没具体分析内部实现*/ #if defined(CONFIG_MISC_INIT_R) /* miscellaneous platform dependent initialisations, miscellaneous:各色各样混在一起, 混杂的*/ misc_init_r (); #endif /* enable exceptions 设置cpsr的I和F位以充许中断*/ 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. */ //直接进入main_loop 该函数在common\main.c中 //!!!!!!!!至此,硬件初始化完成 for (;;) { main_loop (); } /* NOTREACHED - no way out of command loop except booting */ } |