• [uboot] (番外篇)global_data介绍(转)


    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
    本文链接:https://blog.csdn.net/ooonebook/article/details/53013545

    以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例

    [uboot] uboot流程系列:
    [project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
    [uboot] (第一章)uboot流程——概述
    [uboot] (第二章)uboot流程——uboot-spl编译流程

    ===================================================================================
    一、global_data功能
    1、global_data存在的意义

    在某些情况下,uboot是在某些只读存储器上运行,比如ROM、nor flash等等。
    在uboot被重定向到RAM(可读可写)之前,我们都无法写入数据,更无法通过全局变量来传递数据。
    而global_data则是为了解决这个问题。
    这里顺便一下,后续的uboot的relocation操作,也就是uboot的重定向操作,最主要的目的也是为了解决这个问题,后续会专门说明。
    2、 global_data简单介绍

    global_data又称之为GD.
    简单地说,uboot把global_data放在RAM区,并且使用global_data来存储全局数据。由此来解决上述场景中无法使用全局变量的问题。
    二、global_data数据结构
    1、数据结构说明

    global_data数据结构结构体定义为struct global_data,被typedef为gd_t。
    也就是说可以直接通过struct global_data或者gd_t来进行声明。
    struct global_data定义如下(过滤掉一些被宏定义包含的部分):
    include/asm-generic/global_data.h

    typedef struct global_data {
        bd_t *bd;
        unsigned long flags;
        unsigned int baudrate;
        unsigned long cpu_clk;  /* CPU clock in Hz!     */
        unsigned long bus_clk;
        /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */
        unsigned long pci_clk;
        unsigned long mem_clk;
        unsigned long have_console; /* serial_init() was called */
        unsigned long env_addr; /* Address  of Environment struct */
        unsigned long env_valid;    /* Checksum of Environment valid? */
        unsigned long ram_top;  /* Top address of RAM used by U-Boot */
        unsigned long relocaddr;    /* Start address of U-Boot in RAM */
        phys_size_t ram_size;   /* RAM size */
        unsigned long mon_len;  /* monitor len */
        unsigned long irq_sp;       /* irq stack pointer */
        unsigned long start_addr_sp;    /* start_addr_stackpointer */
        unsigned long reloc_off;
        struct global_data *new_gd; /* relocated global data */
        const void *fdt_blob;   /* Our device tree, NULL if none */
        void *new_fdt;      /* Relocated FDT */
        unsigned long fdt_size; /* Space reserved for relocated FDT */
        struct jt_funcs *jt;        /* jump table */
        char env_buf[32];   /* buffer for getenv() before reloc. */
        unsigned long timebase_h;
        unsigned long timebase_l;
        struct udevice *cur_serial_dev; /* current serial device */
        struct arch_global_data arch;   /* architecture-specific data */

    } gd_t;

    2、成员说明

        重点说明
            bd_t *bd:board info数据结构定义,位于文件 include/asm-arm/u-boot.h定义,主要是保存开发板的相关参数。
            unsigned long env_addr:环境变量的地址。
            unsigned long ram_top:RAM空间的顶端地址
            unsigned long relocaddr:UBOOT重定向后地址
            phys_size_t ram_size:物理ram的size
            unsigned long irq_sp:中断的堆栈地址
            unsigned long start_addr_sp:堆栈地址
            unsigned long reloc_off:uboot的relocation的偏移
            struct global_data *new_gd:重定向后的struct global_data结构体
            const void *fdt_blob:我们设备的dtb地址
            void *new_fdt:relocation之后的dtb地址
            unsigned long fdt_size:dtb的长度
            struct udevice *cur_serial_dev:当前使用的串口设备。

    其他成员在后续时候到的时候在进行说明。
    三、global_data存放位置以及如何获取其地址
    1、global_data区域设置代码

    (1)首先参考一下分配global_data的代码。
    common/init/board_init.c

    // 这个函数用于给global_data分配空间,在relocation之前调用
    // 传入的参数是顶部地址,但是不一定是要内存顶部的地址,可以自己进行规划,后面_main函数会说明
    ulong board_init_f_alloc_reserve(ulong top)
    {
        /* Reserve early malloc arena */
    #if defined(CONFIG_SYS_MALLOC_F)
        top -= CONFIG_SYS_MALLOC_F_LEN;
    // 先从顶部向下分配一块CONFIG_SYS_MALLOC_F_LEN大小的空间给early malloc使用
    // 关于CONFIG_SYS_MALLOC_F_LEN可以参考README
    // 这块内存是用于在relocation前用于给malloc函数提供内存池。
    #endif

        /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
        top = rounddown(top-sizeof(struct global_data), 16);
    // 继续向下分配sizeof(struct global_data)大小的内存给global_data使用,向下16byte对齐
    // 这时候得到的地址就是global_data的地址。

        return top;
    // 将top,也就是global_data的地址返回
    }


    (2)然后看一下初始化global_data区域的代码。
    common/init/board_init.c
    去除无关代码的部分

    // 这个函数用于对global_data区域进行初始化,也就是清空global_data区域
    // 传入的参数就是global_data的基地址
    void board_init_f_init_reserve(ulong base)
    {
        struct global_data *gd_ptr;

        /*
         * clear GD entirely and set it up.
         * Use gd_ptr, as gd may not be properly set yet.
         */

        gd_ptr = (struct global_data *)base;
        /* zero the area */
        memset(gd_ptr, '', sizeof(*gd));
    // 先通过memset函数对global_data数据结构进行清零

        /* next alloc will be higher by one GD plus 16-byte alignment */
        base += roundup(sizeof(struct global_data), 16);
    // 因为global_data区域是16Byte对齐的,对齐后,后面的地址就是early malloc的内存池的地址,具体参考上述board_init_f_alloc_reserve
    // 所以这里就获取了early malloc的内存池的地址

        /*
         * record early malloc arena start.
         * Use gd as it is now properly set for all architectures.
         */
    #if defined(CONFIG_SYS_MALLOC_F)
        /* go down one 'early malloc arena' */
        gd->malloc_base = base;
    // 将内存池的地址写入到gd->malloc_base中
        /* next alloc will be higher by one 'early malloc arena' size */
        base += CONFIG_SYS_MALLOC_F_LEN;
    //加上CONFIG_SYS_MALLOC_F_LEN,获取early malloc的内存池的末尾地址,这里并没有什么作用,是为了以后在early malloc的内存池后面多加一个区域时的修改方便。
    #endif
    }

    (3)arm平台如何分配global_data区域,并保存其地址。
    代码如下,去除掉被宏定义包含的无关代码部分
    arch/arm/lib/crt0.S

    ENTRY(_main)
    /*
     * Set up initial C runtime environment and call board_init_f(0).
     */
        ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
    @@ 预设堆栈指针为CONFIG_SYS_INIT_SP_ADDR
    @@ 在tiny210中初步设置为如下(include/configs/tiny210.h):
    @@ #define CONFIG_SYS_SDRAM_BASE           0x20000000
    @@ #define MEMORY_BASE_ADDRESS  CONFIG_SYS_SDRAM_BASE
    @@ #define PHYS_SDRAM_1     MEMORY_BASE_ADDRESS
    @@ #define CONFIG_SYS_LOAD_ADDR     (PHYS_SDRAM_1 + 0x1000000)  /* default load address */
    @@ #define CONFIG_SYS_INIT_SP_ADDR CONFIG_SYS_LOAD_ADDR
    @@ 最终可以得到CONFIG_SYS_INIT_SP_ADDR是0x3000_0000,也就是uboot relocation的起始地址
    @@ 补充一下,DDR的空间是0x2000_0000-0x4000_0000

    @@ 注意!!!这里只是预设的堆栈地址,而不是最终的堆栈地址!!!

        bic sp, sp, #7  /* 8-byte alignment for ABI compliance */
    @@ 8byte对齐

        mov r0, sp
        bl  board_init_f_alloc_reserve
    @@ 将sp的值放到r0中,也就是作为board_init_f_alloc_reserve的参数
    @@ 返回之后,r0里面存放的是global_data的地址
    @@ 注意,同时也是堆栈地址,因为堆栈是向下增长的,所以不必担心和global_data冲突的问题

    @@ 综上,此时r0存放的,既是global_data的地址,也是堆栈的地址

        mov sp, r0
    @@ 把堆栈地址r0存放到sp中

        /* set up gd here, outside any C code */
        mov r9, r0
    @@ 把global_data的地址存放在r9中
    @@ 此时r0存放的还是global_data的地址

        bl  board_init_f_init_reserve
    @@ 调用board_init_f_init_reserve对global_data进行初始化,r0也就是其参数。


    注意:最终global_data的地址存放在r9中了。
    2、global_data内存分布

    内存分布如下:
    ———————-CONFIG_SYS_LOAD_ADDR —————————–高地址

    …………………………….. early malloc 内存池

    ————————-early malloc 内存池基地址 —————————

    ………………………………… global_data区域

    —————-global_data基地址(r9), 也是堆栈的起始地址————-

    ………………………………………堆栈空间

    ————————————–堆栈结束—————————————-低地址
    注意:最终global_data的地址存放在r9中了。
    四、global_data使用方式
    1、原理说明

    前面我们一直强调了global_data的地址存放在r9中了。
    所以当我们需要global_data的时候,直接从r9寄存器中获取其地址即可。

    uboot中定义了一个宏DECLARE_GLOBAL_DATA_PTR,使我们可以更加简单地获取global_data。
    定义如下:
    arch/arm/include/asm/global_data.h

    #define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r9")

     
    DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd,并且其地址是r9中的值。
    一旦使用了DECLARE_GLOBAL_DATA_PTR声明之后,后续就可以直接使用gd变量,也就是global_data了。
    2、使用示例

    DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd,并且其地址是r9中的值。
    一旦使用了DECLARE_GLOBAL_DATA_PTR声明之后,后续就可以直接使用gd变量,也就是global_data了。
    简单例子如下:
    common/board_r.c

    DECLARE_GLOBAL_DATA_PTR
    // 通过DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd
    // 相当于如下:
    // #define DECLARE_GLOBAL_DATA_PTR      register volatile gd_t *gd asm ("r9")

    static int initr_reloc(void)
    {
        /* tell others: relocation done */
        gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT;
    // 直接使用gd变量,也就是uboot的global_data。

        return 0;
    }


    global_data相对比较简单,也就不多说了。
    ————————————————
    版权声明:本文为CSDN博主「ooonebook」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/ooonebook/article/details/53013545

  • 相关阅读:
    极光推送SDK通过泰尔终端实验室检测,符合统一推送接口标准
    极光小课堂|手把手教你做接口测试
    一键登录怎么在iOS端实现?这篇文章教会你!
    一键登录已成大势所趋,Android端操作指南来啦!
    极光一键登录:更快捷、安全的登录认证方式,简单集成即可实现
    跨浏览器问题的五种解决方案
    Laravel 搭建 Composer 包,实现配置 Config、门面 Facade、服务 Service、发布到 Packagist
    How to Install ClamAV on Ubuntu 20.04 and Scan for Vulnerabilities
    单点登录(SSO)看这一篇就够了
    一口气说出 OAuth2.0 的四种授权方式
  • 原文地址:https://www.cnblogs.com/idyllcheung/p/12011385.html
Copyright © 2020-2023  润新知