在分析start.S文件过程中提到过,最后从汇编跳到C函数执行的是start_armboot函数,位于lib_armoard.c文件下,它的执行流程图如下,截图来源于《嵌入式LINUX应用开发完全手册》。根据流程图,以下内容大致分几步写:
1、gd全局变量初始化
2、调用init_sequence函数指针数组里的初始化函数、nand初始化、环境变量初始化、USB初始化
3、死循环main_loop()分析
1、gd全局变量初始化
gd是全局引用的变量,它的定义在Global_data.h (includeasm-arm)中,它利用的是CPU的寄存器r8。只有在文件中引用DECLARE_GLOBAL_DATA_PTR ,就可以使用gd这个变量
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
它是一个指向gd_t结构体的指针,gd_t结构体如下所示
typedef struct global_data { bd_t *bd; //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 */ //lcd的缓存地址 #ifdef CONFIG_VFD unsigned char vfd_type; /* display type */ #endif #if 0 unsigned long cpu_clk; /* CPU clock in Hz! */ unsigned long bus_clk; unsigned long ram_size; /* RAM size */ unsigned long reset_status; /* reset status register at boot */ #endif void **jt; /* jump table */ } gd_t;
其中env_addr、baudrate、bd等参数比较重要,bd也是一个结构体,在U-boot.h (includeasm-arm)里定义,定义如下所示,这里面bi_arch_number与bi_boot_params这两个参数是传给内核的,很重要。
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 */ //物理网络地址,即MAC Address,网卡决定,不可配置 struct environment_s *bi_env; //指向环境变量的指针 ulong bi_arch_number; /* unique id for this board *///CPU架构号码,传给内核 ulong bi_boot_params; /* where this board expects params *///标记列表的开始地址,传给内核,告诉内核从这个地方取参数 struct /* RAM configuration */ { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS];//sdram的起始地址与大小 #ifdef CONFIG_HAS_ETH1 /* second onboard ethernet port */ unsigned char bi_enet1addr[6]; #endif } bd_t;
environment_s结构体定义在Environment.h (include)中,如下所示。环境变量就是以这个格式存储在nand中的
#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)//0x20000-5,减去的5为crc校验与flags占用的 typedef struct environment_s { unsigned long crc; /* CRC32 over data bytes *///crc校验 #ifdef CFG_REDUNDAND_ENVIRONMENT unsigned char flags; /* active/obsolete flags *///环境变量标志 #endif unsigned char data[ENV_SIZE]; /* Environment data *///环境变量具体的数据。最大 } env_t;
start_armboot函数一开始先初始化gd变量。gd变量所指向的内容占用128字节存放在堆区后面,栈区前面。
/* Pointer is writable since we allocated a register for it */ gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));//gd地址向上增长 /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t));//清0 gd段 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));//求出bd段地址 memset (gd->bd, 0, sizeof (bd_t));//清0gd->bd段 monitor_flash_len = _bss_start - _armboot_start;//显示需要的flash长度
2、调用init_sequence函数指针数组里的初始化函数、nand初始化、环境变量初始化、USB初始化
start_armboot函数继续往下执行,执行初始化函数数组里的函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {//初始化各个函数 by andy if ((*init_fnc_ptr)() != 0) {//函数的返回值不为0,认为出错,打印出错信息 by andy hang ();//打印出错信息 by andy } }
init_sequence数组的内容如下所示
init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup *///CPU的一些堆栈大小设置 by andy board_init, /* basic board dependent setup *///设置芯片代码、设置与内核交互的地址 by andy interrupt_init, /* set up exceptions *///10ms时钟定时器设置 by andy env_init, /* initialize environment *///初始化环境变量,采用默认环境变量 by andy init_baudrate, /* initialze baudrate settings *///初始化串口波特率为115200 by andy serial_init, /* serial communications setup *///初始化串口在cpu/arm920t/s3c24x0中实现 by andy console_init_f, /* stage 1 init of console *///设置控制台初始化标志 by andy display_banner, /* say that we are here *///打印UBOOT版本信息 by andy #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 *///内存起始地址以及大小设置 by andy display_dram_config,//打印出DRAM的大小 by andy NULL, };
start_armboot函数继续往下执行,清0分配的堆区的内容
/* armboot_start is defined in the board-specific linker script */ mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);//清0堆区的内容
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);//清0堆区的内容 }
start_armboot函数继续往下执行,初始化nand,环境变量的内容存储在nand中
puts ("NAND: ");//打印出nand的大小 nand_init(); /* go init the NAND *///初始化nand flash
start_armboot函数继续往下执行,重新检测环境变量,环境变量在初始化函数数组中已经初始化过,因为nand初始化后,所以再检测一般环境变量是否需要重新加载。env_relocate 函数大致的意思是检查nand中的存放环境变量位置的crc校验是否有效,如果无效则采用默认的环境变量,如果有效则采用nand中的环境变量
/* initialize environment */ env_relocate ();//初始化环境变量,crc有效的话从nand中读取存储的环境变量,否则采用默认的环境变量 by andy
默认的环境变量的值如下
uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "