AM335x U-boot d程式碼分析過程2
題外話
之前那一篇試水了一下,我回過頭看一下,覺得還是含水量還是太大了。這個系列的部落格的目的應該是讓讀者看完以後,對armV7 cpu的u-boot有個更加深的瞭解,也讓我把知道的東西都寫出來,加深認識,作為後期複習的工具。
原始碼分析
之前那一篇講到了save_omap_boot_params()函式,該函式位於arch\arm\cpu\armv7\Omap-common\Boot-common.c中。我們這裡簡單的重複一下其原始碼:
[cpp] view plain- 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() }
一開始就定義了rom_params,從後面的使用情況來看,其應該儲存著某個地址,這個地址上面儲存了ROM從某個裝置(MMC卡或者SD卡)讀取到MLO(SPL)時的一些具體資訊,所以關鍵就有兩處:
1. 這個地址到底是什麼?
2. 這個地址上到底儲存了什麼東西?
先回答第一個問題,這個地址到底是什麼?通過SI,我們瞭解到:
rom_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS) (本文件中定義)
#define OMAP_SRAM_SCRATCH_BOOT_PARAMS (SRAM_SCRATCH_SPACE_ADDR + 0x24) (arch\arm\include\asm\Omap-common.h中定義)
#ifdef CONFIG_AM33XX (arch\….\arch-am335x\Omap.h文件中定義)
#define NON_SECURE_SRAM_START 0x402F0400
#define NON_SECURE_SRAM_END 0x40310000
#define SRAM_SCRATCH_SPACE_ADDR 0x4030B800
所示實際上,rom_params = *(0x4030B800 + 0x24),也就是說指向了SRAM的摸一個部分。前一篇中有所記錄:
在start.S中的save_boot_params,就是將r0裡的資料複製到OMAP_SRAM_SCRATCH_BOOT_PARAMS這個位置,這個位置也就是上面的地址0x4030B800 + 0x24,也就上司說,這段程式碼的意思就是將在開機載入SPL完成後的r0,賦值給了這段程式碼裡的rom_params 。我們先來看看這個r0是什麼。
在am335x的TRM的4954頁中有記錄:
The R0 register points to the Booting Parameters structure which contains various information about the booting execution. Table 26-40 details this structure.
上面的意思就是說,開機完成後,r0裡面儲存了一個指標,這個指標指向了一個叫做Boot Parament的一堆資料(之所以叫一堆資料,是因為從地址開始offset 0開始到offset 0Ah都分佈著boot的引數):
00h-03h 一共4個位元組 儲存著保留資料
04h-07h 一共4個位元組 儲存著指標指向儲存裝置描述符
08h 共計1個位元組,儲存著從什麼裝置載入的SPL或uboot,一般是MMC,UART,SPI,XIP等等
09h 共計1個位元組,儲存著boot的原因,無外非是重啟或者看門狗時間復位等等
0Ah 共計1個位元組,保留
所以說復位完成後的r0實際上就是指向了這個boot parament中offset為00h的地址。所以最終,rom_params 就是等於r0,這個資料實際上可以作為指標指向上面的boot parament。
而0x4030B800 +24h也是個有趣的地址,
它實際上就是指向了Public RAM中,Download Image區域的結尾和Public stack開始,還記得我們更早之前定義的sp把,實際上這個sp就是處於這個6kB的Public stack中,而現在的OMAP_SRAM_SCRATCH_BOOT_PARAMS=0x4030B800 +24h也是在這個區域內。也就是說,boot復位完成後,r0實際被複制到了該區域暫時儲存,直到進入本函式中才重新被利用!
————————r0和boot parament以及rom_params 之間的關係介紹完畢—————–
回到我們的程式,先對rom_params 進行檢查,也就是對boot parament實際所處的位置進行檢查,這說明在復位完成後的boot parament資料實際上也被保留在了6kB的Public stack中,但是實際被儲存到哪裡我目前也不得而知,以後知道了再回過來修改。
那麼接下來就是利用rom_params 這個變數,把boot parament裡有效的資料都取出來放置到gd所指向的結構體裡(這個結構體的實體放置在.data段中):
第一個引數:gd->arch.omap_boot_params.omap_bootdevice,利用rom_params +08h,得到boot裝置型別
第二個引數:gd->arch.omap_boot_params.ch_flags,利用rom_params +0Ah,得到boot原因
第三個引數:gd->arch.omap_boot_params.omap_bootmode,利用rom_params +04h得到指標指向裝置描述符,再兩次利用神祕offset+指標得到了boot的方式,這個燈我找準了在來詳細的分析;
只要先記著了,這幾個引數在後期拷貝u-boot的image的時候會起到關鍵作用!
回到程式碼,arch/arm/cpu/armv7/am335x/board.c- s_init()
[cpp] view plain copy print ?- watchdog_disable();
- timer_init();
- set_uart_mux_conf();//wlg: initilize the pin mux as uart
- setup_clocks_for_console();
- uart_soft_reset();
watchdog_disable();
timer_init();
set_uart_mux_conf();//wlg: initilize the pin mux as uart
setup_clocks_for_console();
uart_soft_reset();
從上往下,依次1. 實現了看門狗的禁用
2. 計數器的初始化
3. uart外設的引腳複用
4. uart的所見覆位。
以上功能都是比較簡單暴力的利用am335x的暫存器結構體實現的相應暫存器的配置,就以watchdog_disable();為例
[cpp] view plain copy print ?- static void watchdog_disable(void)
- {
- struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
- writel(0xAAAA, &wdtimer->wdtwspr);
- while (readl(&wdtimer->wdtwwps) != 0x0)
- ;
- writel(0x5555, &wdtimer->wdtwspr);
- while (readl(&wdtimer->wdtwwps) != 0x0)
- ;
- }
static void watchdog_disable(void)
{
struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
writel(0xAAAA, &wdtimer->wdtwspr);
while (readl(&wdtimer->wdtwwps) != 0x0)
;
writel(0x5555, &wdtimer->wdtwspr);
while (readl(&wdtimer->wdtwwps) != 0x0)
;
}
看上面的程式碼,首先是將一個巨集定義的資料WDT_BASE強制轉換成一個指向wd_timer 結構體的指標,很明顯,WDT_BASE一定就是實際指向某個看門狗控制暫存器的地址,我們來驗證一下:
WDT_BASE = 0x44E35000 (在arch-am335x\Hardware-am335x.h中有如下定義)
/* Watchdog Timer */
而通過檢視am335x的暫存器地址
我們得到了0x44E35000 正是指向Watchdog Timer Registers
再來驗證一下wd_timer 這個結構體是怎麼定義的
[cpp] view plain copy print ?- /* Watchdog timer registers */
- struct wd_timer {
- unsigned int resv1[4];
- unsigned int wdtwdsc; /* offset 0x010 */
- unsigned int wdtwdst; /* offset 0x014 */
- unsigned int wdtwisr; /* offset 0x018 */
- unsigned int wdtwier; /* offset 0x01C */
- unsigned int wdtwwer; /* offset 0x020 */
- unsigned int wdtwclr; /* offset 0x024 */
- unsigned int wdtwcrr; /* offset 0x028 */
- unsigned int wdtwldr; /* offset 0x02C */
- unsigned int wdtwtgr; /* offset 0x030 */
- unsigned int wdtwwps; /* offset 0x034 */
- unsigned int resv2[3];
- unsigned int wdtwdly; /* offset 0x044 */
- unsigned int wdtwspr; /* offset 0x048 */
- unsigned int resv3[1];
- unsigned int wdtwqeoi; /* offset 0x050 */
- unsigned int wdtwqstar; /* offset 0x054 */
- unsigned int wdtwqsta; /* offset 0x058 */
- unsigned int wdtwqens; /* offset 0x05C */
- unsigned int wdtwqenc; /* offset 0x060 */
- unsigned int resv4[39];
- unsigned int wdt_unfr; /* offset 0x100 */
- };
/* Watchdog timer registers */
struct wd_timer {
unsigned int resv1[4];
unsigned int wdtwdsc; /* offset 0x010 */
unsigned int wdtwdst; /* offset 0x014 */
unsigned int wdtwisr; /* offset 0x018 */
unsigned int wdtwier; /* offset 0x01C */
unsigned int wdtwwer; /* offset 0x020 */
unsigned int wdtwclr; /* offset 0x024 */
unsigned int wdtwcrr; /* offset 0x028 */
unsigned int wdtwldr; /* offset 0x02C */
unsigned int wdtwtgr; /* offset 0x030 */
unsigned int wdtwwps; /* offset 0x034 */
unsigned int resv2[3];
unsigned int wdtwdly; /* offset 0x044 */
unsigned int wdtwspr; /* offset 0x048 */
unsigned int resv3[1];
unsigned int wdtwqeoi; /* offset 0x050 */
unsigned int wdtwqstar; /* offset 0x054 */
unsigned int wdtwqsta; /* offset 0x058 */
unsigned int wdtwqens; /* offset 0x05C */
unsigned int wdtwqenc; /* offset 0x060 */
unsigned int resv4[39];
unsigned int wdt_unfr; /* offset 0x100 */
};
再次通過資料手冊,我們得到了
經過對比,完美的符合。也就是說,接下來的操作writel(0xAAAA, &wdtimer->wdtwspr);實際上就是將am335x晶片上的那個叫做WTD-WSPR這個暫存器修改成0xAAAA,那又是什麼意思?我們繼續往下看
原來是寫入了一個計數器。
以上介紹瞭如何用WTD的基地址轉換成一個結構體指標後,利用其相應的結構體元素進行讀寫操作,來實現相應功能的修改。
第三篇繼續寫,敬請期待。