1. 程式人生 > >AM335x U-boot d程式碼分析過程2

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
copy print ?
  1. void save_omap_boot_params(void)  
  2. {  
  3.     u32 rom_params = *((u32 *)OMAP_SRAM_SCRATCH_BOOT_PARAMS);//wlg: look in TRM P4954  
  4.     u8 boot_device;//wlg: 
      
  5.     u32 dev_desc, dev_data;  
  6.   
  7.     if ((rom_params <  NON_SECURE_SRAM_START) ||  
  8.         (rom_params > NON_SECURE_SRAM_END))  
  9.         return;  
  10.   
  11.     /* 
  12.      * rom_params can be type casted to omap_boot_parameters and 
  13.      * used. But it not correct to assume that romcode structure 
  14.      * encoding would be same as u-boot. So use the defined offsets. 
  15.      */  
  16.     gd->arch.omap_boot_params.omap_bootdevice = boot_device =  
  17.                    *((u8 *)(rom_params + BOOT_DEVICE_OFFSET));//wlg: P4954, offset = 0x8,  
  18.                     //wlg: it point to Current Booting Device!the parament will be use to copy uboot  
  19.     gd->arch.omap_boot_params.ch_flags =  
  20.                 *((u8 *)(rom_params + CH_FLAGS_OFFSET));  
  21.   
  22.     if ((boot_device >= MMC_BOOT_DEVICES_START) &&//wlg: boot device list from 0-6,XIP,MMC0,MMC1 and so on  
  23.         (boot_device <= MMC_BOOT_DEVICES_END)) {  
  24. #if !defined(CONFIG_AM33XX) && !defined(CONFIG_TI81XX) && /*wlg: skip this */  
  25.     !defined(CONFIG_AM43XX)  
  26.         if ((omap_hw_init_context() ==  
  27.                       OMAP_INIT_CONTEXT_UBOOT_AFTER_SPL)) {  
  28.             gd->arch.omap_boot_params.omap_bootmode =  
  29.             *((u8 *)(rom_params + BOOT_MODE_OFFSET));  
  30.         } else  
  31. #endif  
  32.         {  
  33.             dev_desc = *((u32 *)(rom_params + DEV_DESC_PTR_OFFSET));//wlg: point to memory device descriptor  
  34.             dev_data = *((u32 *)(dev_desc + DEV_DATA_PTR_OFFSET));  
  35.             gd->arch.omap_boot_params.omap_bootmode =  
  36.                     *((u32 *)(dev_data + BOOT_MODE_OFFSET));//wlg: record the boot mode into global_data(temp)  
  37.         }  
  38.     }  
  39.   
  40. #ifdef CONFIG_DRA7XX  
  41.     /* 
  42.      * We get different values for QSPI_1 and QSPI_4 being used, but 
  43.      * don’t actually care about this difference.  Rather than 
  44.      * mangle the later code, if we’re coming in as QSPI_4 just 
  45.      * change to the QSPI_1 value. 
  46.      */  
  47.     if (gd->arch.omap_boot_params.omap_bootdevice == 11)  
  48.         gd->arch.omap_boot_params.omap_bootdevice = BOOT_DEVICE_SPI;  
  49. #endif//wlg: return to s_init()   
  50. }  
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 ?
  1.        watchdog_disable();  
  2. timer_init();  
  3. set_uart_mux_conf();//wlg: initilize the pin mux as uart  
  4. setup_clocks_for_console();  
  5. 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 ?
  1. static void watchdog_disable(void)  
  2. {  
  3.     struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;  
  4.   
  5.     writel(0xAAAA, &wdtimer->wdtwspr);  
  6.     while (readl(&wdtimer->wdtwwps) != 0x0)  
  7.         ;  
  8.     writel(0x5555, &wdtimer->wdtwspr);  
  9.     while (readl(&wdtimer->wdtwwps) != 0x0)  
  10.         ;  
  11. }  
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 */

#define WDT_BASE 0x44E35000


    而通過檢視am335x的暫存器地址



    我們得到了0x44E35000 正是指向Watchdog Timer Registers


    再來驗證一下wd_timer 這個結構體是怎麼定義的

[cpp] view plain copy print ?
  1. /* Watchdog timer registers */  
  2. struct wd_timer {  
  3.     unsigned int resv1[4];  
  4.     unsigned int wdtwdsc;   /* offset 0x010 */  
  5.     unsigned int wdtwdst;   /* offset 0x014 */  
  6.     unsigned int wdtwisr;   /* offset 0x018 */  
  7.     unsigned int wdtwier;   /* offset 0x01C */  
  8.     unsigned int wdtwwer;   /* offset 0x020 */  
  9.     unsigned int wdtwclr;   /* offset 0x024 */  
  10.     unsigned int wdtwcrr;   /* offset 0x028 */  
  11.     unsigned int wdtwldr;   /* offset 0x02C */  
  12.     unsigned int wdtwtgr;   /* offset 0x030 */  
  13.     unsigned int wdtwwps;   /* offset 0x034 */  
  14.     unsigned int resv2[3];  
  15.     unsigned int wdtwdly;   /* offset 0x044 */  
  16.     unsigned int wdtwspr;   /* offset 0x048 */  
  17.     unsigned int resv3[1];  
  18.     unsigned int wdtwqeoi;  /* offset 0x050 */  
  19.     unsigned int wdtwqstar; /* offset 0x054 */  
  20.     unsigned int wdtwqsta;  /* offset 0x058 */  
  21.     unsigned int wdtwqens;  /* offset 0x05C */  
  22.     unsigned int wdtwqenc;  /* offset 0x060 */  
  23.     unsigned int resv4[39];  
  24.     unsigned int wdt_unfr;  /* offset 0x100 */  
  25. };  
/* 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的基地址轉換成一個結構體指標後,利用其相應的結構體元素進行讀寫操作,來實現相應功能的修改。

          

    第三篇繼續寫,敬請期待。