嵌入式Linux学习:u-boot源码分析(1)--AM335X系列的2014.10版
2014.10版uboot的SPL源码分析
题外话:
经过一段时间的学习,对u-boot-2014.10有了初步的了解,趁着还记着,赶紧写下来,同时将之前还模棱两可的部分用图表的方式加强一下。
源码分析
汇编部分
之前一直看的是ARM9的u-boot,AM335X系列为代表的ARMv7系列处理器为了使得加载方式尽可能的灵活,就将原来的u-boot分解成了两个部分:SPL和u-boot,而实际上在重启后,到SPL被执行之间,还有一段片上ROM内的代码被运行。该ROM内的代码根据启动时的配置引脚的配置,自动生成一个加载优先列表,比如说首先加载MMC1,在考虑MMC0,最后再考虑uart等等。比如说用户正确的将SPL(SPL加上一个包头变成MLO)和u-boot放置到SD中后,那么ROM就会在启动:1.按照加载列表的boot优先级逐个尝试,2.判断是MMC1上拥有有效设备(比如说SD卡已经插入)后,继续判断MMC上挂的是SD卡还是MMC内存,3. 查看格式 4. 加载SPL(MLO)到片内的SRAM, 5.cpu将pc指到SRAM,SPL正式开始运行。
下面开始分析,SPL是如何工作的,首先是程序的入口,在arch/arm/cpu/armv7/start.S中的程序的入口,reset,下面开始分析,源码如下:
reset:
bl save_boot_params /*lowlevel_init.S (arch\arm\cpu\armv7\omap-common)*/
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
/*
* Setup vector:
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
* Continue to use ROM code vector only in OMAP4 spl)
*/
#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/*this branch will only work in SPL*/
bl cpu_init_cp15 /*wlg: find out in this file, we do not explain in detial*/
bl cpu_init_crit /*wlg: find out in this file, please jump*/
#endif
可以看到reset进去后,1. 首先需要执行save_boot_paras,它的入口就在lowlevel_init.S (arch\arm\cpu\armv7\omap-common)中,它的具体形式如下
ENTRY(save_boot_params)
ldr r1, =OMAP_SRAM_SCRATCH_BOOT_PARAMS
str r0, [r1]
bx lr
ENDPROC(save_boot_params)
可以看到,这里要做的工作实际上就是将r0里的数据复制到OMAP_SRAM_SCRATCH_BOOT_PARAMS这个位置,这个我会在第二篇中详细介绍。执行完了以后又跳回到start.S,继续往下看
2.下面的代码请结合相应的书册,其原始注释已经写得很清楚了,一路往下走,来到了#ifndef CONFIG_SKIP_LOWLEVEL_INIT,这个东西我们简单的介绍下,像这种大的开源工程,其包含了很多的宏开关,相当于用户实际需需要什么,定义什么,就会有相应的驱动代码与之匹配。就像此处,实际上u-boot和SPL是共享start.S的,而实际上他们执行的东西是不一样的,如何让编译器正确的理解用户实际想要包含的代码段,一方面靠的是宏做条件编译,另一方面靠的是makefile,后者就更加复杂了;在这里我们分析的就是SPL程序,所以我们会在相应的文件中定义CONFIG_SKIP_LOWLEVEL_INIT这个宏,那么这段代码实际上就被包含在了SPL程序中;继续往下看:
3. 跳到cpu_init_cp15,这段代码就在本文中,不再展开
4.执行bl cpu_init_crit ,这个程序的入口就在本文档中,以下展开讨论:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
/*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************/
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/*wlg: jump to /arch/arm/cpu/armv7/lowlevel_init.s*/
ENDPROC(cpu_init_crit)
#endif
首先还是宏打头,表示条件编译,在SPL程序中,下面的代码段实际被包含!看注释,已经大致了解它的作用,整个代码段只有一句,直接转跳到下一个程序的入口,在/arch/arm/cpu/armv7/lowlevel_init.s中定义了lowlevel_init。注意到,之前转跳都是用的bl,表示会返回,但是这里却用的是b?我个人的理解是bl没法子应对二次转跳,也就是说在lowlevel_init中还会做更复杂的转跳,甚至开始进入C函数中,那么bl我觉得是无力的。那么这里到底需不需要返回呢?
答案是需要的,但是会利用stack进行返回指针保存,利用push和pop来实现。 拿下一个问题就是,我们还没有初始化sp呢,你就敢乱用push?嘿嘿,我们继续往下看:
ENTRY(lowlevel_init)
/*
* Setup a temporary stack wlg: we set a temp stack in SRAM for building
a enveriment for C program */
ldr sp, =CONFIG_SYS_INIT_SP_ADDR /*wlg: this is a temp stack built in SRAM
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata /*wlg: we set a temp gdata(global_data) in SRAM(.data section), and */
#else /*wlg: r9 is point to that region in SRAM on chip ///SPL*/
sub sp, sp, #GD_SIZE
bic sp, sp, #7 /*wlg: cause uboot will define skip_lowlevel_init, so this branch will*/
mov r9, sp /*wlg: not active, even on stage of uboot!uboot*/
#endif
/*
* Save the old lr(passed in ip) and the current lr to stack
*/
push {ip, lr} /*wlg: save the address return*/
/*
* go setup pll, mux, memory
*/
bl s_init /*wlg: jump to arch/arm/cpu/armv7/am335x/board.c- s_init()*/
pop {ip, pc} /*wlg: to make SDRAM initialition, then make a return to _reset*/
ENDPROC(lowlevel_init)
1. 一开始就把sp指向了SRAM的高位CONFIG_SYS_INIT_SP_ADDR(因为低位拿来保存text程序了,高位用来保存数据bss,stack等等)
2.做了对齐,下面又是一个条件编译,这里我们仍然执行的是ldr r9, =gdata;这个我们要好好分析一下,去寻找gdata的定义,会发现在某个c文件中定义了一个全局变量,即global_data的结构体数据,那么在实际运行的时候,gdata是在SRAM中的。所以这里仅需知道,用户将sp和r9都指向了SRAM中某处。大家都知道sp是专门拿来做堆栈的,那么r9在这里有什么用处呢?我们简单的介绍下,这里的r9,和后面会出面的gd是同一个东西,也就是说,当用用需要修改或读取gdata里的元素的数据时,直接可以使用gd.a,gd.b即可,因为r9实际指向了gdata,而gd实际上就是r9的一个别名(宏定义);
3.再往下push {ip, lr},就是之前所说的用stack的方式保存lr(返回指针),所以我们在前面给sp赋值;同时,给sp赋值也相当于是为进入C语言函数做准备
4. bl s_init,再次作了转跳,这是一个重点的部分,函数定义在arch/arm/cpu/armv7/am335x/board.c- s_init()
C语言
void s_init(void)
{
/*
* The ROM will only have set up sufficient pinmux to allow for the
* first 4KiB NOR to be read, we must finish doing what we know of
* the NOR mux in this space in order to continue.
*/
#ifdef CONFIG_NOR_BOOT
enable_norboot_pin_mux();
#endif
/*
* Save the boot parameters passed from romcode.
* We cannot delay the saving further than this,
* to prevent overwrites.
*/
#ifdef CONFIG_SPL_BUILD
save_omap_boot_params();//wlg: should record the boot parament now, arch/arm/cpu/armv7/Omap-common/Boot-common.c
void save_omap_boot_params(void)
{
u32 rom_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS);//wlg: look in TRM P4954
u8 boot_device;//wlg:
u32 dev_desc, dev_data;
if ((rom_params < NON_SECURE_SRAM_START) ||
(rom_params > NON_SECURE_SRAM_END))
return;
/*
* rom_params can be type casted to omap_boot_parameters and
* used. But it not correct to assume that romcode structure
* encoding would be same as u-boot. So use the defined offsets.
*/
gd->arch.omap_boot_params.omap_bootdevice = boot_device =
*((u8 *)(rom_params + BOOT_DEVICE_OFFSET));//wlg: P4954, offset = 0x8,
//wlg: it point to Current Booting Device!the parament will be use to copy uboot
gd->arch.omap_boot_params.ch_flags =
*((u8 *)(rom_params + CH_FLAGS_OFFSET));
if ((boot_device >= MMC_BOOT_DEVICES_START) &&//wlg: boot device list from 0-6,XIP,MMC0,MMC1 and so on
(boot_device <= MMC_BOOT_DEVICES_END)) {
#if !defined(CONFIG_AM33XX) && !defined(CONFIG_TI81XX) && \//wlg: skip this
!defined(CONFIG_AM43XX)
if ((omap_hw_init_context() ==
OMAP_INIT_CONTEXT_UBOOT_AFTER_SPL)) {
gd->arch.omap_boot_params.omap_bootmode =
*((u8 *)(rom_params + BOOT_MODE_OFFSET));
} else
#endif
{
dev_desc = *((u32 *)(rom_params + DEV_DESC_PTR_OFFSET));//wlg: point to memory device descriptor
dev_data = *((u32 *)(dev_desc + DEV_DATA_PTR_OFFSET));
gd->arch.omap_boot_params.omap_bootmode =
*((u32 *)(dev_data + BOOT_MODE_OFFSET));//wlg: record the boot mode into global_data(temp)
}
}
#ifdef CONFIG_DRA7XX
/*
* We get different values for QSPI_1 and QSPI_4 being used, but
* don't actually care about this difference. Rather than
* mangle the later code, if we're coming in as QSPI_4 just
* change to the QSPI_1 value.
*/
if (gd->arch.omap_boot_params.omap_bootdevice == 11)
gd->arch.omap_boot_params.omap_bootdevice = BOOT_DEVICE_SPI;
#endif//wlg: return to s_init()
}
boot参数请查看TRM P4954,也就是技术文档的4954页,里面记录了参数保存的顺序以及名称,将其用指针(rom_params)取出后一次放入到gd所指向的gdata中,一般有:
1.boot原因
2.SPL文件来源等等
这些参数在后期启动uboot都有作用,这里不展开先,过几天在续写一下!2017.01.22夜
更多推荐
所有评论(0)