1:上一节讲到start.S中进行了一系列的SoC相关硬件初始化以后进行了长跳转到start_armboot 函数中;
start_armboot进一步初始化board中硬件,并设置了uboot下的命令行、环境变量、基本命令、跳转到kernel
下面详细介绍start_armboot中的代码:
------------------------第一段代码---------------------------------------------
红色代码为条件编译以后要执行的代码
1 void start_armboot (void) 2 { 3 init_fnc_t **init_fnc_ptr; 4 char *s; 5 int mmc_exist = 0; 6 #if !defined(CFG_NO_FLASH) || defined (CONFIG_VFD) || defined(CONFIG_LCD) 7 ulong size; 8 #endif 9 10 #if defined(CONFIG_VFD) || defined(CONFIG_LCD) 11 unsigned long addr; 12 #endif 13 14 #if defined(CONFIG_BOOT_MOVINAND) 15 uint *magic = (uint *) (PHYS_SDRAM_1); 16 #endif 17 18 /* Pointer is writable since we allocated a register for it */ 19 #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ 20 ulong gd_base; 21 22 gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t); 23 #ifdef CONFIG_USE_IRQ 24 gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); 25 #endif 26 gd = (gd_t*)gd_base; 27 #else //CONFIG_MEMORY_UPPER_CODE 28 gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); 29 #endif 30 31 if (readl(INF_REG_BASE+INF_REG0_OFFSET)==0xFFAADDEE) 32 { 33 extern int gbl_silent; 34 gbl_silent = 1; 35 } 36 37 /* compiler optimization barrier needed for GCC >= 3.4 */ //这段是c语言内嵌汇编,为了实现内存墙; 38 __asm__ __volatile__("": : :"memory"); 39 40 memset ((void*)gd, 0, sizeof (gd_t)); 41 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 42 memset (gd->bd, 0, sizeof (bd_t)); 43 44 monitor_flash_len = _bss_start - _armboot_start; 45 46 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { 47 if ((*init_fnc_ptr)() != 0) { 48 hang (); 49 } 50 }-------------------未完待续------------------------
首先看一下
init_fnc_t **init_fnc_ptr;这个变量,这是一个init_fnc_t 类型的二重指针;typedef int (init_fnc_t) (void);可以看出init_fnc_t类型为 返回值为int 传参为空的函数类型,看一下下面这段代码:对
init_fnc_ptr 赋值为init_sequence(init_sequence为一个函数指针数组,这个数字为一个全局变量,存放的是硬件初始化有关的一些函数这些函数类型都是init_fnc_t类型),因此下面for循环的作用就是遍历init_sequence数组中的所有函数,并执行这些函数;如果这些初始化函数的返回值为0的话则执行hang() 挂起函数;hang函数的作用是输出puts ("### ERROR ### Please RESET the board ###\n");并进入一个死循环;总结一下:这段代码是初始化一个全局变量数组,数组中存放一些硬件初始化相关的函数指针,遍历这些函数,并进行相应硬件的初始化;这些全局变量放在数据段;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }
1 init_fnc_t *init_sequence[] = { 2 cpu_init, /* basic cpu dependent setup */ 3 #if defined(CONFIG_SKIP_RELOCATE_UBOOT) 4 reloc_init, /* Set the relocation done flag, must 5 do this AFTER cpu_init(), but as soon 6 as possible */ 7 #endif 8 board_init, /* basic board dependent setup */ 9 interrupt_init, /* set up exceptions */ 10 env_init, /* initialize environment */ 11 init_baudrate, /* initialze baudrate settings */ 12 serial_init, /* serial communications setup */ 13 console_init_f, /* stage 1 init of console */ 14 display_banner, /* say that we are here */ 15 #if defined(CONFIG_DISPLAY_CPUINFO) 16 print_cpuinfo, /* display cpu info (and speed) */ 17 #endif 18 #if defined(CONFIG_DISPLAY_BOARDINFO) 19 checkboard, /* display board info */ 20 #endif 21 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) 22 init_func_i2c, 23 #endif 24 dram_init, /* configure available RAM banks */ 25 display_dram_config, 26 NULL, 27 };
1 void hang (void) 2 { 3 puts ("### ERROR ### Please RESET the board ###\n"); 4 for (;;); 5 }
下面这段代码是为gd_base、gd_bd、两个全局变量分配内存地址;gd_t类型为结构体其中的内容为:大小为36byte;
{ bd_t 类型指针 //4字节 flag 无符号整形 //4字节 baudrate 波特率 无符号整形 //4字节 have_console 无符号整形 //4字节 reloc_off; /* Relocation Offset */ //4字节 env_addr //4字节 env_valid //4字节 unsigned long fb_base LCD的内存基地址 //4字节 void **jt; /* jump table */ //指针4字节 } bd_t 也为一结构体,大小为42字节{ int 波特率 //4字节 unsigned int IP地址 //4字节 unsigned char 网卡地址 //6字节 环境变量指针 //4字节 机器码 //4字节 启动参数 //4字节 内存配置结构体 //8字节*2 }
下面这段代码的作用:gd_base 为0x23e00000 + 0x200000 - 912K -512K - 36byte这个地址用来存放这个全局变量
同样 bd_t 全局变量的地址设置在gd_base往下移动42byte的地址;
从这段代码我们可以看出uboot是如何进行内存分配的;
可以看下图:下图为uboot中的内存;
这段代码作的事情就是为gd_t、bd_t两个结构体分配内存地址,并初始化gd、gd->bd指针分别指向这两个结构体;
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
gd为一个register volatile结构体指针;asm ("r8")意思是放在r8寄存器中;
20 ulong gd_base; 21 22 gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t); 23 26 gd = (gd_t*)gd_base; 31 36 37 /* compiler optimization barrier needed for GCC >= 3.4 */ //这段是c语言内嵌汇编,为了实现内存墙; 38 __asm__ __volatile__("": : :"memory"); 39 40 memset ((void*)gd, 0, sizeof (gd_t)); 41 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); 42 memset (gd->bd, 0, sizeof (bd_t)); 43 44 monitor_flash_len = _bss_start - _armboot_start;
1 typedef struct global_data { 2 bd_t *bd; 3 unsigned long flags; 4 unsigned long baudrate; 5 unsigned long have_console; /* serial_init() was called */ 6 unsigned long reloc_off; /* Relocation Offset */ 7 unsigned long env_addr; /* Address of Environment struct */ 8 unsigned long env_valid; /* Checksum of Environment valid? */ 9 unsigned long fb_base; /* base address of frame buffer */ 10 #ifdef CONFIG_VFD 11 unsigned char vfd_type; /* display type */ 12 #endif 13 #if 0 14 unsigned long cpu_clk; /* CPU clock in Hz! */ 15 unsigned long bus_clk; 16 phys_size_t ram_size; /* RAM size */ 17 unsigned long reset_status; /* reset status register at boot */ 18 #endif 19 void **jt; /* jump table */ 20 } gd_t;
1 typedef struct bd_info { 2 int bi_baudrate; /* serial console baudrate */ 3 unsigned long bi_ip_addr; /* IP Address */ 4 unsigned char bi_enetaddr[6]; /* Ethernet adress */ 5 struct environment_s *bi_env; 6 ulong bi_arch_number; /* unique id for this board */ 7 ulong bi_boot_params; /* where this board expects params */ 8 struct /* RAM configuration */ 9 { 10 ulong start; 11 ulong size; 12 } bi_dram[CONFIG_NR_DRAM_BANKS]; 13 #ifdef CONFIG_HAS_ETH1 14 /* second onboard ethernet port */ 15 unsigned char bi_enet1addr[6]; 16 #endif 17 } bd_t;
下面看一下init_sequence数组中有有哪些函数:
init_fnc_t *init_sequence[] = { cpu_init, /* basic cpu dependent setup */ #if defined(CONFIG_SKIP_RELOCATE_UBOOT) reloc_init, /* Set the relocation done flag, must do this AFTER cpu_init(), but as soon as possible */ #endif board_init, /* basic board dependent setup */ interrupt_init, /* set up exceptions */ env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ 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 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) init_func_i2c, #endif dram_init, /* configure available RAM banks */ display_dram_config, NULL, };
函数1:cpu_init函数;因为cpu相关的初始化已经在_start函数中做了,所以这里什么也没有做;
1 int cpu_init (void) 2 { 3 /* 4 * setup up stacks if necessary 5 */ 6 #ifdef CONFIG_USE_IRQ //这个未定义 7 IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4; 8 FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; 9 #endif 10 return 0; 11 }
函数2:board_init函数;这个函数中初始化了dm9000网卡,并且对gd->bd中的机器码以及启动参数赋值;
这里要注意一下:uboot中赋值的机器码要和linux内核中的机器码要一致,否则不能正常启动;
boot参数为:0x02000000+0x100;
1 int board_init(void) 2 { 3 DECLARE_GLOBAL_DATA_PTR; 4 5 6 #ifdef CONFIG_DRIVER_DM9000 7 dm9000_pre_init(); 8 #endif 9 10 gd->bd->bi_arch_number = MACH_TYPE; 11 gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100); 12 13 return 0; 14 }
函数3:interrupt_init中的初始化
1 int interrupt_init(void) 2 { 3 4 S5PC11X_TIMERS *const timers = S5PC11X_GetBase_TIMERS(); 5 6 /* use PWM Timer 4 because it has no output */ 7 /* prescaler for Timer 4 is 16 */ 8 timers->TCFG0 = 0x0f00; //设置预分频为15 +1 = 16 9 if (timer_load_val == 0) { 10 /* 11 * for 10 ms clock period @ PCLK with 4 bit divider = 1/2 12 * (default) and prescaler = 16. Should be 10390 13 * @33.25MHz and @ 66 MHz 14 */ 15 timer_load_val = get_PCLK() / (16 * 100); //设置为10ms 16 } 17 18 /* load value for 10 ms timeout */ 19 lastdec = timers->TCNTB4 = timer_load_val; 20 /* auto load, manual update of Timer 4 */ 21 timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE; 22 /* auto load, start Timer 4 */ 23 timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON; 24 timestamp = 0; 25 26 27 return (0); 28 }
代码解析:
typedef vu_long S5PC11X_REG32;
S5PC11X_TIMERS:定义了一个结构体类型,把与时钟有关的所有所有寄存器都存放在这个结构体内
typedef struct { S5PC11X_REG32 TCFG0; S5PC11X_REG32 TCFG1; S5PC11X_REG32 TCON; S5PC11X_TIMER ch[4]; S5PC11X_REG32 TCNTB4; S5PC11X_REG32 TCNTO4; } /*__attribute__((__packed__))*/ S5PC11X_TIMERS;
这句代码的意思就是把
S5PC11X_TIMERS *const timers = S5PC11X_GetBase_TIMERS();
S5PC11X_GetBase_TIMERS函数:的作用就是把timer寄存器的基地址强制类型转换为S5PC11X_TIMERS * 类型然后赋值给 timers变量;timers->TCFG0实际就是代表基地址右移4字节
之后的地址中的值,直接赋值相当于把0x0f00这个值放到 TCFG0对应的寄存器地址处,但是这个要注意的是,寄存器必须设置为连续的/或者一一对应的,否则会造成赋值的地址错误;
timers->TCFG0 = 0x0f00;
static inline S5PC11X_TIMERS * S5PC11X_GetBase_TIMERS(void) { return (S5PC11X_TIMERS *)ELFIN_TIMER_BASE;}
剩下的代码就和裸机的代码一致了;TCON的timer4的相应控制为清0,设置为自动reload,并且第一次要手动载入,然后时在清0,设置reload,开启timer4
timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE; 22 /* auto load, start Timer 4 */ 23 timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
---------------------------------- 函数4:env_init
1 int env_init(void) 2 { 3 #if defined(ENV_IS_EMBEDDED) 4 ulong total; 5 int crc1_ok = 0, crc2_ok = 0; 6 env_t *tmp_env1, *tmp_env2; 7 8 total = CFG_ENV_SIZE; // tatal = 0x4000 16k的大小,环境变量整个大小为16k 9 10 tmp_env1 = env_ptr; 11 tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE); 12 13 crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); 14 crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); 15 16 if (!crc1_ok && !crc2_ok) 17 gd->env_valid = 0; 18 else if(crc1_ok && !crc2_ok) 19 gd->env_valid = 1; 20 else if(!crc1_ok && crc2_ok) 21 gd->env_valid = 2; 22 else { 23 /* both ok - check serial */ 24 if(tmp_env1->flags == 255 && tmp_env2->flags == 0) 25 gd->env_valid = 2; 26 else if(tmp_env2->flags == 255 && tmp_env1->flags == 0) 27 gd->env_valid = 1; 28 else if(tmp_env1->flags > tmp_env2->flags) 29 gd->env_valid = 1; 30 else if(tmp_env2->flags > tmp_env1->flags) 31 gd->env_valid = 2; 32 else /* flags are equal - almost impossible */ 33 gd->env_valid = 1; 34 } 35 36 if (gd->env_valid == 1) 37 env_ptr = tmp_env1; 38 else if (gd->env_valid == 2) 39 env_ptr = tmp_env2; 40 #else /* ENV_IS_EMBEDDED */ 41 gd->env_addr = (ulong)&default_environment[0]; 42 gd->env_valid = 1; 43 #endif /* ENV_IS_EMBEDDED */ 44 45 return (0); 46 }
执行的是红色的代码:即把common.c中初始化好的default_environment地址赋值到gd->env_addr中,env_valid 赋值为1; 这里对字符串数组的初始化有些疑问???????
uchar default_environment[] = { #endif #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif 。。。。。。。。。。。。。。。。。。。。。 #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=1\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" };
---------------------------------------------------------
函数5:init_baudrate初始化波特率:从env中获取波特率 赋值给gd->bd->bi_baudrate gd->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); }
/*这个函数的作用是读取环境变量name 到 缓存buf中,读取成功返回n大于0,失败返回0*/ 1 int getenv_r (char *name, char *buf, unsigned len) 2 { 3 int i, nxt; 4 5 for (i=0; env_get_char(i) != ‘\0‘; i=nxt+1) { 6 int val, n; 7 8 for (nxt=i; env_get_char(nxt) != ‘\0‘; ++nxt) { 9 if (nxt >= CFG_ENV_SIZE) { 10 return (-1); 11 } 12 } 13 if ((val=envmatch((uchar *)name, i)) < 0) 14 continue; 15 /* found; copy out */ 16 n = 0; 17 while ((len > n++) && (*buf++ = env_get_char(val++)) != ‘\0‘) 18 ; 19 if (len == n) 20 *buf = ‘\0‘; 21 return (n); 22 } 23 return (-1); 24 }
uchar env_get_char (int index) { uchar c; /* if relocated to RAM */ if (gd->flags & GD_FLG_RELOC) c = env_get_char_memory(index); else c = env_get_char_init(index); return (c); }
1 int envmatch (uchar *s1, int i2) 2 { 3 4 while (*s1 == env_get_char(i2++)) 5 if (*s1++ == ‘=‘) 6 return(i2); 7 if (*s1 == ‘\0‘ && env_get_char(i2-1) == ‘=‘) 8 return(i2); 9 return(-1); 10 }
1 uchar env_get_char_memory (int index) 2 { 3 if (gd->env_valid) { 4 return ( *((uchar *)(gd->env_addr + index)) ); 5 } else { 6 return ( default_environment[index] ); 7 } 8 }