.globl _TEXT_BASE //声明连接入口
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
.globl reset //上电后跳转到reset
reset:
bl save_boot_params
//BL 指令的格式为:
//BL{条件} 目标地址
//BL 是另一个跳转指令,但跳转之前,会在寄存器R14 中保存PC 的当前内容,因此,可以通过将R14 的内容重新加载到PC 中,来返回到跳转指令之后的那个指令处执行。该指令是实现子程序调用的一个基本但常用的手段
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr //将当前状态寄存器的值读到r0
and r1, r0, #0x1f @ mask mode bits //将r0的低五位状态赋值r1,也就是cpsr的低五位状态
teq r1, #0x1a @ test for HYP mode //比较CPSR的低五位状态是否等于0x1a,该状态说明对应HYP模式,一种非安全状态行运行的新模式。
bicne r0, r0, #0x1f @ clear all mode bits //如果不等于那就清除低五位
orrne r0, r0, #0x13 @ set SVC mode //同样设置低五位为0x13,也就是10011,对应的是ARM的SVC管理模式
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0 //将通用寄存器r0的值保存到状态寄存器cpsr
ARM v6引入了security extension,把硬件资源划分成了两个部分secure world和normal world。当CPU运行在secure world的时候,它可以访问所以的硬件资源,但当CPU运行在normal world的时候,它只能访问normal world的资源。ARM v7引入了virtualization extension,在normal world里面加入了一个新的CPUmode:HYP mode。这样CPU 在normal world运行的时候就有三种模式:USR mode, SVC mode和HYP mode,分别对应PL0, PL1, PL2(PL:privilege level),number越大,权限越高。我对这里所说的权限的理解是:寄存器的访问和指令的执行。比如说一些特殊的寄存器(HVBAR)只能再HYP mode里面才能访问,一些特殊的指令(HVC)只能再SVC 或者 HYP mode执行。而CPU处于哪个模式是由CPSR这个寄存器决定的
————————————————
状态寄存器传送至通用寄存器类指令 功能:将状态寄存器的内容传送至通用寄存器。 格式: MRS{<条件码>}Rd,CPSR}SPSR
通用寄存器传送至状态寄存器传送指令 功能:将通用寄存器的内容传送至状态寄存器。 格式:
MSR{<条件码>CPSR_f|SPSR_f,<#ommed_8r>
MSR{<条件码>CPSR_|SPSR_,Rm
汇编弹簧床机制:先跳转到save_boot_params,bl跳转后会返回回来继续执行
ENTRY(save_boot_params)
bx lr @ back to my caller
ENDPROC(save_boot_params)
.weak save_boot_params
这里bx lr就直接返回跳转来之前的地址,也就是什么都不做,下面的.weak关键字作用是如果其他地方定义了save_boot_params那就调用,如果没有定义,这就是个空函数。
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif
bl _main
这里我们没有定义CONFIG_OMAP44XX和CONFIG_SPL_BUILD,因此执行
mrc p15, 0, r0, c1, c0, 0,这是协处理器操作,只有mrc和mcr才能对arm的协处理器进行操作:
MRC {条件}协处理器编码,协处理器操作码1,目的寄存器,源寄存器1,源寄存器2,{协处理器操作码2} MCR {条件}协处理器编码,协处理器操作码1,源寄存器,目的寄存器1,目的寄存器2,{协处理器操作码2}
这两个指令一般是成对使用,读出来在写进去,设置CP15协处理器的C1寄存器V位为0,查看寄存器手册:
设置地段一场中断向量0x0~0x1c。
然后将_start的地址给r0,再将该地址写到c12寄存器,也就是设置异常向量的基地址:
紧接着,这里没有定义skip_lowlevel_init,跳入cpu_init_cp15 ,顾名思义还是对cp15协处理器的设置。
————————————————
MOV(双操作数指令) 格式:MOV 目的操作数,源操作数
其中:MOV为操作码:目的操作数,可以是寄存器、存储器、累加器:源操作数,可|以是寄存器、存储器、累加器和立即数。
功能:将一个源操作数(字或字节)送到目的操作数中。
BIC―――――位清除指令 指令格式: BIC{cond}{S} Rd,Rn,operand2 BIC指令将Rn
的值与操作数operand2 的反码按位逻辑”与”,结果存放到目的寄存器Rd 中。指令示例:BIC R0,R0,#0x0F
;将R0最低4位清零,其余位不变
ORR 指令的格式为: ORR{条件}{S} 目的寄存器,操作数 1,操作数 2 ORR
指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数 1 应是一个寄存器,操作数 2
可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数 1 的某些位。 指令示例: ORR R0,R0,#3
; 该指令设置R0的0、1位,其余位保持不变。
ENTRY(cpu_init_cp15)
/*
* Invalidate L1 I/D
* 使无效整个数据和指令TLB,然后使无效整个指令cache,清空整个跳转目标的cache,清空预取缓冲区,清空写缓冲区
*/
mov r0, #0 @ set up for MCR //首先对r0清零
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs //使无效整个数据和指令TLB
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache //使无效整个指令cache
mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array //
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
/*
* disable MMU stuff and caches
* 设置低端异常中断向量,禁止MMU,禁止地址对齐检查,禁止数据Cache,前面已经禁止了指令cache。紧接着使能地址对齐检查,使能跳转预测功能
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache
#else
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache //这里没有定义ICACHE_OFF,因此这里使能指令Cache
#endif
mcr p15, 0, r0, c1, c0, 0
#ifdef CONFIG_ARM_A53
/*
*clear AFE,TRE bit in sctrl
*non-secure: reset value is unknow
*secure : default value is 0
*notice : we must set the TRE bit to enable the memory
* arttribute configuration from the section table.
*/
MRC p15, 0, r0, c1, c0, 0 @Read SCTLR
BIC r0, r0, #(1<<28) @clr TRE bit
BIC r0, r0, #(1<<29) @clr AEF bit
MCR p15, 0, r0, c1, c0, 0 @Write SCTLR
#endif
#ifdef CONFIG_ARM_ERRATA_716044 //没有定义该号码的宏
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif
/*
* 这里是对CP15的C15寄存器进行了操作,这里叫做诊断寄存器,然后将4,6,15都置位
*/
#if (defined(CONFIG_ARM_ERRATA_742230) || defined(CONFIG_ARM_ERRATA_794072))
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_761320
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 << 21 @ set bit #21
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
mov pc, lr @ back to my caller
ENDPROC(cpu_init_cp15)
完了之后,跳回子函数,然后顺序执行到函数cpu_init_crit:
ENTRY(cpu_init_crit)
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
b lowlevel_init @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
跳转到lowlevel_init去了,lowlevel_init的作用就是引导加载c函数做进一步的初始化
/home/milo/miloworkplace/Toyota/yb_rls/brandy/u-boot-2014.07/arch/arm/cpu/armv7/low_level_init.S
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。
比如想把数据从内存中某处读取到寄存器中,只能使用ldr 比如: ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0中。
ENTRY(lowlevel_init)
/*
* Setup a temporary stack
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance设置SP八个字节对齐 */
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata //定义了CONFIG_SPL_BUILD。将gdata赋值给r9
#else
sub sp, sp, #GD_SIZE
bic sp, sp, #7
mov r9, sp
#endif
/*
* Save the old lr(passed in ip) and the current lr to stack
*/
push {ip, lr}
/*
* go setup pll, mux, memory
*/
bl s_init 跳转到s_init函数
pop {ip, pc}
ENDPROC(lowlevel_init)
跳转到s_init函数
s_init在/home/milo/miloworkplace/Toyota/yb_rls/brandy/u-boot-2014.07/arch/arm/cpu/armv7/sunxi/的board.c中,s_init主要是对sun8iw11p1的PFDs进行了板级设置。
void s_init(void)
{
#if !defined CONFIG_SPL_BUILD && (defined CONFIG_SUN7I || defined CONFIG_SUN6I)
/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */
asm volatile(
"mrc p15, 0, r0, c1, c0, 1\n"
"orr r0, r0, #1 << 6\n"
"mcr p15, 0, r0, c1, c0, 1\n");
#endif
clock_init();
timer_init();
gpio_init();
#ifdef CONFIG_SPL_BUILD
gd = &gdata;
preloader_console_init();
sunxi_board_init();
#endif
}
调用结束s_init之后,程序跳转到到_main函数arch/arm/lib/crt0.S
重新对SP赋值, 确认sp是8字对齐
在栈顶保留一个global_data的大小, 这个global_data是uboot里面的一个全局数据, 很多地方都会用到. 俗称 gd_t
确认更新后的sp是8字对齐
r9指向global_data, 后面别的地方想用global_data时候, 可以直接从r9里面获取地址.
r0赋值0
bl board_init_f: 跳转到board_init_f. 在编译SPL时, 分析Makefile可以看出, 该函数的实现是在<arch/arm/lib/spl.c>.
————————————————
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, sp, #GD_SIZE /* allocate one GD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r9, sp /* GD is above SP */
mov r0, #0
bl board_init_f
#if ! defined(CONFIG_SPL_BUILD)
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
here:
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
bl coloured_LED_init
bl red_led_on
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r /* this is auto-relocated! */
/* we should not return here. */
#endif
ENDPROC(_main)
/home/milo/miloworkplace/Toyota/yb_rls/brandy/u-boot-2014.07/arch/arm/lib/spl.c
void __weak board_init_f(ulong dummy)
{
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
/* Set global data pointer. */
gd = &gdata;
board_init_r(NULL, 0);
}
对BSS段进行清零操作
gd = &gdata;
gd的定义在DECLARE_GLOBAL_DATA_PTR <arch/arm/include/asm/global_data.h>
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r9”)
r9之前初始化了
gdata的定义在本文件中: gd_t gdata attribute ((section(".data")));
它是一个 gd_t 也就是global_data类型的变量
__attribute__表示这个变量会被放到".data"这个输入段中. 连接器会把输入段按照链接脚本(u-boot-spl.lds)里面指定的规则存放到输出段
————————————————
跳转到board_init_r,在common/spl/spl.c
对memory,timer初始化,选择在什么介质启动,最后判断image的类型,是u-boot还是linux
void board_init_r(gd_t *dummy1, ulong dummy2)
{
u32 boot_device;
debug(">>spl:board_init_r()\n");
#ifdef CONFIG_SYS_SPL_MALLOC_START
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
#endif
#ifndef CONFIG_PPC
/*
* timer_init() does not exist on PPC systems. The timer is initialized
* and enabled (decrementer) in interrupt_init() here.
*/
timer_init();
#endif
#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif
boot_device = spl_boot_device();
debug("boot device - %d\n", boot_device);
switch (boot_device) {
#ifdef CONFIG_SPL_RAM_DEVICE
case BOOT_DEVICE_RAM:
spl_ram_load_image();
break;
#endif
#ifdef CONFIG_SPL_MMC_SUPPORT
case BOOT_DEVICE_MMC1:
case BOOT_DEVICE_MMC2:
case BOOT_DEVICE_MMC2_2:
spl_mmc_load_image();
break;
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
case BOOT_DEVICE_NAND:
spl_nand_load_image();
break;
#endif
#ifdef CONFIG_SPL_ONENAND_SUPPORT
case BOOT_DEVICE_ONENAND:
spl_onenand_load_image();
break;
#endif
#ifdef CONFIG_SPL_NOR_SUPPORT
case BOOT_DEVICE_NOR:
spl_nor_load_image();
break;
#endif
#ifdef CONFIG_SPL_YMODEM_SUPPORT
case BOOT_DEVICE_UART:
spl_ymodem_load_image();
break;
#endif
#ifdef CONFIG_SPL_SPI_SUPPORT
case BOOT_DEVICE_SPI:
spl_spi_load_image();
break;
#endif
#ifdef CONFIG_SPL_ETH_SUPPORT
case BOOT_DEVICE_CPGMAC:
#ifdef CONFIG_SPL_ETH_DEVICE
spl_net_load_image(CONFIG_SPL_ETH_DEVICE);
#else
spl_net_load_image(NULL);
#endif
break;
#endif
#ifdef CONFIG_SPL_USBETH_SUPPORT
case BOOT_DEVICE_USBETH:
spl_net_load_image("usb_ether");
break;
#endif
#ifdef CONFIG_SPL_USB_SUPPORT
case BOOT_DEVICE_USB:
spl_usb_load_image();
break;
#endif
#ifdef CONFIG_SPL_SATA_SUPPORT
case BOOT_DEVICE_SATA:
spl_sata_load_image();
break;
#endif
default:
debug("SPL: Un-supported Boot Device\n");
hang();
}
switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("Jumping to U-Boot\n");
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
jump_to_image_no_args(&spl_image);
}