1、前言
在前面的文章《Uboot启动流程分析(三)》中,链接如下:
https://www.cnblogs.com/Cqlismy/p/12006287.html
已经对init_sequence_f前半部分函数进行了简单分析,前半部分主要是对调试串口终端进行了初始化,以及输出了一些必要的字符串,接下来,本篇文章将对init_sequence_f后半部分函数进行分析,后半部分主要是对DRAM的内存进行分配,并对gd的相关结构体成员进行初始化,在init_sequence_f函数初始化列表中,已经执行到了setup_dest_addr()函数,后半部分初始化列表如下:
/* 初始化序列函数数组 */ static init_fnc_t init_sequence_f[] = { ...
...
/* * Now that we have DRAM mapped and working, we can * relocate the code and continue running from DRAM. * * Reserve memory at end of RAM for (top down in that order): * - area that won't get touched by U-Boot and Linux (optional) * - kernel log buffer * - protected RAM * - LCD framebuffer * - monitor code * - board info struct */ setup_dest_addr,/* 设置目的地址(gd->ram_size,gd->ram_top,gd->relocaddr) */ reserve_round_4k, /* 对gd->relocaddr做4K对齐 */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && defined(CONFIG_ARM) reserve_mmu, /* 留出mmu的TLB表位置 */ #endif reserve_trace, /* 留出ddr调试追踪的内存 */ #if !defined(CONFIG_BLACKFIN) reserve_uboot, /* 留出重定位uboot占用的位置 */ #endif #ifndef CONFIG_SPL_BUILD reserve_malloc, /* 留出malloc的内存位置和ENV的内存大小 */ reserve_board, /* 留出bd所占用的内存大小(80字节) */ #endif setup_machine, /* 对于i.mx6ul该函数无效 */ reserve_global_data, /* 留出gd_t结构的内存大小(248字节) */ reserve_fdt, /* 留出设备树的内存大小(i.mx6ul没有用) */ reserve_arch, /* 空函数 */ reserve_stacks, /* 留出栈空间(16字节)并做16字节对齐 */ setup_dram_config, /* 设置DRAM的信息 */ show_dram_config, /* 显示DRAM的位置 */ display_new_sp, /* 显示新的sp位置 */ INIT_FUNC_WATCHDOG_RESET reloc_fdt, /* 重定位fdt(没有用) */ setup_reloc, /* 设置gd结构体的一些其他成员 */ NULL, };
2、board_init_f函数
程序将调用setup_dest_addr()函数,该函数的定义如下:
static int setup_dest_addr(void) { /* * Subtract specified amount of memory to hide so that it won't * get "touched" at all by U-Boot. By fixing up gd->ram_size * the Linux kernel should now get passed the now "corrected" * memory size and won't touch it either. This has been used * by arch/powerpc exclusively. Now ARMv8 takes advantage of * thie mechanism. If memory is split into banks, addresses * need to be calculated. */ gd->ram_size = board_reserve_ram_top(gd->ram_size); #ifdef CONFIG_SYS_SDRAM_BASE gd->ram_top = CONFIG_SYS_SDRAM_BASE; #endif gd->ram_top += get_effective_memsize(); gd->ram_top = board_get_usable_ram_top(gd->mon_len); gd->relocaddr = gd->ram_top; /* 将gd->ram_size、gd->ram_top、gd->relocaddr的值打印 */ printf(" gd->ram_size = %#lx " "gd->ram_top = %#lx " "gd->relocaddr = %#lx ", gd->ram_size, gd->ram_top, gd->relocaddr); return 0; }
函数setup_dest_addr()用来设置目的地址,设置gd结构体中的ram_size成员、ram_top成员和relocaddr成员,我们可以使用printf()函数打印输出查看这些值,其中ram_size表示DRAM的大小,ram_top表示DRAM的最终地址,relocaddr表示uboot重定位的地址,添加打印输出后,重新烧写uboot,将板子上电,该函数调用后,输出的值如下:
从图中可以看到,DRAM的大小为256MB,DRAM的最终地址为0x90000000,而此时uboot重定位的地址被设置为在DRAM的最终地址0x90000000。
程序继续往下运行,调用函数reserve_round_4k(),该函数的定义如下:
/* Round memory pointer down to next 4 kB limit */ static int reserve_round_4k(void) { gd->relocaddr &= ~(4096 - 1); return 0; }
该函数是gd结构体中的relocaddr地址做4KB字节对齐,在setup_dest_addr()函数调用后,relocaddr的值被设置为0x90000000,已经是4KB字节对齐了,因此,该函数调用后,值不变,还是0x90000000。
接下来,调用函数reserve_mmu(),该函数的定义如下:
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && defined(CONFIG_ARM) static int reserve_mmu(void) { /* reserve TLB table */ gd->arch.tlb_size = PGTABLE_SIZE; gd->relocaddr -= gd->arch.tlb_size; /* round down to next 64 kB limit */ gd->relocaddr &= ~(0x10000 - 1); /* 对gd->relocaddr做64KB字节对齐 */ gd->arch.tlb_addr = gd->relocaddr; printf("gd->arch.tlb_size = %#lx " "gd->arch.tlb_addr = %#lx " "gd->relocaddr = %#lx ", gd->arch.tlb_size, gd->arch.tlb_addr, gd->relocaddr); return 0; } #endif
该函数用于留出MMU的TLB表位置,留出该块内存后,对relocaddr地址做64KB字节对齐,调试输出如下所示:
从图中可以看到,MMU的TLB大小为0x4000,TLB的首地址为0x8fff0000,此时的relocaddr的值和TLB的首地址一样,也是0x8fff0000。
接下来,调用函数reserve_trace(),该函数用于留出uboot跟踪内存调试的地方,由于i.mx6ul的相关配置文件中没有定义相关宏,因此并没有分配出此块内存。
程序继续往下运行,将调用函数reserve_uboot(),该函数的定义如下所示:
static int reserve_uboot(void) { /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ gd->relocaddr -= gd->mon_len; gd->relocaddr &= ~(4096 - 1); gd->start_addr_sp = gd->relocaddr; printf("gd->mon_len = %#lx " "gd->relocaddr = %#lx " "gd->start_addr_sp = %#lx ", gd->mon_len, gd->relocaddr, gd->start_addr_sp); return 0; }
函数reserve_uboot()用于留出重定位后uboot代码所占用的内存位置,uboot所占用的内存位置空间由gd结构体中mon_len成员变量指定,调试输出如下所示:
从上图中可以看出,uboot重定位后代码所占用的内存大小为0xb4bb4,分配后uboot重定位内存位置后,对relocaddr的值做4KB字节对齐,此时relocaddr的值为0x8ff3b000,同时重新将gd结构体中的start_addr_sp成员变量设置和此时的relocaddr的值一样,也就是0x8ff3b000。
由于i.mx6ul中的配置文件中没有定义宏CONFIG_SPL_BUILD,因此,接下来将会调用函数reserve_malloc(),该函数的定义如下:
/* reserve memory for malloc() area */ static int reserve_malloc(void) { gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; printf("TOTAL_MALLOC_LEN = %#lx " "gd->start_addr_sp = %#lx ", TOTAL_MALLOC_LEN, gd->start_addr_sp); return 0; }
对于TOTAL_MALLOC_LEN宏的定义如下:
/* 对于imx6ul从nand flash启动 */ #define CONFIG_ENV_SECT_SIZE (128 << 10) /* 128KB */ #define CONFIG_ENV_SIZE CONFIG_ENV_SECT_SIZE /* Size of malloc() pool */ #define CONFIG_SYS_MALLOC_LEN (16 * SZ_1M) /* 16MB */
#define TOTAL_MALLOC_LEN (CONFIG_SYS_MALLOC_LEN + CONFIG_ENV_SIZE)
从宏定义可以计算出TOTAL_MALLOC_LEN = 16MB + 128KB = 0x1020000,调试输出如下:
从上面的分析和输出可以知道,函数reserve_malloc()分配出了malloc的内存区域,分配的大小为0x1020000,包含了16MB的malloc内存池以及128KB的环境变量内存区域,malloc内存分配完成后,重新赋值gd结构体中的start_addr_sp成员变量,此时的start_addr_sp值为0x8ef1b000。
接下来,继续调用函数reserve_board(),该函数的定义如下:
/* (permanently) allocate a Board Info struct */ static int reserve_board(void) { if (!gd->bd) { gd->start_addr_sp -= sizeof(bd_t); gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); memset(gd->bd, '