AM335x U-boot d代码分析过程2

题外话

    之前那一篇试水了一下,我回过头看一下,觉得还是含水量还是太大了。这个系列的博客的目的应该是让读者看完以后,对armV7 cpu的u-boot有个更加深的了解,也让我把知道的东西都写出来,加深认识,作为后期复习的工具。

源码分析

    之前那一篇讲到了save_omap_boot_params()函数,该函数位于arch\arm\cpu\armv7\Omap-common\Boot-common.c中。我们这里简单的重复一下其源码:

  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()

  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();为例

  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 这个结构体是怎么定义的

  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的基地址转换成一个结构体指针后,利用其相应的结构体元素进行读写操作,来实现相应功能的修改。

          

    第三篇继续写,敬请期待。




猜你喜欢

转载自blog.csdn.net/tbadolph/article/details/77852248