前面已经准备好了sp指针,而且pc也指向了start_armboot,下面就该运行这个c函数了。
start_armboot()在lib_arm/board.c中,我想一行一行的分析,练习基本功。
void start_armboot (void)
首先分析参数和返回值(由于不是计算机专业毕业的,只能做些基础的分析),这个函数的返回值和参数都是空,它对系统的影响只能体现在对全局量的改变上。
272 {
273 init_fnc_t **init_fnc_ptr;
274 char *s;
275 #if defined(CONFIG_VFD) || defined(CONFIG_LCD)
276 unsigned long addr;
277 #endif
init_fnc_t类型的定义在前面
233 typedef int (init_fnc_t) (void);
返回值是int,参数是void。int的返回值,应该是标记程序运行的结果状态。
279
280 gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
281
282 __asm__ __volatile__("": : :"memory");
284 memset ((void*)gd, 0, sizeof (gd_t));
285 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
286 memset (gd->bd, 0, sizeof (bd_t));
288 gd->flags |= GD_FLG_RELOC;
290 monitor_flash_len = _bss_start - _armboot_start;
结构体gd_t保存了一些全局信息,需要用到的时候才看
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console;
unsigned long env_addr;
unsigned long env_valid;
unsigned long fb_base;
#ifdef CONFIG_VFD
unsigned char vfd_type;
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
void **jt;
} gd_t;
其中成员bd_t是板子信息
typedef struct bd_info {
int bi_baudrate;
unsigned long bi_ip_addr;
struct environment_s *bi_env;
ulong bi_arch_number;
ulong bi_boot_params;
struct
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
从成员名字上就能看出各个的作用,其中有波特率,ip地址,环境参数,板子id,板子参数,内存等信息。
从_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
可以看出_armboot_start = _start = TEXT_BASE
而Config.mk (board\samsung\smdk2410):TEXT_BASE = 0x33F80000
所以_armboot_start = 0x33F80000
#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128*1024)
#define CONFIG_ENV_SIZE 0x10000
CONFIG_SYS_MALLOC_LEN = 64K + 128K = 192K
其中64K用来放环境变量,另外128K用来作为动态内存(malloc分配),看来u_boot只需要128K内存就可以了。
第280行指定gd存放地址
第285行指定gd的成员gd->bd存放地址
内存分配的一部分,如下图所示:
另外几个暂时不知道意思,用到时候再看
292 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
293 if ((*init_fnc_ptr)() != 0) {
294 hang ();
295 }
296 }
init_sequence在同一文件的上面一点定义如下:
237 init_fnc_t *init_sequence[] = {
238 #if defined(CONFIG_ARCH_CPU_INIT)
239 arch_cpu_init,
240 #endif
241 board_init,
242 #if defined(CONFIG_USE_IRQ)
243 interrupt_init,
244 #endif
245 timer_init,
246 #ifdef CONFIG_FSL_ESDHC
247 get_clocks,
248 #endif
249 env_init,
250 init_baudrate,
251 serial_init,
252 console_init_f,
253 display_banner,
254 #if defined(CONFIG_DISPLAY_CPUINFO)
255 print_cpuinfo,
256 #endif
257 #if defined(CONFIG_DISPLAY_BOARDINFO)
258 checkboard,
259 #endif
260 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
261 init_func_i2c,
262 #endif
263 dram_init,
264 #if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
265 arm_pci_init,
266 #endif
267 display_dram_config,
268 NULL,
269 };
第293行 挨个运行其中初始化函数,每个函数都是没有参数的,返回值表示运行结果状态。
这里以文件include/configs/sbc2410x.h为线索。
1. board/sbc2410x/sbc2410x.c中
int board_init (void)
没有参数的函数,和前面分析的定义一样。
72
76 int board_init (void)
77 {
78 struct s3c24x0_clock_power * const clk_power =
79 s3c24x0_get_base_clock_power();
80 struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();
82
83 clk_power->LOCKTIME = 0xFFFFFF;
85
86 clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
88
89 delay (4000);
91
92 clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
94
95 delay (8000);
97
98 gpio->GPACON = 0x007FFFFF;
99 gpio->GPBCON = 0x00044556;
100 gpio->GPBUP = 0x000007FF;
101 gpio->GPCCON = 0xAAAAAAAA;
102 gpio->GPCUP = 0x0000FFFF;
103 gpio->GPDCON = 0xAAAAAAAA;
104 gpio->GPDUP = 0x0000FFFF;
105 gpio->GPECON = 0xAAAAAAAA;
106 gpio->GPEUP = 0x0000FFFF;
107 gpio->GPFCON = 0x000055AA;
108 gpio->GPFUP = 0x000000FF;
109 gpio->GPGCON = 0xFF95FF3A;
110 gpio->GPGUP = 0x0000FFFF;
111 gpio->GPHCON = 0x0016FAAA;
112 gpio->GPHUP = 0x000007FF;
114 gpio->EXTINT0=0x22222222;
115 gpio->EXTINT1=0x22222222;
116 gpio->EXTINT2=0x22222222;
118
119 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
121
122 gd->bd->bi_boot_params = 0x30000100;
124 icache_enable();
125 dcache_enable();
127 return 0;
128 }
这样子的函数,由于没有参数,返回值也只是反映运行的结果状态,很好懂。
总的来说,设置了时钟、gpio、gd->bd->bi_arch_number、gd->bd->bi_boot_params、打开数据和指令cache。
1.1 分析一下delay()这个函数
65 static inline void delay (unsigned long loops)
66 {
67 __asm__ volatile ("1:\n"
68 "subs %0, %1, #1\n"
69 "bne 1b":"=r" (loops):"0" (loops));
70 }
是个内联函数,没有返回值,参数是unsigned long,表示延时循环的次数。
内部是嵌入汇编,看不懂的话,看看《linux内核代码情景分析》第一章就没有问题了。
展开为汇编如下(这里假设loops变量存放在寄存器t0中):
1:
subs t0, t0, #1 //运行t0 = t0 -1
bne 1b //如果t0 == 0结束,否则跳回标号1处,运行
一共运行loops次结束,用这个方式产生延时。如第89行的delay (4000)语句是延时cpu运行4000次上面指令的时间,和现实中我们熟悉的时间单位s,ms,us,ns等没有明确关系。
1.2 分析icache_enable()
<lib_arm/cache-cp15.c>
void icache_enable(void)
{
cache_enable(CR_I);
}
没有返回值,也没有参数,是个简单函数
static void cache_enable(uint32_t cache_bit)
{
uint32_t reg;
reg = get_cr();
cp_delay();
set_cr(reg | cache_bit);
}
<include/asm-arm/system.h>
static inline unsigned int get_cr(void)
{
unsigned int val;
asm("mrc p15, 0, %0, c1, c0, 0 @ get CR" : "=r" (val) : : "cc");
return val;
}
cp15 register1 bit2 for dcache, bit12 for icache
static void cp_delay (void)
{
volatile int i;
for (i = 0; i < 100; i++)
nop();
}
#define nop() __asm__ __volatile__("mov\tr0,r0\t@ nop\n\t");
相当于mov r0 r0
r0 = r0没啥意义,浪费时间用的
static inline void set_cr(unsigned int val)
{
asm volatile("mcr p15, 0, %0, c1, c0, 0 @ set CR"
: : "r" (val) : "cc");
isb();
}
#define isb() __asm__ __volatile__ ("" : : : "memory")
相当于内存同步
设置cp15 register1 中 cache相应的位
1.3 void dcache_enable(void)
和icache_enable()一样
2. < cpu/arm920t/s3c24x0/timer.c>
int timer_init(void)
简单函数
52 int timer_init(void)
53 {
54 struct s3c24x0_timers *timers = s3c24x0_get_base_timers();
55 ulong tmr;
56
57
58
59 writel(0x0f00, &timers->TCFG0);
60 if (timer_load_val == 0) {
61
66 timer_load_val = get_PCLK() / (2 * 16 * 100);
67 timer_clk = get_PCLK() / (2 * 16);
68 }
69
70 lastdec = timer_load_val;
71 writel(timer_load_val, &timers->TCNTB4);
72
73 tmr = (readl(&timers->TCON) & ~0x0700000) | 0x0600000;
74 writel(tmr, &timers->TCON);
75
76 tmr = (tmr & ~0x0700000) | 0x0500000;
77 writel(tmr, &timers->TCON);
78 timestamp = 0;
80 return (0);
81 }
#define writel(v,a) __arch_putl(v,a)
#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))
可见本处理器是io和ram统一编制,直接取地址就可以了
本函数就是始终相关的初始化,简单,要结合寄存器手册看
3. <common/env_flash.c>
246 int env_init(void)
247 {
248 if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
249 gd->env_addr = (ulong)&(env_ptr->data);
250 gd->env_valid = 1;
251 return(0);
252 }
254 gd->env_addr = (ulong)&default_environment[0];
255 gd->env_valid = 0;
256 return (0);
257 }
简单函数
uint32_t ZEXPORT crc32 (uint32_t crc, const Bytef *p, uInt len)
{
return crc32_no_comp(crc ^ 0xffffffffL, p, len) ^ 0xffffffffL;
}
算出CRC校验值,比较是否正确。有兴趣的可以看看crc如何算出来的。
env_t *env_ptr = (env_t *)CONFIG_ENV_ADDR;
在include/configs/sbc2410x.h中可以算出CONFIG_ENV_ADDR的值
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x0F0000)
#define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1
#define PHYS_FLASH_1 0x00000000
这种情况下是0x0F0000,这个地址是属于flash
在0x0F0000处存放结构体env_t
typedef struct environment_s {
uint32_t crc;
unsigned char data[ENV_SIZE];
} env_t;
干啥用的,以后用的时候才能知道,也才需要知道
第254行,如果没有定义,就采用默认的。
默认的是default_environment,可以看到里面都是启动配置信息
4. <lib_arm/board.c>
125 static int init_baudrate (void)
126 {
127 char tmp[64];
128 int i = getenv_r ("baudrate", tmp, sizeof (tmp));
129 gd->bd->bi_baudrate = gd->baudrate = (i > 0)
130 ? (int) simple_strtoul (tmp, NULL, 10)
131 : CONFIG_BAUDRATE;
133 return (0);
134 }
第128行在上面的环境变量中查找baudrate字符串,看看有没有设置正确的波特率,有的话就设置上。
5. <drivers/serial/serial_s3c24x0.c>
163 int serial_init(void)
164 {
165 return serial_init_dev(UART_NR);
166 }
124 static int serial_init_dev(const int dev_index)
125 {
126 struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
128 #ifdef CONFIG_HWFLOW
129 hwflow = 0;
130 #endif
132
133 writel(0x07, &uart->UFCON);
134 writel(0x0, &uart->UMCON);
136
137 writel(0x3, &uart->ULCON);
138
142 writel(0x245, &uart->UCON);
144 #ifdef CONFIG_HWFLOW
145 writel(0x1, &uart->UMCON);
146 #endif
148
149 #if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
150
151 if (dev_index == 0 || dev_index == 1)
152 writel(0x10, &uart->UMCON);
153 #endif
154 _serial_setbrg(dev_index);
156 return (0);
157 }
94 void _serial_setbrg(const int dev_index)
95 {
96 struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
97 unsigned int reg = 0;
98 int i;
99
100
101 reg = get_PCLK() / (16 * gd->baudrate) - 1;
102
103 writel(reg, &uart->UBRDIV);
104 for (i = 0; i < 100; i++)
105 ;
106 }
初始化串口寄存器,相应的要看寄存器手册
6. <common/console.c>
522
523 int console_init_f(void)
524 {
525 gd->have_console = 1;
527 #ifdef CONFIG_SILENT_CONSOLE
528 if (getenv("silent") != NULL)
529 gd->flags |= GD_FLG_SILENT;
530 #endif
532 return 0;
533 }
设置gd->have_console,表示有console
7. <lib_arm/board.c>
136 static int display_banner (void)
137 {
138 printf ("\n\n%s\n\n", version_string);
139 debug ("U-Boot code: lX -> lX BSS: -> lX\n",
140 _armboot_start, _bss_start, _bss_end);
141 #ifdef CONFIG_MODEM_SUPPORT
142 debug ("Modem Support enabled\n");
143 #endif
144 #ifdef CONFIG_USE_IRQ
145 debug ("IRQ Stack: lx\n", IRQ_STACK_START);
146 debug ("FIQ Stack: lx\n", FIQ_STACK_START);
147 #endif
148
149 return (0);
150 }
打印信息
8. <board/sbc2410x/sbc2410x.c>
130 int dram_init (void)
131 {
132 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
133 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
135 return 0;
136 }
#define CONFIG_NR_DRAM_BANKS 1
#define PHYS_SDRAM_1 0x30000000
#define PHYS_SDRAM_1_SIZE 0x04000000
有64M内存,在0x30000000处
9. <lib_arm/board.c>
159 static int display_dram_config (void)
160 {
161 int i;
171 ulong size = 0;
173 for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {
174 size += gd->bd->bi_dram[i].size;
175 }
176 puts("DRAM: ");
177 print_size(size, "\n");
180 return (0);
181 }
打印出内存信息
原文见:http://blog.sina.com.cn/s/blog_559f6ffc0100mgw9.html