uboot源碼分析八 uboot啓動流程五 relocate_code 函數

relocate_code 函數

relocate_code 函數是用於代碼拷貝的 此函數定義在文件 arch/arm/lib/relocate.S 中

/*
 *  relocate - common relocation function for ARM U-Boot
 *
 *  Copyright (c) 2013  Albert ARIBAUD <[email protected]>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */

#include <asm-offsets.h>
#include <config.h>
#include <linux/linkage.h>
#ifdef CONFIG_CPU_V7M
#include <asm/armv7m.h>
#endif

/*
 * Default/weak exception vectors relocation routine
 *
 * This routine covers the standard ARM cases: normal (0x00000000),
 * high (0xffff0000) and VBAR. SoCs which do not comply with any of
 * the standard cases must provide their own, strong, version.
 */

	.section	.text.relocate_vectors,"ax",%progbits
	.weak		relocate_vectors

ENTRY(relocate_vectors)

#ifdef CONFIG_CPU_V7M
	/*
	 * On ARMv7-M we only have to write the new vector address
	 * to VTOR register.
	 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	ldr	r1, =V7M_SCB_BASE
	str	r0, [r1, V7M_SCB_VTOR]
#else
#ifdef CONFIG_HAS_VBAR
	/*
	 * If the ARM processor has the security extensions,
	 * use VBAR to relocate the exception vectors.
	 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */
#else
	/*
	 * Copy the relocated exception vectors to the
	 * correct address
	 * CP15 c1 V bit gives us the location of the vectors:
	 * 0x00000000 or 0xFFFF0000.
	 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	mrc	p15, 0, r2, c1, c0, 0	/* V bit (bit[13]) in CP15 c1 */
	ands	r2, r2, #(1 << 13)
	ldreq	r1, =0x00000000		/* If V=0 */
	ldrne	r1, =0xFFFF0000		/* If V=1 */
	ldmia	r0!, {r2-r8,r10}
	stmia	r1!, {r2-r8,r10}
	ldmia	r0!, {r2-r8,r10}
	stmia	r1!, {r2-r8,r10}
#endif
#endif
	bx	lr

ENDPROC(relocate_vectors)

/*
 * void relocate_code(addr_moni)
 *
 * This function relocates the monitor code.
 *
 * NOTE:
 * To prevent the code below from containing references with an R_ARM_ABS32
 * relocation record type, we never refer to linker-defined symbols directly.
 * Instead, we declare literals which contain their relative location with
 * respect to relocate_code, and at run time, add relocate_code back to them.
 */

ENTRY(relocate_code)
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
	subs	r4, r0, r1		/* r4 <- relocation offset */
	beq	relocate_done		/* skip relocation */
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	cmp	r1, r2			/* until source end address [r2]    */
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end	/* r3 <- SRC &__rel_dyn_end */
fixloop:
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	and	r1, r1, #0xff
	cmp	r1, #23			/* relative fixup? */
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4
	ldr	r1, [r0]
	add	r1, r1, r4
	str	r1, [r0]
fixnext:
	cmp	r2, r3
	blo	fixloop

relocate_done:

#ifdef __XSCALE__
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr
#endif

ENDPROC(relocate_code)

第 80 行, r1=__image_copy_start,也就是 r1 寄存器保存源地址,__image_copy_start=0X87800000。
第 81 行, r0=0X9FF47000,這個地址就是 uboot 拷貝的目標首地址。 r4=r0-r1=0X9FF47000-0X87800000=0X18747000,因此 r4 保存偏移量。
第 82 行,如果在第 81 中, r0-r1 等於 0,說明 r0 和 r1 相等,也就是源地址和目的地址是一樣的,那肯定就不需要拷貝了!執行 relocate_done 函數
第 83 行, r2=__image_copy_end, r2 中保存拷貝之前的代碼結束地址,__image_copy_end =0x8785dd54。
第 84 行,函數 copy_loop 完成代碼拷貝工作!從 r1,也就是__image_copy_start 開始,讀取 uboot 代碼保存到 r10 和 r11 中,一次就只拷貝這 2 個 32 位的數據。拷貝完成以後 r1 的值會更新,保存下一個要拷貝的數據地址。
第 87 行,將 r10 和 r11 的數據寫到 r0 開始的地方,也就是目的地址。寫完以後 r0 的值會更新,更新爲下一個要寫入的數據地址。
第 88 行,比較 r1 是否和 r2 相等,也就是檢查是否拷貝完成,如果不相等的話說明沒有拷貝完成, 沒有拷貝完成的話就跳轉到 copy_loop 接着拷貝,直至拷貝完成。接下來的第 94 行~109 行是重定位.rel.dyn 段, .rel.dyn 段是存放.text 段中需要重定位地址的集合。重定位就是 uboot 將自身拷貝到 DRAM 的另一個地放去繼續運行(DRAM 的高地址處)。我們知道,一個可執行的 bin 文件,其鏈接地址和運行地址要相等,也就是鏈接到哪個地址,在運行之前就要拷貝到哪個地址去。現在我們重定位以後,運行地址就和鏈接地址不同了,這樣尋址的時候不會出問題嗎?

①、在函數 rel_test 末尾處有一個地址爲 0X87804198 的內存空間(示例代碼 32.2.6.3 第 7行),此內存空間保存着變量 rel_a 的地址。
②、函數 rel_test 要想訪問變量 rel_a,首先訪問末尾的 0X87804198 來獲取變量 rel_a 的地址,而訪問 0X87804198 是通過偏移來訪問的,很明顯是個位置無關的操作。
③、通過 0X87804198 獲取到變量 rel_a 的地址,對變量 rel_a 進行操作。
④、可以看出,函數 rel_test 對變量 rel_a 的訪問沒有直接進行,而是使用了一個第三方偏移地址 0X87804198,專業術語叫做 Label。這個第三方偏移地址就是實現重定位後運行不會出錯的重要原因!
uboot 重 定 位 後 偏 移 爲 0X18747000 , 那 麼 重 定 位 後 函 數 rel_test 的 首 地 址 就 是0X87804184+0X18747000=0X9FF4B184 。 保 存 變 量 rel_a 地 址 的 Label 就 是0X9FF4B184+8+12=0X9FF4B198( 既 : 0X87804198+0X18747000) , 變 量 rel_a 的 地 址 就 爲0X8785DA50+0X18747000=0X9FFA4A50。重定位後函數 rel_test 要想正常訪問變量 rel_a 就得設置 0X9FF4B198(重定位後的 Label)地址出的值爲 0X9FFA4A50(重定位後的變量 rel_a 地址)。這樣就解決了重定位後鏈接地址和運行地址不一致的問題。
可以看出, uboot 對於重定位後鏈接地址和運行地址不一致的解決方法就是採用位置無關碼,在使用 ld 進行鏈接的時候使用選項“ -pie”生成位置無關的可執行文件。

第 94 行, r2=__rel_dyn_start,也就是.rel.dyn 段的起始地址。
第 95 行, r3=__rel_dyn_end,也就是.rel.dyn 段的終止地址。
第 97 行,從.rel.dyn 段起始地址開始,每次讀取兩個 4 字節的數據存放到 r0 和 r1 寄存器中, r0 存放低 4 字節的數據,也就是 Label 地址; r1 存放高 4 字節的數據,也就是 Label 標誌。
第 98 行, r1 中給的值與 0xff 進行與運算,其實就是取 r1 的低 8 位。
第 99 行,判斷 r1 中的值是否等於 23(0X17)。
第 100 行,如果 r1 不等於 23 的話就說明不是描述 Label 的,執行函數 fixnext,否則的話繼續執行下面的代碼。
第 103 行, r0 保存着 Label 值, r4 保存着重定位後的地址偏移, r0+r4 就得到了重定位後的Label 值。此時 r0 保存着重定位後的 Label 值,相當於 0X87804198+0X18747000=0X9FF4B198。
第 104,讀取重定位後 Label 所保存的變量地址,此時這個變量地址還是重定位前的(相當於 rel_a 重定位前的地址 0X8785DA50),將得到的值放到 r1 寄存器中。
第 105 行 , r1+r4 即 可 得 到 重 定 位 後 的 變 量 地 址 , 相 當 於 rel_a 重 定 位 後 的0X8785DA50+0X18747000=0X9FFA4A50。
第 106 行,重定位後的變量地址寫入到重定位後的 Label 中,相等於設置地址 0X9FF4B198處的值爲 0X9FFA4A50。
第 108 行,比較 r2 和 r3,查看.rel.dyn 段重定位是否完成。
第 109 行,如果 r2 和 r3 不相等,說明.rel.dyn 重定位還未完成,因此跳到 fixloop 繼續重定位.rel.dyn 段。

relocate_vectors 函數(設置vbar寄存器爲重定位後的向量表偏移)

第 29 行,如果定義了 CONFIG_CPU_V7M 的話就執行第 30~36 行的代碼,這是 Cortex-M內核單片機執行的語句,因此對於 I.MX6ULL 來說是無效的。
第 38 行,如果定義了 CONFIG_HAS_VBAR 的話就執行此語句,這個是向量表偏移, CortexA7 是支持向量表偏移的。而且,在.config 裏面定義了 CONFIG_HAS_VBAR,因此會執行這個分支。
第 43 行, r0=gd->relocaddr,也就是重定位後 uboot 的首地址,向量表肯定是從這個地址開始存放的。
第 44 行,將 r0 的值寫入到 CP15 的 VBAR 寄存器中,也就是將新的向量表首地址寫入到寄存器 VBAR 中,設置向量表偏移。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章