1、从SD卡启动的逻辑分析

(1)S5PV210芯片首先会去SD卡通道0启动,启动失败才会去从SD卡通道二启动;
(2)S5PV210芯片先从SD卡中读8kb的BL1到iRAM中执行;
(3)BL1程序会把完成DDR的初始化,并把整个uboot重定位到DDR,接着执行后面的uboot代码;
S5PV210芯片的启动流程参考博客:《S5PV210的启动过程详解》

2、制作启动SD卡

2.1、文件分析

sd_fusing
	├── C110-EVT1-mkbl1.c
	├── c110.signedBL1_bin
	├── Makefile
	├── mkbl1
	├── sd_fdisk
	├── sd_fdisk.c
	├── sd_fusing2.sh
	└── sd_fusing.sh

(1)sd_fusing.sh:在linux环境中制作启动SD卡的烧录脚本;
(2)sd_fdisk.c:编译得到sd_fdisk可执行程序,生成SD卡的MBR(主引导记录区);
(3)C110-EVT1-mkbl1.c:编译得到mkbl1,功能是截取uboot.bin的前8kb,制作成BL1;
(4)Makefile:编译脚本;
(5)sd_fusing2.sh、c110.signedBL1_bin:制作S5PV210芯片的启动SD卡用不到,应该是其他芯片使用的;

2.2、编译指令

make clean
make
./sd_fusing.sh /dev/sdb
备注:确保你的SD卡在Linux系统中是/dev/sdb,如果不是则需要改脚本;uboot.bin在上一层目录,因为在脚本里用的相对地址(…/u-boot.bin);

3、烧录脚本分析(sd_fusing.sh)

#SD卡或者mmc卡的设备文件
reader_type1="/dev/sdb"
reader_type2="/dev/mmcblk0"

#判断传入的$1是否为空
if [ -z $1 ]
then
    echo "usage: ./sd_fusing.sh [/dev/sdb | /dev/mmcblk0]"
    exit 0
fi

if [ $1 = $reader_type1 ]
then 
    partition1="$11"
    partition2="$12"
    partition3="$13"
    partition4="$14"

elif [ $1 = $reader_type2 ]
then 
    partition1="$1p1"
    partition2="$1p2"
    partition3="$1p3"
    partition4="$1p4"

else
    echo "Unsupported SD reader"
    exit 0
fi

#文件存在且为块设备文件
if [ -b $1 ]
then
    echo "$1 reader is identified."
else
    echo "$1 is NOT identified."
    exit 0
fi

####################################
# make partition
echo "make sd card partition"
echo "./sd_fdisk $1" 

#构建SD卡的MBR(主引导记录区),写入到0号扇区
./sd_fdisk $1 
dd iflag=dsync oflag=dsync if=sd_mbr.dat of=$1 
rm sd_mbr.dat
 
####################################
# format
umount $partition1 2> /dev/null
umount $partition2 2> /dev/null
umount $partition3 2> /dev/null
umount $partition4 2> /dev/null

#将SD卡格式化成vfat格式
echo "mkfs.vfat -F 32 $partition1"
mkfs.vfat -F 32 $partition1

#<BL1 fusing>
bl1_position=1
uboot_position=49

#截取uboot.bin的前8kb做成BL1
echo "BL1 fusing"
./mkbl1 ../u-boot.bin SD-bl1-8k.bin 8192

#将SD-bl1-8k.bin写入到SD卡的1扇区开始的地方。(扇区是编号从0开始的)
dd iflag=dsync oflag=dsync if=SD-bl1-8k.bin of=$1 seek=$bl1_position
rm SD-bl1-8k.bin

####################################
#<u-boot fusing>
echo "u-boot fusing"

# 将整个uboot写入到SD卡49扇区开始的地方,当做BL2
dd iflag=dsync oflag=dsync if=../u-boot.bin of=$1 seek=$uboot_position

####################################
#<Message Display>
echo "U-boot image is fused successfully."
echo "Eject SD card and insert it again."

(1)判断输入的设备节点类型是SD卡的还是MMC卡的;
(2)构建SD卡/MMC卡的MBR,写入到0号扇区;
(3)将SD卡/MMC卡格式化成vfat格式;
(4)调用mkbl1制作BL1,写入到1号扇区开始的地方;
(5)将uboot.bin写入到49扇区开始的地方;

4、制作BL1的代码分析(C110-EVT1-mkbl1.c)

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
	FILE		*fp;
	char		*Buf, *a;
	int		BufLen;
	int		nbytes, fileLen;
	unsigned int	checksum;
	int		i;
//
	if (argc != 4)
	{
		printf("Usage: mkbl1 <source file> <destination file> <size> \n");
		return -1;
	}
//
	BufLen = atoi(argv[3]);
	Buf = (char *)malloc(BufLen);
	memset(Buf, 0x00, BufLen);
//
	fp = fopen(argv[1], "rb");
	if( fp == NULL)
	{
		printf("source file open error\n");
		free(Buf);
		return -1;
	}
	fseek(fp, 0L, SEEK_END);
	fileLen = ftell(fp);
	fseek(fp, 0L, SEEK_SET);
	if ( BufLen > fileLen )
	{
		printf("Usage: unsupported size\n");
		free(Buf);
		fclose(fp);
		return -1;
	}
	nbytes = fread(Buf, 1, BufLen, fp);

	if ( nbytes != BufLen )
	{
		printf("source file read error\n");
		free(Buf);
		fclose(fp);
		return -1;
	}
	fclose(fp);
	//计算校验和
	a = Buf + 16;
	for(i = 0, checksum = 0; i < BufLen - 16; i++)
		checksum += (0x000000FF) & *a++;

	//保存校验和
	a = Buf + 8;	
	*( (unsigned int *)a ) = checksum;

//
	fp = fopen(argv[2], "wb");
	if (fp == NULL)
	{
		printf("destination file open error\n");
		free(Buf);
		return -1;
	}

	a	= Buf;
	nbytes	= fwrite( a, 1, BufLen, fp);
	if ( nbytes != BufLen )
	{
		printf("destination file write error\n");
		free(Buf);
		fclose(fp);
		return -1;
	}
	free(Buf);
	fclose(fp);
	return 0;
}

(1)从uboot中读出前8KB数据;
(2)计算校验和;(不包含前16字节)
(3)将校验和保存在前8kb数据的8-12字节;
(4)将添加了校验和的前8kb数据保存成文件;

5、BL1和BL2在SD中的分布

SD卡的0号扇区放的MBR(扇区引导记录),1-16扇区存放的BL1,49开始的扇区存放的完整的uboot.bin。之所以放1号扇区和49号扇区,是因为uboot源码里读BL1就是从1号扇区读,BL2就是从49号扇区读,后面会做分析。

6、S5PV210对从SD卡启动的要求

嵌入式bios烧写 bios芯片烧录_ARM

嵌入式bios烧写 bios芯片烧录_linux_02

嵌入式bios烧写 bios芯片烧录_linux_03

(1)iROM代码去SD卡读取BL1时,就是从1扇区开始读的,数据手册里明确写了第一个扇区要保留,这是三星官方的内置代码决定的;
(2)BL1代码有16字节的头信息,其中包含BL1的大小和校验和。其中校验和是必须填的,BL1的大小可以不填。

7、uboot启动过程中读取BL2

7.1、函数调用关系

start.S
		movi_bl2_copy()
			copy_bl2()

7.2、movi_bl2_copy函数分析

void movi_bl2_copy(void)
{
	ulong ch;
	
	ch = *(volatile u32 *)(0xD0037488);
	copy_sd_mmc_to_mem copy_bl2 =
	    (copy_sd_mmc_to_mem) (*(u32 *) (0xD0037F98));

	u32 ret;
	if (ch == 0xEB000000) {
		ret = copy_bl2(0, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
			CFG_PHY_UBOOT_BASE, 0);
	}
	else if (ch == 0xEB200000) {
		ret = copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
			CFG_PHY_UBOOT_BASE, 0);
	}
	else
		return;

	if (ret == 0)
		while (1)
			;
	else
		return;
}

(1)0xD0037488和0xD0037F98是特殊的地址,具体查数据手册。参考博客:《ARM芯片开发(S5PV210芯片)——SD卡启动》;
(2)读取0xD0037488地址处的数据,判断是从SD卡通道0还是从SD卡通道2读取BL2;
(3)copy_bl2函数功能:从SD卡通道0/2的MOVI_BL2_POS扇区开始处读取MOVI_BL2_BLKCNT个扇区的数据,放到CFG_PHY_UBOOT_BASE地址(内存地址)处;
(4)宏定义是在uboot的源码里定义的,MOVI_BL2_POS–49,MOVI_BL2_BLKCNT–1024,CFG_PHY_UBOOT_BASE–0x33e00000;