本文共 7608 字,大约阅读时间需要 25 分钟。
以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为例
[uboot] uboot流程系列:
===================================================================================
在某些情况下,uboot是在某些只读存储器上运行,比如ROM、nor flash等等。
在uboot被重定向到RAM(可读可写)之前,我们都无法写入数据,更无法通过全局变量来传递数据。 而global_data则是为了解决这个问题。 这里顺便一下,后续的uboot的relocation操作,也就是uboot的重定向操作,最主要的目的也是为了解决这个问题,后续会专门说明。global_data又称之为GD.
简单地说,uboot把global_data放在RAM区,并且使用global_data来存储全局数据。由此来解决上述场景中无法使用全局变量的问题。global_data数据结构结构体定义为struct global_data,被typedef为gd_t。
也就是说可以直接通过struct global_data或者gd_t来进行声明。 struct global_data定义如下(过滤掉一些被宏定义包含的部分): include/asm-generic/global_data.htypedef 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; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
其他成员在后续时候到的时候在进行说明。
(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的地址返回} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
(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} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
(3)arm平台如何分配global_data区域,并保存其地址。
代码如下,去除掉被宏定义包含的无关代码部分 arch/arm/lib/crt0.SENTRY(_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也就是其参数。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
注意:最终global_data的地址存放在r9中了。
内存分布如下:
———————-CONFIG_SYS_LOAD_ADDR —————————–高地址…………………………….. early malloc 内存池
————————-early malloc 内存池基地址 —————————
………………………………… global_data区域
—————-global_data基地址(r9), 也是堆栈的起始地址————-
………………………………………堆栈空间
————————————–堆栈结束—————————————-低地址
注意:最终global_data的地址存放在r9中了。前面我们一直强调了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") 1
DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd,并且其地址是r9中的值。
一旦使用了DECLARE_GLOBAL_DATA_PTR声明之后,后续就可以直接使用gd变量,也就是global_data了。DECLARE_GLOBAL_DATA_PTR定义了gd_t *gd,并且其地址是r9中的值。
一旦使用了DECLARE_GLOBAL_DATA_PTR声明之后,后续就可以直接使用gd变量,也就是global_data了。 简单例子如下: common/board_r.cDECLARE_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;} 1 2 3 4 5 6 7 8 9 10 11 12 13
global_data相对比较简单,也就不多说了。