一、S5PV210的SD卡启动实战1
1、任务:大于16KB的bin文件使用 SD 卡启动
(1) 总体思路:将我们的代码分为 2 部分:第一部分 BL1 ≤ 16KB,第二部分为任意大小。
iROM 代码执行完成后,从 SD 卡启动会自动读取 BL1 到 SRAM 中执行;BL1 执行时负责初始化 DDR,然后手动将 BL2 从 SD 卡 copy 到 DDR 中正确位置,然后 BL1 远跳转到 BL2 中执行 BL2.
(2) 细节1:程序怎么安排?程序整个分为 2 个文件夹 BL1 和 BL2,各自管理各自的项目。
(3) 细节2:BL1 中要完成:关看门狗、设置栈、开iCache、初始化 DDR、从 SD 卡复制 BL2 到 DDR 中特定位置,跳转执行 BL2.
(4) 细节3:BL1 在 SD 卡中必须从 Block1 开始(Block0 不能用,这个是三星官方规定的),长度为 16KB 内,我们就定为 16KB(也就是 32 个block);BL1 理论上可以从 33 扇区开始,但是实际上为了安全,都会留一些空扇区作为隔离,譬如可以从 45 扇区开始,长度由自己定(实际根据自己的 BL2 大小来分配长度,我们实验时 BL1 非常小,因此我们定义 BL1 长度为 16KB,也就是 32 扇区)。
(5) 细节4:DDR 初始化好之后,整个 DDR 都可以使用了,这时在其中选择一段长度足够 BL2 的 DDR 空间即可。我们选 0x23E00000(因为我们 BL1 中只初始化了 DDR1,地址空间范围是0x20000000~0x2FFFFFFF)。
2、代码划分为 2 部分(BL1 和 BL2)
文件名:write2sd
$ cat write2sd
#!/bin/sh
# sudo dd iflag=dsync oflag=dsync if=210.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45
文件名:Makefile
$ cat Makefile
all:
make -C ./BL1
make -C ./BL2
clean:
make clean -C ./BL1
make clean -C ./BL2
3、BL1 中的重定位
文件名: start.S
$ cat start.S
/*
* 文件名: start.S
* 描述: 演示重定位(在SRAM内部重定位)
*/
#define WTCON 0xE2700000
#define SVC_STACK 0xd0037d80
.global _start // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:
// 第1步:关看门狗(向WTCON的bit5写入0即可)
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
// 第2步:设置SVC栈
ldr sp, =SVC_STACK
// 第3步:开/关icache
mrc p15,0,r0,c1,c0,0; // 读出cp15的c1到r0中
//bic r0, r0, #(1<<12) // bit12 置0 关icache
orr r0, r0, #(1<<12) // bit12 置1 开icache
mcr p15,0,r0,c1,c0,0;
// 第4步:初始化ddr
bl sdram_asm_init
// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的 0x23E0,0000
bl copy_bl2_2_ddr
// 汇编最后的这个死循环不能丢
b .
文件名:BL1/link.lds
$ cat link.lds
SECTIONS
{
. = 0xd0020010;
.text : {
start.o
sdram_init.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
文件名:BL2/link.lds
$ cat ../BL2/link.lds
SECTIONS
{
. = 0x23E00000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
4、BL2 远跳转
(1) 因为我们 BL1 和 BL2 其实是 2 个独立的程序,链接时也是独立分开链接的,所以不能像以前一样使用 ldr pc, =main 这种方式来通过链接地址实现远跳转到 BL2.
(2) 我们的解决方案是,使用地址进行强制跳转。因为我们知道 BL2 在内存地址 0x23E00000 处,所以直接去执行这个地址即可。
文件名:sd_relocate.c
#define SD_START_BLOCK (45)
#define SD_BLOCK_CNT (32)
#define DDR_START_ADDR (0x23E00000)
typedef unsigned int bool;
//通道号: 0, 或者 2
//开始扇区号: 45
//读取扇区个数: 32
//读取后放入内存地址: 0x23E00000
//with_init:0
typedef bool (*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);
//#define CopySDMMCtoMem(z,a,b,c,e)(((bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))
typedef void (*pBL2Type)(void);
//从SD卡第45扇区开始, 复制 32 个扇区内容到 DDR 的 0x23E00000, 然后跳转到 0x23E00000 去执行.
void copy_bl2_2_ddr(void)
{
//第一步,读取 SD 卡扇区到 DDR 中
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int*)0xD0037F98);
p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int*)DDR_START_ADDR, 0); //读取 SD 卡到 DDR 中
//第二步,跳转到 DDR 中的 BL2 去执行
pBL2Type p2 = (pBL2Type)DDR_START_ADDR;
p2();
}
二、S5PV210的SD卡启动实战2
1、烧录启动实验
- 确定将 U 盘连接到虚拟机。
- 在 VM Ware 识别到读卡器U盘。
- 确定 读卡器U盘的盘符。
sda 是 VM Ware 中的硬盘,sda1~sda5 代表硬盘的 5 个分区。
sdb 就是我们新插入的读卡器 U 盘,只有一个分区 sdb1。
- 执行 make 编译整个 BL1/BL2 之后,确定我们程序中的脚本写的是 sdb ,与 VM Ware 识别到的读卡器 U 盘名称相同。
- 执行 write2sd 脚本,烧写 U 盘。
- 破坏 iNand 中的 bootloader 以从 SD2 启动。
进入 android 系统控制台,执行如下指令:
busybox dd if=/dev/zero of=/dev/block/mmcblk0 bs=512 seek=1 count=1 conv=sync
sync
解释:这句话的意思就是说把板载的 iNand 的第一个扇区用全 0 来填充,其实就是擦除它,这样我们板载的 iNand 的 bootloader 的开始第 1 个扇区就被破坏了。将来启动时 iROM 还是会先从 iNand 中读取前 16KB,然后计算校验和。这时候因为有 1 个扇区被擦掉了,所以校验和不通过,所以启动失败(会从 SD2 去执行 2nd 启动)。
- 最后,将 U 盘插入 SD 通道 2,即可看到 BL2 中的代码 LED 灯闪烁的现象。
2、代码分为 2 部分启动(上一节讲的)的缺陷
(1) 代码分为 2 部分,这种技术叫分散加载。这种分散加载的方法可以解决问题,但是比较麻烦。
(2) 分散加载的缺陷:第一,代码完全分 2 部分,完全独立,代码编写和组织上麻烦;第二,无法让工程项目兼容 SD 卡启动和 Nand 启动、NorFlash 启动等各种启动方式。
3、uboot 中的做法
(1) 第二种思路:程序代码仍然包括 BL1 和 BL2 两部分,但是组织形式上不分为 2 部分,而是作为一个整体来组织。它的实现方式是:iROM 启动,然后从 SD 卡的扇区 1 开始读取 16KB 的 BL1,然后去执行 BL1,BL1 负责初始化 DDR,然后从 SD 卡中读取整个程序(BL1 + BL2)到 DDR 中,然后从 DDR 中执行(利用 ldr pc, =main 这种方式以远跳转从 SRAM 中运行的 BL1 跳转到 DDR 中运行的 BL2)。
4、再来分析 uboot 的 SD 卡启动细节
(1) uboot 编译好之后有 200 多KB,超出了 16KB。uboot 的组织方式就是前面 16KB 为 BL1,剩下的部分为 BL2.
(2) uboot 在烧录到 SD 卡的时候,先截取 uboot.bin 的前 16KB(实际脚本截取的是 8KB)烧录到 SD 卡的 block1~bolck32;然后将整个 uboot 烧录到 SD 卡的某个扇区中(譬如49扇区)。
(3) 实际 uboot 从 SD 卡启动时是这样的:iROM 先执行,根据 OMpin 判断出启动设备是 SD 卡,然后从 SD 卡的 block1 开始读取 16KB(8KB)到 SRAM 中执行 BL1,BL1 执行时负责初始化 DDR,并且从 SD 卡的 49 扇区开始复制整个 uboot 到 DDR 中指定位置(0x23E00000)去备用;然后 BL1 继续执行直到 ldr pc, =main 时,BL1 跳转到 DDR 上的 BL2 中接着执行 uboot 的第二阶段。
总结:uboot 中的这种启动方式比上节讲的分散加载的好处在于:能够兼容各种启动方式。
源自朱有鹏老师.