2.5.1、start.S引入
2.5.1.1、u-boot.lds中找到start.S入口
2.5.1.2、SourceInsight中如何找到文件
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
2.5.1.3、SI中找文件技巧
2.5.2、start.S解析1
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU) //这个宏在include/configs/x210_sd.h中定义。如果有则定义了,如果没有,则没有定义
#include <asm/proc/domain.h>
#endif
#include <regs.h>
#ifndef CONFIG_ENABLE_MMU //这个宏在include/configs/x210_sd.h中定义。如果有则定义了,如果没有,则没有定义
#ifndef CFG_PHY_UBOOT_BASE //这个宏在include/configs/x210_sd.h中定义。如果有则定义了,如果没有,则没有定义
#define CFG_PHY_UBOOT_BASE CFG_UBOOT_BASE
#endif
#endif
2.5.2.1、不简单的头文件包含
#
# Create board specific header file
# 创建board指定的头文件
# Append to existing config file,追加到现有配置文件
if [ "$APPEND" = "yes" ]
then
echo >> config.h
else
> config.h # Create new config file,创建新的配置文件
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h //向里面添加内容
exit 0 //正常结束
/* Automatically generated - do not edit */
#include <configs/x210_sd.h> //头文件
root@xfj-virtual-machine:~/x210v3_bsp/uboot/include# pwd
/root/x210v3_bsp/uboot/include
root@xfj-virtual-machine:~/x210v3_bsp/uboot/include# ls -l version.h
-rw-rw-r-- 1 root root 1023 3月 7 2016 version.h
root@xfj-virtual-machine:~/x210v3_bsp/uboot/include# vim version.h
root@xfj-virtual-machine:~/x210v3_bsp/uboot/include#
//#include <version.h>中的内容
#ifndef __VERSION_H__
#define __VERSION_H__
#ifndef DO_DEPS_ONLY
#include "version_autogenerated.h" //包含了此头文件
#endif
#endif /* __VERSION_H__ */
VERSION = 1 //主版本号(环境变量) Makefile定义和使用变量,直接定义和使用,引用变量时用%var
PATCHLEVEL = 3 //次版本号(环境变量) Mkaefile不要求赋值运算符两边一定要有空格或者无空格
SUBLEVEL = 4 //再次版本号(环境变量)
EXTRAVERSION = //附加的版本信息(环境变量)
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) //最终版本号
//用=赋值的变量,在被解析时他的值取决于最后一次赋值时的值,所以你看变量引用时的值不能只往前面看,还要往后面看
VERSION_FILE = $(obj)include/version_autogenerated.h //=往后面找
x210_sd_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
#
# Create link to architecture specific headers
# 创建到架构特定标题的链接
if [ "$SRCTREE" != "$OBJTREE" ] ; then //如果不是本地编译的话
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else //如果是本地编译的话
cd ./include //进入当前目录的include目录
rm -f asm //将asm文件删掉
ln -s asm-$2 asm //为asm-$2创建符号链接,名字叫asm
fi
2.5.3、start.S解析2
2.5.3.1、启动代码的16字节头部
#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) //判断输入参数是否正确,输入参数为4
{
printf("Usage: mkbl1 <source file> <destination file> <size> \n"); //命令 源文件 目标文件 大小
return -1;
}
//////////////////////////////////////////////////////////////
BufLen = atoi(argv[3]); //将argv[3]转成整形; atoi是把字符串转换成整型数的一个函数
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;
}
//fileLen:源文件大小
fseek(fp, 0L, SEEK_END); //把文件指针移向末尾
fileLen = ftell(fp); //函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。
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); //从fp中读取BufLen个元素,每个元素1个字节,读入Buf中
if ( nbytes != BufLen ) //如果从fp中读出的数据不等于BufLen的长度
{
printf("source file read error\n");
free(Buf);
fclose(fp);
return -1;
}
fclose(fp);
//////////////////////////////////////////////////////////////
a = Buf + 16; //此时Buf中就是源文件的内容
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;
}
2.5.3.2、异常向量表的构建
异常向量表(CPU发明出来用来异常处理的)
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
* 跳转向量表如表3.1中的[1]
*
*************************************************************************
*/
//CONFIG_EVT1定义了,CONFIG_FUSED没有定义
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED) //如果定义了CONFIG_EVT1这个宏而且没有定义CONFIG_FUSED这个宏
.word 0x2000 //.word:数据类型,用来定义变量;为gnu伪指令
.word 0x0 //.word相当于int类型。此处一共16个字节
.word 0x0 //保留16字节头部
.word 0x0
#endif
//异常向量表
.globl _start //给_start外部链接属性(可以在别的文件访问)
//b:跳转指令,直接跳转,没打算返回
//ldr:将内存内容加载到寄存器中;str:将寄存器内容加载到内存中
//pc(r15):程序控制寄存器,其为程序指针,PC指向哪里,CPU就会执行那条指令.所以程序跳转时把目标地址放在PC中
_start: b reset //复位异常
ldr pc, _undefined_instruction //未定义异常
ldr pc, _software_interrupt //软中断异常
ldr pc, _prefetch_abort //预取址异常
ldr pc, _data_abort //数据异常
ldr pc, _not_used //未使用异常
ldr pc, _irq //普通中断异常
ldr pc, _fiq //快速中断异常
//:以冒号结尾的是标号,标号这一行是一行符号,不是指令,标号的含义是标记这一行指令的地址。
_undefined_instruction: //未定义异常
.word undefined_instruction //.word:数据类型,用来定义变量;为gnu伪指令
_software_interrupt: //软中断异常
.word software_interrupt
_prefetch_abort: //预取址异常
.word prefetch_abort
_data_abort: //数据异常
.word data_abort
_not_used:
.word not_used
_irq:
.word irq
_fiq:
.word fiq
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect //给_end_vect外部链接属性(可以在别的文件访问)
_end_vect:
.balignl 16,0xdeadbeef //让内存对齐,16为单位
2.5.3.3、有点意思的deadbeef
2.5.3.4、TEXT_BASE等
//此处代码在uboot/makefile中
//当我们make x210_sd_config时创建config.mk文件,然后向里面添加内容TEXT_BASE = 0xc3e00000
x210_sd_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
/*
*************************************************************************
* Startup Code (reset vector)启动代码(重置向量)
*
* do important init only if we don't start from memory!
* 只有当我们不是从内存开始的时候,才做重要的事情!
* setup Memory and board specific bits prior to relocation.
* 在重新定位之前设置内存和板特定位。
* relocate armboot to ram 将armboot重新定位到ram
* setup stack 设置堆栈
*
*************************************************************************
*/
//:以冒号结尾的是标号,标号这一行是一行符号,不是指令,标号的含义是标记这一行指令的地址。
_TEXT_BASE://标号
.word
2.5.4、start.S解析3
(1)CFG_PHY_UBOOT_BASE 33e00000 uboot在DDR中的物理地址
//在start.S中
/*
* Below variable is very important because we use MMU in U-Boot.
* Without it, we cannot run code correctly before MMU is ON.
* by scsuh.
* 下面的变量非常重要,因为我们在U-Boot中使用了MMU。
* 没有它,我们无法在MMU打开之前正确运行代码。scsuh写的。
*/
//:以冒号结尾的是标号,标号这一行是一行符号,不是指令,标号的含义是标记这一行指令的地址?
_TEXT_PHY_BASE:
.word CFG_PHY_UBOOT_BASE
//在X210_nand.h中
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x3e00000
//在X210_sd.h中
#define MEMORY_BASE_ADDRESS 0x30000000 //内存起始地址
2.5.4.1、设置CPU为SVC模式
0xd3 = 11010011
/*
* the actual reset code 真实的复位代码
*/
//:以冒号结尾的是标号,标号这一行是一行符号,不是指令,标号的含义是标记这一行指令的地址?
reset:
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
* 将cpu设置为SVC32模式,并禁用IRQ & FIQ
*/
@;mrs r0,cpsr //读cpsr的内容到r0寄存器
@;bic r0,r0,#0x1f //将r0的bit0-bit4位清0后赋值给r0
@;orr r0,r0,#0xd3 //0xd3=1101 0011
@;msr cpsr,r0 //将r0的值写入cpsr寄存器中
//写程序状态寄存器,修改bit0-bit7,将cpu设置为SVC模式,处于ARM状态,禁止FIQ,IRQ
msr cpsr_c, #0xd3 @ I & F disable, Mode: 0x13 - SVC
2.5.4.2、设置L2、L1cache和MMU
bl disable_l2cache //禁止l2cache
bl set_l2cache_auxctrl_cycle //l2cache相关初始化
bl enable_l2cache
2.5.4.3、识别并暂存启动介质选择
/* NAND BOOT */
cmp r2, #0x0 @ 512B 4-cycle //
moveq r3, #BOOT_NAND
cmp r2, #0x2 @ 2KB 5-cycle //
moveq r3, #BOOT_NAND
cmp r2, #0x4 @ 4KB 5-cycle 8-bit ECC
moveq r3, #BOOT_NAND
//若r2寄存器的值为 #0x6时,从BOOT_NAND启动
cmp r2, #0x6 @ 4KB 5-cycle 16-bit ECC#0x8
moveq r3, #BOOT_NAND
cmp r2, #0x8 @ OneNAND Mux //若r2寄存器的值为 #0x8时,从OneNAND Mux启动
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0xc //若r2寄存器的值为 #0xc时,从SD/MMC BOOT启动
moveq r3, #BOOT_MMCSD
/* NOR BOOT */
cmp r2, #0x14
moveq r3, #BOOT_NOR
2.5.4.4、设置栈(SRAM中的栈)并调用lowlevel_init
/*
* Go setup Memory and board specific bits prior to relocation.
* 在重新定位之前,请设置内存和主板特定位。
*/
//设置栈,sp(r13):sp指针,堆栈指针,工作区。
ldr sp, =0xd0036000 /* end of sram dedicated to u-boot专用于u-boot的sram结束 */
sub sp, sp, #12 /* set stack 设置栈*/
bl lowlevel_init /* go setup pll,mux,memory */ //调用函数就要使用栈,底层初始化
2.5.10、start.S解析8
2.5.10.1、什么是虚拟地址、物理地址
2.5.10.2、MMU单元的作用
2.5.10.3、地址映射的额外收益1:访问控制
2.5.10.4、地址映射的额外收益2:cache
2.5.11、start.S解析9
MRC & MCR的使用方法
2.5.11.1、使能域访问(cp15的c3寄存器)
/* enable domain access 使能域访问*/
ldr r5, =0x0000ffff //将内存内容下载到r5寄存器中
//将寄存器r5中的内容写到cp15的c3寄存器中
mcr p15, 0, r5, c3, c0, 0 @load domain access register
2.5.11.2、设置TTB(cp15的c2寄存器)
/* Set the TTB register 设置TTB寄存器 */
//进行高位内存运算,运行到DDR中
//得到转换表的基地址,写到cp15寄存器的c2寄存器中
ldr r0, _mmu_table_base //_mmu_table_base 转换表基地址。将转换表基地址加载到r0寄存器中
//CFG_PHY_UBOOT_BASE uboot的物理基地址。将uboot的物理基地址加载到r1寄存器中
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000 //将0xfff00000加载到r2寄存器中
bic r0, r0, r2 //将r0寄存器中的内容,即转换表基地址进行高位运算
orr r1, r0, r1 //将r0寄存器中的值进行低位运算赋值给r1
mcr p15, 0, r1, c2, c0, 0 //将寄存器r1中的内容写到cp15的c2寄存器中
2.5.11.3、使能MMU单元(cp15的c1寄存器)
/* Enable the MMU 使能MMU*/
mmu_on: //将c1寄存器的bit0设置为1,使能MMU
mrc p15, 0, r0, c1, c0, 0 //读取c1寄存器的内容加载到r0寄存器中
orr r0, r0, #1 //将r0寄存器中的bit0位置1
mcr p15, 0, r0, c1, c0, 0 //将寄存器r0中的内容写到cp15的c1寄存器中
2.5.11.4、找到映射表待分析
2.5.11.5、S5PV210的2种虚拟地址管理
2.5.12、start.S解析10
DRAM有效范围:
DMC0: 0x30000000-0x3FFFFFFF
DMC1: 0x40000000-0x4FFFFFFF
结论:
虚拟地址映射只是把虚拟地址的c0000000开头的256MB映射到了DMC0的30000000开头的256MB物理内存上去了。其他的虚拟地址空间根本没动,还是原样映射的。
思考:
为什么配置时将链接地址设置为c3e00000,因为这个地址将来会被映射到33e00000这个物理地址。
2.5.12.1、宏FL_SECTION_ENTRY
/* form a first-level section entry */
//.macro汇编中用来定义宏,.endm用来终止宏,FL_SECTION_ENTRY为宏名,这个宏接收五个参数
.macro FL_SECTION_ENTRY base,ap,d,c,b
.word (\base << 20) | (\ap << 10) | \ //.word数据结构
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
.endm
// the following alignment creates the mmu table at address 0x4000.
//以下对齐方式在地址0x4000处创建mmu表。
.globl mmu_table
mmu_table: //转换表
.set __base,0 //.set __base就是一个数字
// Access for iRAM
.rept 0x100 //.rept伪指令,和.endr连用,相当于for循环,中间为循环体,循环的次数为0x100
FL_SECTION_ENTRY __base,3,0,0,0 //FL_SECTION_ENTRY为一个宏,此宏就是建立一个表项
.set __base,__base+1 // __base就是一个数字,加1
.endr //256个映射,256MB
// Not Allowed
.rept 0x300 - 0x100
.word 0x00000000
.endr
.set __base,0x300
// should be accessed
.rept 0x400 - 0x300
//.rept 0x350 - 0x300
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
// Not Allowed
//.rept 0x400 - 0x350
//.word 0x00000000
//.endr
// DRAM - DMC1 area - used for STL_write : djpark (20090729)
.set __base,0x400
// should be accessed
.rept 0x500 - 0x400
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
.rept 0x800 - 0x500
.word 0x00000000
.endr
.set __base,0x800
// should be accessed
.rept 0xb00 - 0x800
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
.set __base,0xB00
.rept 0xc00 - 0xb00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
.set __base,0x300
// 80MB for SDRAM with cacheable
.rept 0xd00 - 0xC00
//.rept 0xC50 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
// Not Allowed
@.rept 0xD00 - 0xC80
@.word 0x00000000
@.endr
// Not Allowed
//.rept 0xD00 - 0xC50
//.word 0x00000000
.set __base,0xD00
// 1:1 mapping for debugging with non-cacheable
.rept 0x1000 - 0xD00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
#endif
#endif