2.5、uboot源码分析1-启动第一阶段(2021-4-23)


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 37  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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值