基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f

基于IMX6Q的uboot启动流程分析(1):uboot入口函数
基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f
基于IMX6Q的uboot启动流程分析(3):_main函数之relocate_code与board_init_r
基于IMX6Q的uboot启动流程分析(4):uboot中的串口设备

第2章:_main函数之board_init_f

上一节内容中_start函数最后调用_main函数,其位置在arch/arm/lib/crt0.S文件中。

_main函数的功能已经在文件中进行英文注释,先看看_main函数实现的功能有哪些。从注释中,我们可以大概知道_main函数的执行顺序为:

  • 先设置用于调用board_init_f()函数的初始环境,该环境仅仅是提供了堆栈和存储位置GD(global data)结构,两者都是位于可以使用的RAM(SRAM,locked cache…)中,在调用board_init_f()函数前,GD应该被清0;
  • 调用board_init_f()函数,该函数的功能为从system RAM(DRAM,DDR…)中执行准备硬件,当system RAM还不能够使用的时,必须要使用目前的GD存储传递到后续阶段的所有数据,这些数据包括重定位的目标,将来的堆栈和GD的位置
  • 设置中间环境,其中堆栈和GD是由board_init_f()函数在system RAM中进行分配的,但此时的bss和初始化的非常量仍然不能使用;
  • 对于正常的uboot引导(非SPL),调用relocate_code()函数,该函数的功能将uboot从当前的位置重新转移到由board_init_f()函数计算的目标位置
  • 对于SPL,board_init_f()函数仅仅是返回(crt0),没有代码的重定位;
  • 设置用于调用board_init_r()函数的最终环境,该环境将有bss段(初始化为0),初始化非常量数据(初始化为预期值),并入栈到system RAM中,GD保留了board_init_f()函数设置的值;
  • 为了使uboot正常运行(非SPL),某些CPU还有一些关于内存的工作要做,调用c_runtime_cpu_setup()函数;
  • 调用board_init_r()函数。

_main函数的内容为:

 
118 
119 #if ! defined(CONFIG_SPL_BUILD)
120 
121 /*
122  * Set up intermediate environment (new sp and gd) and call
123  * relocate_code(addr_moni). Trick here is that we'll return
124  * 'here' but relocated.
125  */
126 
127         ldr     r0, [r9, #GD_START_ADDR_SP]     /* sp = gd->start_addr_sp */
128         bic     r0, r0, #7      /* 8-byte alignment for ABI compliance */
129         mov     sp, r0
130         ldr     r9, [r9, #GD_NEW_GD]            /* r9 <- gd->new_gd */
131 
132         adr     lr, here
133         ldr     r0, [r9, #GD_RELOC_OFF]         /* r0 = gd->reloc_off */
134         add     lr, lr, r0
135 #if defined(CONFIG_CPU_V7M)
136         orr     lr, #1                          /* As required by Thumb-only */
137 #endif
138         ldr     r0, [r9, #GD_RELOCADDR]         /* r0 = gd->relocaddr */
139         b       relocate_code
140 here:
141 /*
142  * now relocate vectors
143  */
144 
145         bl      relocate_vectors
146 
147 /* Set up final (full) environment */
148 
149         bl      c_runtime_cpu_setup     /* we still call old routine here */
150 #endif
151 #if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)
152 
153 #if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_SPL_EARLY_BSS)
154         CLEAR_BSS
155 #endif
156 
157 # ifdef CONFIG_SPL_BUILD
158         /* Use a DRAM stack for the rest of SPL, if requested */
159         bl      spl_relocate_stack_gd
160         cmp     r0, #0
161         movne   sp, r0
162         movne   r9, r0
163 # endif
164 
165 #if ! defined(CONFIG_SPL_BUILD)
166         bl coloured_LED_init
167         bl red_led_on
168 #endif
169         /* call board_init_r(gd_t *id, ulong dest_addr) */
170         mov     r0, r9                  /* gd_t */
171         ldr     r1, [r9, #GD_RELOCADDR] /* dest_addr */
172         /* call board_init_r */
173 #if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
174         ldr     lr, =board_init_r       /* this is auto-relocated! */
175         bx      lr
176 #else
177         ldr     pc, =board_init_r       /* this is auto-relocated! */
178 #endif
179         /* we should not return here. */
180 #endif
181 
182 ENDPROC(_main)                                  

_main函数的实现比较复杂,需要对其分部分进行分析:

  1. 第一部分,首先是设置初始化C运行环境,然后调用board_init_f(0)函数;
  2. 第二部分,设置中间环境(新的sp指针值和gd指针值),并且调用relocate_code函数;
  3. 第三部分,主要是完成最终的环境设置,并且调用board_init_r()函数。

下面将详细介绍。

2.1 board_init_f函数

2.1.1 初始化C运行环境

在调用board_init_f函数前,要设置初始化C运行环境,这部分代码为:

91 ENTRY(_main)
 92 
 93 /*
 94  * Set up initial C runtime environment and call board_init_f(0).
 95  */
 96 
 97 #if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
 98         ldr     r0, =(CONFIG_TPL_STACK)
 99 #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
100         ldr     r0, =(CONFIG_SPL_STACK)
101 #else
102         ldr     r0, =(CONFIG_SYS_INIT_SP_ADDR)
103 #endif
104         bic     r0, r0, #7      /* 8-byte alignment for ABI compliance */
105         mov     sp, r0
106         bl      board_init_f_alloc_reserve
107         mov     sp, r0
108         /* set up gd here, outside any C code */
109         mov     r9, r0
110         bl      board_init_f_init_reserve
111 
112 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_EARLY_BSS)
113         CLEAR_BSS
114 #endif
115 
116         mov     r0, #0
117         bl      board_init_f
  • 第102-105行,将sp设置为CONFIG_SYS_INIT_SP_ADDR,即sp = 0x0093FF00。

  • 第106行,调用board_init_f_alloc_reserve函数,留出早期的malloc内存区域和global_data结构体内存区域。其函数定义在common/init/board_init.c,其内容为:

    ulong board_init_f_alloc_reserve(ulong top)   //top = 0x0093FF00
    {
    	/* Reserve early malloc arena */
    #if CONFIG_VAL(SYS_MALLOC_F_LEN)
    	top -= CONFIG_VAL(SYS_MALLOC_F_LEN); //top = 0x0093FF00 - 0x2000 = 0x0093DF00
    #endif
    	/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
    	top = rounddown(top-sizeof(struct global_data), 16);
        //top = top - sizeof(struct global_data),16字节对齐
    	//top = 0x0093DF00 - 248 - 8 = 0x0093DE00
    	return top;
    }
    

    board_init_f_alloc_reserve()函数是具有返回值,将top=0x0093DE00返回,最终的sp= 0x0093DE00
    基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f_第1张图片

    补充:

    对于ARM32的CPU,uboot定义了一个指向gd_t类型的gd指针,gd是一个全局变量,存放在r9寄存器,gd_t的类型定义在文件include/asm-generic/global_data.h中,其实就是struct global_data

  • 第109行,将r0赋值给r9,其实就是设置gd指针的值为0x0093DE00,也就是指向在OCRAM中分配的struct global_data结构体的首地址。

  • 第110行,调用board_init_f_init_reserve函数,参数为r0寄存器的值,也就是struct global_data结构体首地址0x0093DE00。函数的内容为:

    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; //①:获取global_data结构体首地址
    	/* zero the area */
    	memset(gd_ptr, '\0', sizeof(*gd));    //②:将global_data结构体清0操作
    	/* set GD unless architecture did it already */
    #if !defined(CONFIG_ARM)
    	arch_setup_gd(gd_ptr);
    #endif
    
    	if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE))
    		board_init_f_init_stack_protection_addr(base);
    
    	/* next alloc will be higher by one GD plus 16-byte alignment */
    	base += roundup(sizeof(struct global_data), 16); 
    	 //③:指向early_alloc的起始地址0x0093DF00
    	/*
    	 * record early malloc arena start.
    	 * Use gd as it is now properly set for all architectures.
    	 */
    
    #if CONFIG_VAL(SYS_MALLOC_F_LEN)
    	/* go down one 'early malloc arena' */
    	gd->malloc_base = base; //④:设置gd->malloc_base指向early malloc首地址
    #endif
    
    	if (CONFIG_IS_ENABLED(SYS_REPORT_STACK_F_USAGE))
    		board_init_f_init_stack_protection();
    }
    

    在上面的代码中可以看出,该函数主要是将OCRAM中分配的struct global_data结构体区域进行清0操作,另外设置gd->malloc_base成员的值为early malloc区域的首地址。

  • 第116-117行,设置r0寄存器的值为0,然后调用board_init_f函数,传入的参数为r0寄存器的值0,下面详细介绍board_init_f函数

2.1.2 board_init_f函数详解

board_init_f函数定义在common/board_f.c文件中,其内容为:

void board_init_f(ulong boot_flags)
{
	gd->flags = boot_flags;
	gd->have_console = 0;

	if (initcall_run_list(init_sequence_f))
		hang();
	...
}

该函数调用后,首先会设置gd->flags这个标志位,然后将gd结构体中的have_console成员变量设置为0,表示此时并没有console。

该函数的重点为initcall_run_list(init_sequence_f),也就是初始化init_sequence_f序列。init_sequence_f包含了一系列的初始化函数,定义同样在common/board_f.c文件中,其内容为:

 810 static const init_fnc_t init_sequence_f[] = {   /* 初始化序列函数数组 */
 811         setup_mon_len,
 812 #ifdef CONFIG_OF_CONTROL
 813         fdtdec_setup,
 814 #endif
 815 #ifdef CONFIG_TRACE_EARLY
 816         trace_early_init,
 817 #endif
 818         initf_malloc,
 819         log_init,
 820         initf_bootstage,        /* uses its own timer, so does not need DM */
 821 #ifdef CONFIG_BLOBLIST
 822         bloblist_init,
 823 #endif
 824         setup_spl_handoff,
 825 #if defined(CONFIG_CONSOLE_RECORD_INIT_F)
 826         console_record_init,
 827 #endif
 828 #if defined(CONFIG_HAVE_FSP)
 829         arch_fsp_init,
 830 #endif
 831         arch_cpu_init,          /* basic arch cpu dependent setup */
 832         mach_cpu_init,          /* SoC/machine dependent CPU setup */
 833         initf_dm,
 834         arch_cpu_init_dm,
 835 #if defined(CONFIG_BOARD_EARLY_INIT_F)
 836         board_early_init_f,
 837 #endif
 838 #if defined(CONFIG_PPC) || defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
 839         /* get CPU and bus clocks according to the environment variable */
 840         get_clocks,             /* get CPU and bus clocks (etc.) */
 841 #endif
 842 #if !defined(CONFIG_M68K)
 843         timer_init,             /* initialize timer */
 844 #endif
 845 #if defined(CONFIG_BOARD_POSTCLK_INIT)
 846         board_postclk_init,
 847 #endif
 848         env_init,               /* initialize environment */
 849         init_baud_rate,         /* initialze baudrate settings */
 850 #ifndef CONFIG_ANDROID_AUTO_SUPPORT
 851         serial_init,            /* serial communications setup */
 852 #endif
 853         console_init_f,         /* stage 1 init of console */
 854         display_options,        /* say that we are here */
 855         display_text_info,      /* show debugging info if required */
 856         checkcpu,
 857 #if defined(CONFIG_SYSRESET)
 858         print_resetinfo,
 859 #endif
 860 #if defined(CONFIG_DISPLAY_CPUINFO)
 861         print_cpuinfo,          /* display cpu info (and speed) */
 862 #endif
 863 #if defined(CONFIG_DTB_RESELECT)
 864         embedded_dtb_select,
 865 #endif
 866 #if defined(CONFIG_DISPLAY_BOARDINFO)
 867         show_board_info,
 868 #endif
 869         INIT_FUNC_WATCHDOG_INIT
 870 #if defined(CONFIG_MISC_INIT_F)
 871         misc_init_f,
 872 #endif
 873         INIT_FUNC_WATCHDOG_RESET
 874 #if defined(CONFIG_SYS_I2C)
 875         init_func_i2c,
 876 #endif
 877 #if defined(CONFIG_VID) && !defined(CONFIG_SPL)
 878         init_func_vid,
 879 #endif
 880         announce_dram_init,
 881         dram_init,              /* configure available RAM banks */
 882 #ifdef CONFIG_POST
 883         post_init_f,
 884 #endif
 885         INIT_FUNC_WATCHDOG_RESET
 886 #if defined(CONFIG_SYS_DRAM_TEST)
 887         testdram,
 888 #endif /* CONFIG_SYS_DRAM_TEST */
 889         INIT_FUNC_WATCHDOG_RESET
 890 
 891 #ifdef CONFIG_POST
 892         init_post,
 893 #endif
 894         INIT_FUNC_WATCHDOG_RESET
 895         /*
 896          * Now that we have DRAM mapped and working, we can
 897          * relocate the code and continue running from DRAM.
 898          *
 899          * Reserve memory at end of RAM for (top down in that order):
 900          *  - area that won't get touched by U-Boot and Linux (optional)
 901          *  - kernel log buffer
 902          *  - protected RAM
 903          *  - LCD framebuffer
 904          *  - monitor code
 905          *  - board info struct
 906          */
 907         setup_dest_addr,
 908 #ifdef CONFIG_OF_BOARD_FIXUP
 909         fix_fdt,
 910 #endif
 911 #ifdef CONFIG_PRAM
 912         reserve_pram,
 913 #endif
 914         reserve_round_4k,
 915         arch_reserve_mmu,
 916         reserve_video,
 917         reserve_trace,
 918         reserve_uboot,
 919         reserve_malloc,
 920         reserve_board,
 921         reserve_global_data,
 922         reserve_fdt,
 923         reserve_bootstage,
 924         reserve_bloblist,
 925         reserve_arch,
 926         reserve_stacks,
 927         dram_init_banksize,
 928         show_dram_config,
 929         INIT_FUNC_WATCHDOG_RESET
 930         setup_bdinfo,
 931         display_new_sp,
 932         INIT_FUNC_WATCHDOG_RESET
 933         reloc_fdt,
 934         reloc_bootstage,
 935         reloc_bloblist,
 936         setup_reloc,
 937 #if defined(CONFIG_X86) || defined(CONFIG_ARC)
 938         copy_uboot_to_ram,
 939         do_elf_reloc_fixups,
 940 #endif
 941         clear_bss,
 942 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
 943                 !CONFIG_IS_ENABLED(X86_64)
 944         jump_to_copy,
 945 #endif
 946         NULL,
 947 };
  • 第811行,setup_mon_len,该函数用来设置gd结构中的mon_len成员变量,大小为__bss_end -_start ,用于保存uboot代码的长度。

  • 第818行,initf_malloc,该函数用来设置gd结构中与malloc相关的变量,定义在common/dlmalloc.c,内容为:

    int initf_malloc(void)
    {
    #if CONFIG_VAL(SYS_MALLOC_F_LEN)
    	assert(gd->malloc_base);	/* Set up by crt0.S */
    	gd->malloc_limit = CONFIG_VAL(SYS_MALLOC_F_LEN);/* malloc的大小0x2000 */
    	gd->malloc_ptr = 0;
    #endif
    
    	return 0;
    }
    
  • 第831-832行,arch_cpu_initmach_cpu_init初始化与cpu相关的内容。

  • 第833行,initf_dm,初始化一些与驱动模型相关的东西。

  • 第836行,board_early_init_f,板子相关的早期的一些初始化设置,对于IMX6Q,初始化uart的IO配置与display相关。定义在board/freescale/mx6sabresd/mx6sabresd.c文件中,其内容为:

    int board_early_init_f(void)
    {
    	setup_iomux_uart();  			//串口初始化
    #if defined(CONFIG_VIDEO_IPUV3)
    	setup_display();				//显示初始化,比如hdmi
    #endif
    
    	return 0;
    }
    
  • 第840行,get_clocks,该函数用于获取芯片内某些外设时钟,赋值给gd->arch.sdhc_clk变量,也就是SD卡时钟。

  • 第843行,timer_init,该函数来初始化ARM内核中的定时器,赋值gd->arch.tblgd->arch.tbu变量。定义在arch/arm/mach-imx/timer.c文件。

  • 第848行,env_init,该函数用来初始化gd结构中和env相关的成员,设置gd结构中的env_addr成员,也就是环境变量的保存地址。定义在env/env.c文件中。

  • 第849行,init_baud_rate,该函数用于初始化debug调试串口的波特率,设置gd结构体中的baudrate成员,该值是从"baudrate"环境变量中读取的。函数内容为:

    static int init_baud_rate(void)
    {
    	gd->baudrate = env_get_ulong("baudrate", 10, CONFIG_BAUDRATE);
    	return 0;
    }
    
  • 第851行,serial_init,该函数用于对串口进行初始化,会调用串口驱动设备中注册的start()函数完成初始化。定义在drivers/serial/serial.c文件中,其内容为:

    int serial_init(void)
    {
    	gd->flags |= GD_FLG_SERIAL_READY;
    	return get_current()->start();
    }
    

    IMX6Q的串口驱动位置在drivers/serial/serial_mxc.c,驱动结构体为:

    static struct serial_device mxc_serial_drv = {
    	.name	= "mxc_serial",
    	.start	= mxc_serial_init,  //被serial_init调用
    	.stop	= NULL,
    	.setbrg	= mxc_serial_setbrg,
    	.putc	= mxc_serial_putc,
    	.puts	= default_serial_puts,
    	.getc	= mxc_serial_getc,
    	.tstc	= mxc_serial_tstc,
    };
    
  • 第853行,console_init_f,该函数将会设置gd结构体的have_console成员为1,标志则此时已经有了console,也就是上面已经初始化好的串口终端,至此可以正常使用串口打印信息。

  • 第854行,display_options,该函数用来打印输出uboot版本的字符串。

  • 第855行,display_text_info,该函数用于显示一些debug调试信息,需要在板子的配置文件中定义宏DEBUG,才会开启uboot的调试信息输出。

  • 第861行,print_cpuinfo,该函数输出板子上的CPU的相关信息。

  • 第867行,show_board_info,该函数用于显示板子的相关信息,若定义了宏CONFIG_DISPLAY_BOARDINFO。

  • 第875行,init_func_i2c,该函数用于初始化I2C接口,内容为:

    static int init_func_i2c(void)
    {
    	puts("I2C:   ");
    	i2c_init_all();
    	puts("ready\n");
    	return 0;
    }
    
  • 第880行,announce_dram_init,该函数只是输出"DRAM: "字符串。

  • 第881行,dram_init,该函数并没有初始化DRAM,只是设置gd结构体中ram_size成员变量,也就是DRAM的大小,ram_size=1G。

  • 第907行,setup_dest_addr,该函数用来设置目的地址,设置gd结构体中的ram_size成员、ram_top成员和relocaddr成员。其中ram_size表示DRAM的大小,ram_top表示DRAM的最终地址,relocaddr表示uboot重定位的地址。其代码内容为:

    static int setup_dest_addr(void)
    {
    	debug("Monitor len: %08lX\n", gd->mon_len);
    	debug("Ram size: %08lX\n", (ulong)gd->ram_size);
        ...
    #ifdef CONFIG_SYS_SDRAM_BASE
    	gd->ram_base = CONFIG_SYS_SDRAM_BASE;  //gd->ram_base = 0x10000000 芯片的DRAM基址
    #endif
    	gd->ram_top = gd->ram_base + get_effective_memsize(); 
        //gd->ram_top = gd->ram_base + gd->ram_size
        //			  = 0x10000000 + 0x40000000 = 0x50000000
    	gd->ram_top = board_get_usable_ram_top(gd->mon_len);
    	gd->relocaddr = gd->ram_top;
        //gd->relocaddr = 0x50000000
    	debug("Ram top: %08lX\n", (ulong)gd->ram_top);
    	...
    	return 0;
    }
    

基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f_第2张图片

  • 第919行,reserve_round_4k,该函数是gd结构体中的relocaddr地址做4KB字节对齐,在setup_dest_addr函数调用后,relocaddr的值被设置为0x50000000,已经是4KB字节对齐了,因此,该函数调用后,值不变,还是0x50000000。

  • 第920行,arch_reserve_mmu,该函数调用arm_reserve_mmu函数,用于留出MMU的TLB表位置,留出该块内存后,对relocaddr地址做64KB字节对齐。函数内容为:

    __weak int arm_reserve_mmu(void)
    {
    #if !(CONFIG_IS_ENABLED(SYS_ICACHE_OFF) && CONFIG_IS_ENABLED(SYS_DCACHE_OFF))
    	/* reserve TLB table */
    	gd->arch.tlb_size = PGTABLE_SIZE;  //gd->arch.tlb_size = 0x4000
    	gd->relocaddr -= gd->arch.tlb_size;
    
    	/* round down to next 64 kB limit */
    	gd->relocaddr &= ~(0x10000 - 1);  //gd->relocaddr = 0x4fff0000
    
    	gd->arch.tlb_addr = gd->relocaddr;
    	debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr,
    	      gd->arch.tlb_addr + gd->arch.tlb_size);
    
    #ifdef CONFIG_SYS_MEM_RESERVE_SECURE
    	/*
    	 * Record allocated tlb_addr in case gd->tlb_addr to be overwritten
    	 * with location within secure ram.
    	 */
    	gd->arch.tlb_allocated = gd->arch.tlb_addr;
    #endif
    #endif
    	return 0;
    }
    

基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f_第3张图片

  • 第916-926行,如上述相同,为保留gd变量的成员,在DRAM中预留空间并赋值,不再绘图。

    函数reserve_uboot用于留出重定位后uboot代码所占用的内存位置,uboot所占用的内存位置空间由gd结构体中mon_len成员变量指定,再4KB字节对齐。赋值gd->relocaddr变量,为uboot重定向地址,gd->relocaddr = 0x4EF78000

    函数reserve_malloc用于留出 malloc 区域 ,大小由TOTAL_MALLOC_LEN决定。

    函数reserve_board用于描述Board的一些信息。

    函数reserve_global_data用于留出gd_t结构体的内存区域,分配的内存大小为248Bytes,分配完成后,并对gd结构体中的new_gd成员变量重新赋值,表示新的gd_t结构体的内存地址。

    函数reserve_fdt用于留出设备树的内存区域。

    函数reserve_stacks用于留出栈区域的内存大小,大小为16Bytes,内存分配后,给gd->irq_sp赋值,即irq预留空间。由文件arch/arm/lib/stack.c函数arch_reserve_stacks实现。

  • 第927行,dram_init_banksize,完成了内存栈的分配的后,该函数设置DRAM的一些配置信息,主要是设置DRAM的起始地址和大小,函数内容为:

    __weak int dram_init_banksize(void)
    {
    	gd->bd->bi_dram[0].start = gd->ram_base;  			 //DRAM起始地址
    	gd->bd->bi_dram[0].size = get_effective_memsize();	 //DRAM大小
    	return 0;
    }
    
  • 第927行,show_dram_config,用于显示DRAM配置信息。该函数调用后,将会将的DRAM的大小以字符串的形式输出,也就是输出"1GB"。

  • 第930行,display_new_sp,内存预留后,该函数显示新的sp指针的值,即gd->start_addr_sp的值,通过打印或计算得知gd->start_addr_sp = 0x4DF68F50。

  • 第932行,reloc_fdt,重定位设备树。函数内容为:

    static int reloc_fdt(void)
    {
    	if (!IS_ENABLED(CONFIG_OF_EMBED)) {
    		if (gd->flags & GD_FLG_SKIP_RELOC)
    			return 0;
    		if (gd->new_fdt) {
    			memcpy(gd->new_fdt, gd->fdt_blob,
    			       fdt_totalsize(gd->fdt_blob));  //将设备树拷贝到DRAM
    			gd->fdt_blob = gd->new_fdt;           //重定位到DRAM
    		}
    	}
    
    	return 0;
    }
    
  • 第935行,setup_reloc,该函数设置gd结构体中一些成员变量,是和重定位相关。函数内容如下:

    static int setup_reloc(void)
    {
    	if (gd->flags & GD_FLG_SKIP_RELOC) {
    		debug("Skipping relocation due to flag\n");
    		return 0;
    	}
    
    #ifdef CONFIG_SYS_TEXT_BASE
    #ifdef ARM
    	gd->reloc_off = gd->relocaddr - (unsigned long)__image_copy_start;
    #elif defined(CONFIG_M68K)
    	/*
    	 * On all ColdFire arch cpu, monitor code starts always
    	 * just after the default vector table location, so at 0x400
    	 */
    	gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400);
    #elif !defined(CONFIG_SANDBOX)
    	gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
    #endif
    #endif
    	memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));
    	return 0;
    }
    

至此board_init_f函数调用完,DRAM内存分配完成,如下图。
基于IMX6Q的uboot启动流程分析(2):_main函数之board_init_f_第4张图片
接下来就是开始uboot的重定位了。

你可能感兴趣的:(uboot,uboot,嵌入式,IMX6Q)