AM335x U-boot d程式碼分析過程1
題外話:
經過一段時間的學習,對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,下面開始分析,原始碼如下:
[plain] view plain copy print ?- 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:
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)中,它的具體形式如下
[plain] view plain copy print ?- ENTRY(save_boot_params)
- ldr r1, =OMAP_SRAM_SCRATCH_BOOT_PARAMS
- str r0, [r1]
- bx lr
- ENDPROC(save_boot_params)
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
#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?嘿嘿,我們繼續往下看:
[plain] view plain copy print ?- 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)
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語言
實際上在之前已經完成了sp的初始化,我們可以開始呼叫C語言函數了。同時,我們需要看到在文件board.c的最下方 #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r9”) 這實際上就是說,以後使用者呼叫gd的地方,實際上就是用了r9裡的數作為指標,而經過上面的分析,r9實際上指向了gdata,使用者定義的一個global_data的結構體。一句話說就是,gd不是一個野指標,而是實際上指向了gdata;以後不在展開討論; 繼續往下看,首先是一個條件編譯,這部分我覺得是跳過的,也就是說沒有被編譯進來,繼續往下看 進入了save_omap_boot_params()函式,該函式在arch/arm/cpu/armv7/Omap-common/Boot-common.c中定義;我們之前提及過boot引數,之前一直沒有儲存,就是因為當時才啟動,沒有合適的儲存boot引數的區域(gd指標還沒有實際指向有效的資料區域)。當用戶完成所有的準備工作後,那麼boot引數的儲存就必須要開始了:[cpp] view plain copy print ?
- 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 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()
- }
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夜