||
第一章 重定位代码到SDRAM-DDR3第一节 重定位
对于程序而言,我们需要理解两个概念,一是程序当前所处的地址,即程序在运行时,所处的当前地址;二是程序的链接地址,即程序运行时应该位于的运行地址。编译程序时,可以指定程序的链接地址。
对于Exynos 4412而言,启动时只会从iNAND Flash/sd等启动设备中拷贝BL1(三星提供,无源码),在由BL1拷贝BL2(14K,其中4字节为校验码)代码到IRAM(连接地址0x02023400)中,那么当我们的程序超过14K-4,怎么办?且IRAM
只有256K的大小。那么我们就需要将我们程序分为2个部分,BL2和BL3,BL2(14K)先有BL1拷贝到IRAM中,跳到BL2执行,再由BL2将BL3拷贝到DRAM中,然后再跳转到DRAM中继续运行我们的代码,这个拷贝然后跳转的过程就叫重定位。
从这章开始我们的程序结构发生了一些变化,前几个实验我们只生成一个文件,从这个实验开始我们生成两个文件,BL2.bin和BL3.bin,其中BL2.bin文件的链接地址是0x02023400 (使用的是位置无关码,程序可以在任意可用的内存中运行) ;BL3.bin 文件的链接地址是0x41000000(使用的并不是位置无关码,所有程序必须位于该地址处才能正常运行)。需要在SD卡上烧写三部分程序,分别是:
1.BL1(由三星提供):实现一些初始化
2.BL2(我们自己编写源码,用mkbl2工具生成):板级初始化,并完成代码重定位到SDRAM,完成跳转
3.BL3:实现我们想要的功能
三部分代码在SD卡的位置如下:
程序在SD卡的位置分布
从图中可以看出,BL1.bin烧写到SD卡扇区1,BL2.bin烧写到sd卡的扇区17,BL3.bin烧写到sd卡的扇区49处。
整个程序的运行过程大致如下:系统上电后,首先将sd卡扇区1处的BL1拷贝到IRAM的0x02020000地址处,然后运行该部分代码,该部分代码首先又会加载BL2.bin到IRAM的0x02023400地址处,BL2.bin会进行时钟和DRAM初始化,然后把位于sd卡中扇区49处的BL3.bin拷贝到DRAM的0x41000000地址处,最后跳转到该地址处继续运行。
第二节 程序相关讲解完整代码见目录9.link_sdram和10.link_sdram_printf,10.link_sdram_printf代码的BL3部分加入了串口打印和命令的功能,现在只简单讲解下9.link_sdram,此代码分为BL2和BL3两部分,上面已经介绍.
1. BL2/start.S
.text
.globl _start
_start:
//关看门狗
ldr r1, = 0x10060000
mov r0, #0x0
str r0, [r1]
//使能Icache,P15协寄存器的第12位
orr r0, r0, #0x00001000
mcr p15, 0, r0, c1, c0, 0
//设置栈
ldr sp, =0x02050000
bl system_clock_init
bl ddr3_mem_init
//重新设置栈到SDRAM
ldr sp, =0x43000000
bl leds_init
bl uart_init
bl copy_code_to_dram
halt:
b halt
以上先关闭看门狗,使能icache,接着初始化时钟、内存、led、串口,最后调用copy_code_to_dram函数拷贝BL3到SDRAM的0x41000000处。
2. BL2/ mmc_relocate.c
#define SDMMC_ReadBlocks(uStartBlk, uNumOfBlks, uDstAddr) \
(((void(*)(unsigned int, unsigned int, unsigned int*))(*((unsigned int *)0x02020030)))(uStartBlk, uNumOfBlks, uDstAddr))
void copy_code_to_dram (void)
{
void (*BL3) (void);
printf("Copy start ...\r\n");
SDMMC_ReadBlocks(49,32,(unsigned int *)0x41000000);
printf("Copy over !!!!\r\n");
#if 0
unsigned int *p;
int i, j;
p = (unsigned int *) 0x41000000;
for (j = 0; j < 32; j++)
{
printf("%8x: ", (unsigned int)p);
for (i = 0; i < 2; i++)
printf("%8x ", *p++);
printf("\r\n");
}
#endif
printf("Start BL3...\r\n");
/*跳转到DRAM*/
BL3 = (void *)0x41000000;
(*BL3)();
}
此函数只要是调用三星固化好在芯片里的SD卡拷贝函数SDMMC_ReadBlocks,将SD卡的BL3程序拷贝到0x41000000,然后跳到0x41000000执行BL3.
《Android_Exynos4412_iROM_Secure_Booting_Guide_Ver.1.00.00.pdf》对Exynos 4412的启动和拷贝函数都有所介绍。
3. BL3/start.S
.global _start
_start:
bl main
halt:
b halt
BL3程序的start直接跳转到main函数执行。
4. BL3/main.c
int main(void)
{
// 设置GPX0CON的bit[0:15],配置GPJ0_5/7引脚为输出功能
GPX0CON |= (0x1 << 20) | (0x1 << 28);
// 设置GPX2CON的bit[0:15],配置GPJ0_4/5引脚为输出功能
GPX2CON |= (0x1 << 16) | (0x1 << 20);
while(1)
{
// 设置GPX0DAT的bit[0:15],使GPJ0_5/7引脚输出低电平,LED亮
GPX0DAT &= ~((0x1 << 5) | (0x1 << 7));
// 设置GPX2DAT的bit[0:15],使GPJ0_4/5引脚输出低电平,LED亮
GPX2DAT &= ~((0x1 << 4) | (0x1 << 5));
delay(0x100000);
// 设置GPX0DAT的bit[0:15],使GPJ0_5/7引脚输出高电平,LED灭
GPX0DAT |= (0x1 << 5) | (0x1 << 7);
// 设置GPX2DAT的bit[0:15],使GPJ0_4/5引脚输出高电平,LED灭
GPX2DAT |= (0x1 << 4) | (0x1 << 5);
delay(0x100000);
}
return 0;
}
main函数及时BL3实现功能的代码,在此实现LED闪烁的功能。
5. sd_fusing.sh
sudo dd iflag=dsync oflag=dsync if=/work/UT4412BV03/NO_OS/tools/E4412_N.bl1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/bl2.bin of=/dev/sdb seek=17
sudo dd iflag=dsync oflag=dsync if=./BL3/bl3.bin of=/dev/sdb seek=49
此脚本程序的功能大家也可以看出,就是分别将BL1、BL2、和BL3拷贝到SD的1扇区、17扇区和49扇区。
第三节 编译程序和烧写运行将sd卡插入PC,在Ubuntu终端执行如下命令:
#cd 9.link_sdram
#make all
#./sd_fusing.sh
第四节 实验现象将sd卡插入UT4412BV03中,选择sd卡启动,然后上电。
串口打印出这三句话后就看见LED灯在闪烁。