第二章 Vivi源代码详细分析
2.1 Vivi的启动流程
由于 Boot Loader 的实现依赖于 CPU 的体系结构,因此大多数 Boot Loader 都分为 stage1 和 stage2 两大部分。依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在 stage1 中,而且通常都用汇编语言来实现,以达到短小精悍的目的。而 stage2 则通常用C语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。
2.1.1 第一阶段(Stage 1)
第一阶段的启动代码在cpu\\
? 为加载 Boot Loader 的 stage2 准备 RAM 空间。 ? 拷贝 Boot Loader 的 stage2 到 RAM 空间中。 ? 设置好堆栈。
? 跳转到 stage2 的 C 入口点。
1 、基本的硬件初始化
这是 Boot Loader 一开始就执行的操作,其目的是为 stage2 的执行以及随后的 kernel 的执行准备好一些基本的硬件环境。它通常包括以下步骤(以执行的先后顺序): 1. 屏蔽所有的中断。为中断提供服务通常是 OS 设备驱动程序的责任,因此在 Boot
Loader 的执行全过程中可以不必响应任何中断。中断屏蔽可以通过写 CPU 的中断屏蔽寄存器或状态寄存器(比如 ARM 的 CPSR 寄存器)来完成。 Reset: @ disable watch dog timer ;禁止看门狗计时器
mov r1, #0x53000000 ;WTCON寄存器地址是
0x53000000,清0
mov r2, #0x0 str r2, [r1]
#ifdef CONFIG_S3C2410_MPORT3 ;不符合条件,跳到下面的关中断 /**** 在/vivi/include/autoconf.h中#undef CONFIG_S3C2410_MPORT3******/ mov r1, #0x56000000 ;GPACON寄存器地址是
0x56000000
mov r2, #0x00000005 str r2, [r1, #0x70] ;配置GPHCON寄存器 mov r2, #0x00000001 str r2, [r1, #0x78] ;配置GPHUP寄存器 mov r2, #0x00000001
6
str r2, [r1, #0x74] #endif @ disable all interrupts mov r1, #INT_CTL_BASE mov r2, #0xffffffff str r2, [r1, #oINTMSK] ldr r2, =0x7ff str r2, [r1, #oINTSUBMSK]
;配置GPHDAT寄存器 ;禁止全部中断
;掩码关闭所有中断
2. 设置 CPU 的速度和时钟频率。 @ initialise system clocks mov r1, #CLK_CTL_BASE mvn r2, #0xff000000 str r2, [r1, #oLOCKTIME] @ldr r2, mpll_50mhz @str r2, [r1, #oMPLLCON]
;初始化系统时钟
3. RAM 初始化。包括正确地设置系统的内存控制器的功能寄存器以及各内存库控制寄存
器等。 ENTRY(memsetup) @ initialise the static memory @ set memory control registers ;设置内存控制寄存器的初值 mov r1, #MEM_CTL_BASE adrl r2, mem_cfg_val
4. 初始化 LED。典型地,通过 GPIO 来驱动 LED,其目的是表明系统的状态是 OK 还
是 Error。如果板子上没有 LED,那么也可以通过初始化 UART 向串口打印 Boot Loader 的 Logo 字符信息来完成这一点。 @ All LED on ;点亮开发板上的LED mov r1, #GPIO_CTL_BASE add r1, r1, #oGPIO_F ;LED使用GPIOF组的管脚
ldr r2,=0x55aa ;使能EINT0,EINT1,EINT2,EINT3,
;另四个管脚配置成输出,屏蔽EINT4,5,6,7
str r2, [r1, #oGPIO_CON] mov r2, #0xff str r2, [r1, #oGPIO_UP] ;disable the pull-up function mov r2, #0x00 str r2, [r1, #oGPIO_DAT] #endif
7
5. 关闭 CPU 内部指令/数据 cache。
2、 为加载 stage2 准备 RAM 空间
为了获得更快的执行速度,通常把 stage2 加载到 RAM 空间中来执行,因此必须为加载 Boot Loader 的 stage2 准备好一段可用的 RAM 空间范围。
由于 stage2 通常是 C 语言执行代码,因此在考虑空间大小时,除了 stage2 可执行映象的大小外,还必须把堆栈空间也考虑进来。此外,空间大小最好是 memory page 大小(通常是 4KB)的倍数。一般而言,1M 的 RAM 空间已经足够了。具体的地址范围可以任意安排,比如 blob 就将它的 stage2 可执行映像安排到从系统 RAM 起始地址 0xc0200000 开始的 1M 空间内执行。但是,将 stage2 安排到整个 RAM 空间的最顶 1MB(也即(RamEnd-1MB) - RamEnd)是一种值得推荐的方法。
为了后面的叙述方便,这里把所安排的 RAM 空间范围的大小记为:stage2_size(字节),把起始地址和终止地址分别记为:stage2_start 和 stage2_end(这两个地址均以 4 字节边界对齐)。因此:
stage2_end=stage2_start+stage2_size
另外,还必须确保所安排的地址范围的的确确是可读写的 RAM 空间,因此,必须对你所安排的地址范围进行测试。具体的测试方法可以采用类似于 blob 的方法,也即:以 memory page 为被测试单位,测试每个 memory page 开始的两个字是否是可读写的。为了后面叙述的方便,我们记这个检测算法为:test_mempage,其具体步骤如下: 1. 先保存 memory page 一开始两个字的内容。
2. 向这两个字中写入任意的数字。比如:向第一个字写入 0x55,第 2 个字写入 0xaa。 3. 然后,立即将这两个字的内容读回。显然,我们读到的内容应该分别是 0x55 和 0xaa。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。 4. 再向这两个字中写入任意的数字。比如:向第一个字写入 0xaa,第 2 个字中写入 0x55。
5. 然后,立即将这两个字的内容立即读回。显然,我们读到的内容应该分别是 0xaa 和 0x55。如果不是,则说明这个 memory page 所占据的地址范围不是一段有效的 RAM 空间。
6. 恢复这两个字的原始内容。测试完毕。 为了得到一段干净的 RAM 空间范围,我们也可以将所安排的 RAM 空间范围进行清零操作。
@ set GPIO for UART mov r1, #GPIO_CTL_BASE add r1, r1, #oGPIO_H ldr r2, gpio_con_uart str r2, [r1, #oGPIO_CON] ldr r2, gpio_up_uart str r2, [r1, #oGPIO_UP]
;设置串口
;设置GPIO_H组管脚为串口
8
bl InitUART
;跳转到InitUART串口初始化函数
1.1.3 拷贝 stage2 到 RAM 中 拷贝时要确定两点:
(1) stage2 的可执行映象在固态存储设备的存放起始地址和终止地址; (2) RAM 空间的起始地址。 1:
1.1.4 设置堆栈指针 sp
堆栈指针的设置是为了执行 C 语言代码作好准备。通常我们可以把 sp 的值设置为(stage2_end-4),也即在 1.1.2 节所安排的那个 1MB 的 RAM 空间的最顶端(堆栈向下生长)。
此外,在设置堆栈指针 sp 之前,也可以关闭 led 灯,以提示用户我们准备跳转到 stage2。 经过上述这些执行步骤后,系统的物理内存布局应该如下图2所示。 @ get read to call C functions ldr sp, DW_STACK_START @ setup stack pointer mov fp, #0 @ no previous frame, so fp=0
1.1.5 跳转到 stage2 的 C 入口点
在上述一切都就绪后,就可以跳转到 Boot Loader 的 stage2 去执行了。比如,在 ARM 系统中,这可以通过修改 PC 寄存器为合适的地址来实现。
head.S 负责完成硬件初始化操作,具体分析见源码注释 ,汇编差不多忘光了,下面注释中有关汇编的东西多些。 @ get read to call C functions ldr sp, DW_STACK_START @ setup stack pointer mov fp, #0 @ no previous frame, so fp=0 mov a2, #0 @ set argv to NULL bl main @ call main
9
@ jump to ram
ldr r1, =on_the_ram add pc, r1, #0 nop nop b 1b @ infinite loop
mov pc, #FLASH_BASE @ otherwise, reboot
10