reset: bl save_boot_params /*lowlevel_init.S (arch/arm/cpu/armv7/omap-common)*/ /* * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, * except if in HYP mode already */ mrs r0, cpsr and r1, r0, #0x1f @ mask mode bits teq r1, #0x1a @ test for HYP mode bicne r0, r0, #0x1f @ clear all mode bits orrne r0, r0, #0x13 @ set SVC mode orr r0, r0, #0xc0 @ disable FIQ and IRQ msr cpsr,r0/* * Setup vector: * (OMAP4 spl TEXT_BASE is not 32 byte aligned. * Continue to use ROM code vector only in OMAP4 spl) */#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD)) /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */ mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register bic r0, #CR_V @ V = 0 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register /* Set vector address in CP15 VBAR register */ ldr r0, =_start mcr p15, 0, r0, c12, c0, 0 @Set VBAR#endif /* the mask ROM code should have PLL and others stable */#ifndef CONFIG_SKip_LOWLEVEL_INIT/*this branch will only work in SPL*/ bl cpu_init_cp15 /*wlg: find out in this file, we do not explain in detial*/ bl cpu_init_crit /*wlg: find out in this file, please jump*/#endif bl _main /*wlg: jump to arch/arm/lib/crt0.s*/ 沒毛病,上面的代碼和這個博客(1)中是一樣的,至少看起來是一樣的,它主要執行了以下功能: 1. bl save_boot_params 這是第一個不同于SPL階段的代碼,之前save_boot_params保存了r0寄存器中的數據到SRAM中,在uboot中,它實際上只是:ENTRY(save_boot_params) bx lr @ back to my callerENDPROC(save_boot_params) .weak save_boot_params 可以看到,此時的r0早就已經被SPL用了很多次,上次已經不再保存啟動信息了,而且這時候啟動信息也已經不再重要了,因為我們已經正在執行uboot了,所以上面的代碼只是做了簡單的返回。 2. 請注意,這個時候CONFIG_SPL_BUILD不再被定義,所以后面的很多條件編譯請自覺忽略。關閉了中斷,設置了SVC后 3. 這個時候我們會定義CONFIG_SKIP_LOWLEVEL_INIT,所以其后面的cpu_init_cp15等代碼實際上不會被執行,而是直接來到了_main, arch/arm/lib/crt0.s 接下來繼續看_main代碼,實際上也是和SPL一樣的,只是部分條件編譯不一樣,如下:ENTRY(_main)/* * Set up initial C runtime environment and call board_init_f(0). */#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK)/* wlg: in spl, it seems be 0x40310000-sizeof(global_data)*/#else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)/* wlg: in uboot, it will be ?*/#endif bic sp, sp, #7 /* 8-byte alignment for ABI compliance | SPL | uboot |*/ mov r2, sp /* wlg: we record the end of address of the initial |sp is useful, r9 (gdata) will |sp is useful, a new temp |*/ sub sp, sp, #GD_SIZE /* allocate one GD above SP |be redefined in board_init_f |GD(pointed by r9) will be |*/ bic sp, sp, #7 /* 8-byte alignment for ABI compliance |so next text is only to clear | set above on sp, gdata |*/ mov r9, sp /* GD is above SP |a invalid memory |be discarded |*/ mov r1, sp /* wlg: we record the start address of the initial*/ mov r0, #0 /*wlg : the num of initialition*/clr_gd: cmp r1, r2 /* while not at end of GD */ strlo r0, [r1] /* clear 32-bit GD Word */ /*wlg: ro >> [r1]*/ addlo r1, r1, #4 /* move to next */ blo clr_gd#if defined(CONFIG_SYS_MALLOC_F_LEN) && !defined(CONFIG_SPL_BUILD) sub sp, sp, #CONFIG_SYS_MALLOC_F_LEN str sp, [r9, #GD_MALLOC_BASE]#endif /* mov r0, #0 not needed due to above code */ bl board_init_f /*wlg: SPL: board_init_f - Function in spl.c (arch/arm/lib) , and it will not return, it will jump to uboot's start's*/ /*wlg: Uboot: board_init_f - Function in Board.c (arch/arm/lib) at line 263 (199 lines), it will return*/ 上述代碼主要完成了: 1. 設置一個臨時的sp,為后面的C語言函數調用做準備 2. 在這個臨時的sp上分配出一部分空間專門用來保存全局變量,并把這部分空間清0,并將r9指向這個全局變量!請注意,這里的全局變量已經不同于SPL階段的gdata(全局變量),這里的全局變量是一個臨時的用來保存關鍵數據的。所以請記住,在uboot的前期所用到的全局變量(會用gd指針表示)實際上指的就是建立在SRAM上的這部分空間,而且已經初始化為0,不再繼承SPL階段的全局變量(SPL階段的全局變量雖然也是保存在SRAM中,但是是預先定義好的.data中) 3. 定義一個malloc空間,并將malloc空間的地址賦值給最新的臨時全局變量(因為r9所指就是全局變量的開頭,其加上一個偏移量后就是gd->malloc_base) 4. 萬事俱備(有了臨時sp和全局變量,為什么說是臨時的呢?因為這兩個玩意都還建立在SRAM上,而我們cpu目前試運行在SDRAM上的,我們最終希望sp和全局變量都是指向SDRAM的?。?,那么接下來就可以轉跳到從語言函數board_init_f,他的定義位置和SPL不同,在Board.c (arch/arm/lib)C語言部分
這部分代碼很長,我們分幾次將其貼上void board_init_f(ulong bootflag){ bd_t *bd; init_fnc_t **init_fnc_ptr; gd_t *id; ulong addr, addr_sp;#ifdef CONFIG_PRAM ulong reg;#endif void *new_fdt = NULL; size_t fdt_size = 0; memset((void *)gd, 0, sizeof(gd_t));//DECLARE_GLOBAL_DATA_PTR make gd >> r9, //wlg: all we now used gd is a point which saved in r9, so it is point to SRAM gd->mon_len = (ulong)&__bss_end - (ulong)_start;//wlg: it will be the sum size of uboot, it should be made at ld?#ifdef CONFIG_OF_EMBED /* Get a pointer to the FDT */ gd->fdt_blob = __dtb_dt_begin;#elif defined CONFIG_OF_SEPARATE /* FDT is at end of image */ gd->fdt_blob = &_end;#endif /* Allow the early environment to override the fdt address */ gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16, (uintptr_t)gd->fdt_blob); //initial function sequence as defined before, containing serial_init() for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { if ((*init_fnc_ptr)() != 0) { hang (); } }#ifdef CONFIG_OF_CONTROL /* For now, put this check after the console is ready */ if (fdtdec_prepare_fdt()) { panic("** CONFIG_OF_CONTROL defined but no FDT - please see " "doc/README.fdt-control"); }#endif debug("monitor len: %08lX/n", gd->mon_len); /* * Ram is setup, size stored in gd !! */ debug("ramsize: %08lX/n", gd->ram_size); 這部分代碼的主要工作是對對全局變量進行賦值,也就是上文gd所指向的建立在SRAM上的臨時全局變量;然后利用函數指針進行各種初始化,展示如下:init_fnc_t *init_sequence[] = { arch_cpu_init, /* basic arch cpu dependent setup */ mark_bootstage,#ifdef CONFIG_OF_CONTROL fdtdec_check_fdt,#endif#if defined(CONFIG_BOARD_EARLY_INIT_F) board_early_init_f,#endif timer_init, /* initialize timer */#ifdef CONFIG_BOARD_POSTCLK_INIT board_postclk_init,#endif#ifdef CONFIG_FSL_ESDHC get_clocks,#endif env_init, /* initialize environment */ init_baudrate, /* initialze baudrate settings */ serial_init, /* serial communications setup */ //wlg: at very first of SPL, we have no serial, so we get the default serial as cerrent console_init_f, /* stage 1 init of console */ //wlg: while in boot, display_banner, /* say that we are here */ //wlg: now we have the first information printed out: U-Boot 2014.10... print_cpuinfo, /* display cpu info (and speed) */#if defined(CONFIG_DISPLAY_BOARDINFO) checkboard, /* display board info */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C) init_func_i2c,#endif dram_init, /* configure available RAM banks */// wlg: make initialing to gd->ram-size NULL,}; 將上面的函數指針數組中的元素所指向的函數全部執行一遍,每一個函數都比較簡單,其作用在注釋中都有所記錄。初始化過程也會對上述全局變量gd中元素做修改,初始化過程也大量用到了環境變量,這些默認的環境變量在編譯時就已經確定,下次專門開一篇來介紹環境變量!addr = CONFIG_SYS_SDRAM_BASE + get_effective_memsize(); //wlg: now, we could divide the menory to parts as follow#ifdef CONFIG_LOGBUFFER#ifndef CONFIG_ALT_LB_ADDR /* reserve kernel log buffer */ addr -= (LOGBUFF_RESERVE); debug("Reserving %dk for kernel logbuffer at %08lx/n", LOGBUFF_LEN, addr);#endif#endif#ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lx/n", reg, addr);#endif /* CONFIG_PRAM */#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ gd->arch.tlb_size = PGTABLE_SIZE;//wlg: tlb will be 64KB addr -= gd->arch.tlb_size; /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->arch.tlb_addr = addr;//wlg: record the addr in global_data debug("TLB table from %08lx to %08lx/n", addr, addr + gd->arch.tlb_size);#endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lx/n", addr);#ifdef CONFIG_LCD#ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR;#else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr;#endif /* CONFIG_FB_ADDR */#endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lx/n", gd->mon_len >> 10, addr);////////////////////////////the different between SPL and uboot ///////////////////////////////#ifndef CONFIG_SPL_BUILD//////////////wlg: in this branch, we put the global_data and board_data into SDRAM /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lx/n", TOTAL_MALLOC_LEN >> 10, addr_sp); /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lx/n", sizeof (bd_t), addr_sp);#ifdef CONFIG_MACH_TYPE gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */#endif addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lx/n", sizeof (gd_t), addr_sp);#if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL) /* * If the device tree is sitting immediate above our image then we * must relocate it. If it is embedded in the data section, then it * will be relocated with other data. */ if (gd->fdt_blob) { fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32); addr_sp -= fdt_size; new_fdt = (void *)addr_sp; debug("Reserving %zu Bytes for FDT at: %08lx/n", fdt_size, addr_sp); }#endif#ifndef CONFIG_ARM64 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp;#ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lx/n", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);#endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07;#else /* CONFIG_ARM64 */ /* 16-byte alignment for ABI compliance */ addr_sp &= ~0x0f;#endif /* CONFIG_ARM64 */ 上面的代碼完成了SDRAM高位地址的劃分,從SDRAM頂上往下依次是: a. TLB,放置TLB。請注意這里只是劃分出這么個空間作為TLB的區域,里面并沒有數據! b.FB LCD,放置frame buffer 緩沖(一般沒有) c. Uboot .text .data .bss,放置完整的uboot代碼段和bss數據。請注意這里只是劃分出這么個空間作為uboot的區域,里面并沒有數據! d. malloc 放置molloc空間。請注意這里只是劃分出這么個空間作為malloc的區域,里面并沒有數據! e. bd 放置board data 結構體。請注意這里只是劃分出這么個空間作為bd的區域,里面并沒有數據! f. gd 放置 global data結構體,這個gd結構體才是真正在后期uboot要用的全局變量。請注意這里只是劃分出這么個空間作為gd的區域,里面并沒有數據! g. IRQ stack。請注意這里只是劃分出這么個空間作為IRQ的區域,里面并沒有數據! h. sp,這個sp才是后期uboot要用的堆棧。請注意這里只是劃分出這么個空間作為sp的區域,里面并沒有數據! 請注意,上述代碼只是簡單的劃分了SDRAM的內存區域,比如說uboot區域,實際上當前正在執行的就是uboot程序,只是當前代碼目前保存在SDRAM的低位,上述的uboot區是在SDRAM的高端,后期我們會將低位的uboot復制到高位的uboot區,再執行重定位,然后代碼就可以安全的在高位執行了! 在完后看gd->relocaddr = addr; //wlg: the uboot start here gd->start_addr_sp = addr_sp; //wlg: the stack is here gd->reloc_off = addr - (ulong)&_start; //wlg: record the offset, we will relocation. debug("relocation Offset is: %08lx/n", gd->reloc_off); if (new_fdt) { memcpy(new_fdt, gd->fdt_blob, fdt_size); gd->fdt_blob = new_fdt; } memcpy(id, (void *)gd, sizeof(gd_t)); //wlg: copy the temp global_data the the static global_data in SDRAM 劃分完SDRAM后,需要將關鍵參數保存到gd中,此時的gd仍是SRAM中的臨時全局變量,最后那一句,就將在SRAM中的全局變量,復制到了處在SDRAM中的全局變量中。也就是說現在的全局變量有兩個備份,一個在SRAM中,另一個在SDRAM中!-------------------------------返回到_main------------------------ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ /*wlg: because r9 point to SRAM and keep gd*/ bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ /*wlg: and also we have a copy in SDRAM*/ ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ /*wlg: we should change r9 to point to SDRAM*/ sub r9, r9, #GD_SIZE /* new GD is below bd */ /*wlg: <<<<<change r9<<<<<<<<<<<<<*/ /*wlg: also, we change sp to point to SDRAM*/ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ /*wlg: now r9 is point to SDRAM*/ add lr, lr, r0 /*wlg: lr will be the here function which run in SDRAM*/ ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ /*wlg: so when we return, the program will run on SDRAM*/ b relocate_code /*the function is locate in relocate.S (arch/arm/lib)*/here: 這段代碼在注視中解釋的很清晰了,其作用就是: a. 更新r9,讓其指向SDRAM中的全局變量!因為以前面已經完成了SRAM到SDRAM的全局變量復制 i.第一句中的r9還是指向SRAM,其作用就是將SRAM中的全局變量中的start_addr_sp賦值給sp,這樣就完成了sp指向SDRAM中正確位置的工作!后做對齊 ii.第三句的r9還是指向SRAM,其作用就是將SRAM中的全局變量中的bd賦值給r9 iii.將此時的r9減去GD_SIZE的(即全局變量的size)后,再賦值給r9,此時的r9已經正確的指向了SDRAM中的全局變量(而且已經將SRAM中的相應數據復制進去) b. 將此時的here標號的地址賦值給lr,將r0賦值為重定位的偏移地址r0 = gd->reloc_off,將lr加上這個r0,這樣就相當于完成了here(lr)的重定位! c.將r0賦值為gd->relocaddr,這個地址就是在SDRAM中重新分配的uboot的起始地址(這個uboot區域目前還沒有數據) d. 跳到relocate_code進行重定位!注意這里不是用bl,所以實際上沒有記錄返回地址。前面我們已經將here的重定位地址賦給了lr,所以relocate_code返回的話,就會返回到重定位后的SDRAM中區執行,也就是在上述SDRAM中的uboot段中區執行。 注意:uboot原本也是運行在SDRAM中,但是從SDRAM的地址分配來看,我們無法提前預知uboot的最佳運行位置。所以uboot的前期都是在SDRAM的低位運行的,而且前期的代碼都是位置無關的,所以執行起來沒有問題。直到重定位完成以后,才將uboot搬移到SDRAM中的uboot區(人為劃分的,實際運行的最佳位置),這個時候開始,uboot才開始復雜的功能!所以接下來的重定位非常的關鍵。 可看之前的博客-講解重定位的那篇
新聞熱點
疑難解答