[uboot] global_data介绍

16 篇文章 5 订阅

==

一、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, '\0', 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相对比较简单,也就不多说了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值