轉自https://blog.csdn.net/ooonebook/article/details/53047992
以下例子都以project X項目tiny210(s5pv210平台,armv7架構)為例
[uboot] uboot流程系列:
[project X] tiny210(s5pv210)上電啟動流程(BL0-BL2)
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl編譯流程
========================================================================================================
一、relocate介紹
1、uboot的relocate
uboot的relocate動作就是指uboot的重定向動作,也就是将uboot自身鏡像拷貝到ddr上的另外一個位置的動作。
2、uboot為什麼要進行relocate
考慮以下問題
* 在某些情況下,uboot是在某些隻讀存儲器上運作,比如ROM、nor flash等等。需要将這部分代碼拷貝到DDR上才能完整運作uboot。
(當然,如果我們在spl階段就把uboot拷貝到ddr上,就不會有這種情況。但是uboot本身就是要考慮各種可能性)
* 一般會把kernel放在ddr的低端位址上。
考慮到以上情況,uboot的relocation動作會把自己本身relocate到ddr上(前提是在SPL的過程中或者在dram_init中已經對ddr進行初始化了),并且會relocate到ddr的頂端位址使之不會和kernel的沖突。
3、uboot的一些注意事項
- 既然uboot會把自身relocate到ddr的其他位置上,那麼相當于執行位址也會發生變化。也就是要求uboot既要能在relocate正常執行,也要能在relocate之後正常執行。這就涉及到uboot需要使用“位置無關代碼”技術,也就是Position independent code技術。
二、“位置無關代碼”介紹及其原理
1、什麼是“位置無關代碼”
“位置無關代碼”是指無論代碼加載到記憶體上的什麼位址上,都可以被正常運作。也就是當加載位址和連接配接位址不一樣時,CPU也可以通過相對尋址獲得到正确的指令位址。
2、如何生成“位置無關代碼”
(1)生成位置無關代碼分成兩部分
* 首先是編譯源檔案的時候,需要将其編譯成位置無關代碼,主要通過gcc的-fpic選項(也有可能是fPIC,fPIE, mword-relocations選項)
* 其次是連接配接時要将其連接配接成一個完整的位置無關的可執行檔案,主要通過ld的-fpie選項
(2)ARM在如何生成“位置無關代碼”
* 編譯PIC代碼
在《[uboot] (第四章)uboot流程——uboot編譯流程》中,我們知道gcc的編譯選項如下:
c_flags=-Wp,-MD,arch/arm/mach-s5pc1xx/.clock.o.d -nostdinc -isystem /home/disk3/xys/temp/project-x/build/arm-none-linux-gnueabi-4.8/bin/../lib/gcc/arm-none-linux-gnueabi/4.8.3/include -Iinclude -I/home/disk3/xys/temp/project-x/u-boot/include -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/include -include /home/disk3/xys/temp/project-x/u-boot/include/linux/kconfig.h -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/mach-s5pc1xx -Iarch/arm/mach-s5pc1xx -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -Os -fno-stack-protector -fno-delete-null-pointer-checks -g -fstack-usage -Wno-format-nonliteral -D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -mword-relocations -fno-pic -mno-unaligned-access -ffunction-sections -fdata-sections -fno-common -ffixed-r9 -msoft-float -pipe -march=armv7-a -I/home/disk3/xys/temp/project-x/u-boot/arch/arm/mach-s5pc1xx/include -DKBUILD_STR(s)=#s -DKBUILD_BASENAME=KBUILD_STR(clock) -DKBUILD_MODNAME=KBUILD_STR(clock)
重點關注“-mword-relocations -fno-pic”。
由于使用pic時movt / movw指令會寫死16bit的位址域,而uboot的relocation并不支援這個,
是以arm平台使用mword-relocations來生成位置無關代碼。-fno-pic則表示不使用pic。
如下./arch/arm/config.mk
# The movt / movw can hardcode 16 bit parts of the addresses in the
# instruction. Relocation is not supported for that case, so disable
# such usage by requiring word relocations.
PLATFORM_CPPFLAGS += $(call cc-option, -mword-relocations)
PLATFORM_CPPFLAGS += $(call cc-option, -fno-pic)
-
生成PIE可執行檔案
在《[uboot] (第四章)uboot流程——uboot編譯流程》中,我們知道ld的連接配接選項如下:
LDFLAGS_u-boot=-pie --gc-sections -Bstatic -Ttext 0x23E00000
-pie選項用于生成PIE位置無關可執行檔案。
3、“位置無關代碼”原理
這裡隻是個人根據實驗的一些看法。
“位置無關代碼”主要是通過使用一些隻會使用相對位址的指令實作,比如“b”、“bl”、“ldr”、“adr”等等。
對于一些絕對位址符号(例如已經初始化的全局變量),會将其以label的形式放在每個函數的代碼實作的末端。
同時,在連結的過程中,會把這些label的位址統一維護在.rel.dyn段中,當relocation的時候,友善對這些位址的fix。
綜上,個人覺得,既然使用絕對位址,那麼就是說并不是完全的代碼無關,而是說可以通過調整絕對位址符号的label表來實作代碼的搬移。如果不做relocate或者在relocate之前還是需要加載到連接配接位址的位置上,這裡隻是個人看法!!!
個人也挺迷惑的,不知道對不對,這裡希望有知道答案的大神給個意見。
4、.rel.dyn段介紹和使用
前面也說了:
對于一些絕對位址符号(例如已經初始化的全局變量),會将其以label的形式放在每個函數的代碼實作的末端。
同時,在連結的過程中,會把這些label的位址統一維護在.rel.dyn段中,當relocation的時候,友善對這些位址的fix。
這邊簡單的給個例子:
u-boot/common/board_f.c中
static init_fnc_t init_sequence_f[] = {
// 這裡定義了全局變量init_sequence_f
}
void board_init_f(ulong boot_flags)
{
if (initcall_run_list(init_sequence_f))
// 這裡使用了全局變量init_sequence_f
hang();
}
通過如下指令對編譯生成的u-boot
arm-none-linux-gnueabi-objdump -D u-boot > uboot_objdump.txt
board_init_f和init_sequence_f相關的連接配接位址如下:
Disassembly of section .text:
23e08428 <board_init_f>:
23e08438: e59f000c ldr r0, [pc, #12] ; 23e0844c <board_init_f+0x24>
// 通過ldr r0, [pc, #12],相當于是ldr r0,[23e0844c] ,
// 也就是通過後面的label項,獲得了init_sequence_f的位址。
23e0844c: 23e35dcc mvncs r5, #204, 26 ; 0x3300
// 23e0844c: 23e35dcc 是一個label項,23e0844c表示這個label的位址,23e35dcc表示這個label裡面的值,也就是全局變量23e35dcc的位址。
Disassembly of section .data:
23e35dcc <init_sequence_f>:
// 全局變量init_sequence_f的位址在23e35dcc
Disassembly of section .rel.dyn:
23e37b88: 23e0844c mvncs r8, #76, 8 ; 0x4c000000
23e37b8c: 00000017 andeq r0, r0, r7, lsl r0
// 把init_sequence_f的label的位址存在.rel.dyn段中,友善後續relocation的時候,對label中的絕對變量位址進行整理修改。
- 各個符号的位址意義
- 23e08428,是board_init_f的位址
- 23e35dcc,是init_sequence_f的位址
- 23e0844c,是board_init_f為init_sequence_f做的label的位址,是以其值是init_sequence_f的位址,也就是23e35dcc
- 23e37b88,把init_sequence_f的label的位址存放在.rel.dyn段中的這個位置
-
根據上述對全局變量的尋址進行簡單的說明
當board_init_f讀取init_sequence_f時,會通過相對偏移擷取init_sequence_f的label的位址(23e0844c),再從23e0844c中擷取到init_sequence_f的位址(23e35dcc)。
綜上,當uboot對自身進行relocate之後,此時全局變量的絕對位址已經發生變化,如果函數按照原來的label去擷取全局變量的位址的時候,這個位址其實是relocate之前的位址。是以,在relocate的過程中需要對全局變量的label中的位址值進行修改,是以uboot将這些label的位址全部維護在.rel.dyn段中,然後再統一對.rel.dyn段指向的label進行修改。後續代碼可以看出來。
三、uboot relocate代碼介紹
1、uboot relocate位址和布局。
前面已經說明,uboot的relocation動作會把自己本身relocate到ddr上(前提是在SPL的過程中或者在dram_init中已經對ddr進行初始化了),并且會relocate到ddr的頂端位址使之不會和kernel的沖突。
但是relocate過程中,并不是直接把uboot直接放到ddr的頂端位置,而是會有一定的布局,預留一些空間給其他一些需要固定空間的功能使用。
- uboot relocate從高位址到低位址布局如下(并不是所有的區域都是需要的,可以根據宏定義來确定),注意,對應區域的size在這個時候都是确定的,不會發生變化了。
relocate區域 | size |
---|---|
prom頁表區域 | 8192byte |
logbuffer | LOGBUFF_RESERVE |
pram區域 | CONFIG_PRAM<<10 |
round_4k | 用于4kb對齊 |
mmu頁表區域 | PGTABLE_SIZE |
video buffer | 不關心。但是是确定的。不會随着代碼變化 |
lcd buffer | 不關心。但是是确定的。不會随着代碼變化 |
trace buffer | CONFIG_TRACE_BUFFER_SIZE |
uboot代碼區域 | gd->mon_len,并且對齊4KB對齊 |
malloc記憶體池 | TOTAL_MALLOC_LEN |
Board Info區域 | sizeof(bd_t) |
新global_data區域 | sizeof(gd_t) |
fdt區域 | gd->fdt_size |
對齊 | 16b對齊 |
堆棧區域 | 無限制 |
2、relocate代碼流程
主要是分成如下流程
* 對relocate進行空間規劃
* 計算uboot代碼空間到relocation的位置的偏移
* relocate舊的global_data到新的global_data的空間上
* relocate舊的uboot代碼空間到新的空間上去
* 修改relocate之後全局變量的label。(不懂的話參考第二節)
* relocate中斷向量表
(1)首先看一下relocate的整體代碼
去掉無關代碼的代碼如下:
arch/arm/lib/crt0.S
ENTRY(_main)
bl board_init_f
@@ 在board_init_f裡面實作了
@@ (1)對relocate進行空間規劃
@@ (2)計算uboot代碼空間到relocation的位置的偏移
@@ (3)relocate舊的global_data到新的global_data的空間上
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
@@ 把新的global_data位址放在r9寄存器中
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
@@ 計算傳回位址在新的uboot空間中的位址。b調用函數傳回之後,就跳到了新的uboot代碼空間中。
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
@@ 把uboot的新的位址空間放到r0寄存器中,作為relocate_code的參數
b relocate_code
@@ 跳轉到relocate_code中,在這裡面實作了
@@ (1)relocate舊的uboot代碼空間到新的空間上去
@@ (2)修改relocate之後全局變量的label
@@ 注意,由于上述已經把lr寄存器重定義到uboot新的代碼空間中了,是以傳回之後,就已經跳到了新的代碼空間了!!!!!!
bl relocate_vectors
@@ relocate中斷向量表
注意上面的注釋,從relocate_code傳回之後就已經在新的uboot代碼空間中運作了。
這裡簡單地說明一下board_init_f:
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
setup_ram_buf,
#endif
setup_mon_len,
#ifdef CONFIG_OF_CONTROL
fdtdec_setup,
#endif
#ifdef CONFIG_TRACE
trace_early_init,
...
}
// 可以看出init_sequence_f是一個函數指針數組
void board_init_f(ulong boot_flags)
{
if (initcall_run_list(init_sequence_f))
// 在這裡會init_sequence_f裡面的函數
hang();
}
(2)對relocate進行空間規劃
布局已經在上面說過了。
其規劃隻要展現在gd一些指針的設定,如下面所示
——————————————————— <—–(gd->ram_top)
| 最高的區域
———————————————————
| ……
———————————————————
| uboot代碼區域
——————————————————— <—–(gd->relocaddr)
| ……
———————————————————
| Board Info區域
——————————————————— <—–(gd->bd)
| 新global_data區域
——————————————————— <—–(gd->new_gd)
| fdt區域
——————————————————— <—–(gd->new_fdt)
| …..
——————————————————— <—–(gd->start_addr_sp)
| 堆棧區域
———————————————————
在board_init_f中,會依次執行init_sequence_f數組裡面函數。其中,和relocate空間規劃的函數如下:
static init_fnc_t init_sequence_f[] = {
setup_dest_addr,
#if defined(CONFIG_SPARC)
reserve_prom,
#endif
#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)
reserve_logbuffer,
#endif
#ifdef CONFIG_PRAM
reserve_pram,
#endif
reserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
defined(CONFIG_ARM)
reserve_mmu,
#endif
#ifdef CONFIG_DM_VIDEO
reserve_video,
#else
# ifdef CONFIG_LCD
reserve_lcd,
# endif
/* TODO: Why the dependency on CONFIG_8xx? */
# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
reserve_legacy_video,
# endif
#endif /* CONFIG_DM_VIDEO */
reserve_trace,
#if !defined(CONFIG_BLACKFIN)
reserve_uboot,
#endif
#ifndef CONFIG_SPL_BUILD
reserve_malloc,
reserve_board,
#endif
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_arch,
reserve_stacks,
代碼裡面都是一些簡單的減法以及指針的設定。可以參考上述“區域布局”和指針設定自己看一下代碼,這裡不詳細說明。
這裡說明一下setup_dest_addr,也就是一些指針的初始化。
static int setup_dest_addr(void)
{
debug("Monitor len: %08lX\n", gd->mon_len);
// gd->mon_len表示了整個uboot代碼空間的大小,如下
// gd->mon_len = (ulong)&__bss_end - (ulong)_start;
// 在uboot代碼空間relocate的時候,relocate的size就是由這裡決定
debug("Ram size: %08lX\n", (ulong)gd->ram_size);
// gd->ram_size表示了ram的size,也就是可使用的ddr的size,在board.c中定義如下
// int dram_init(void)
// {
// gd->ram_size = PHYS_SDRAM_1_SIZE;也就是0x2000_0000
// return 0;
// }
#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->ram_top計算ddr的頂端位址
// CONFIG_SYS_SDRAM_BASE(0x2000_0000+0x2000_0000=0x4000_0000)
gd->relocaddr = gd->ram_top;
// 從gd->ram_top的位置開始配置設定
debug("Ram top: %08lX\n", (ulong)gd->ram_top);
return 0;
}
(3)計算uboot代碼空間到relocation的位置的偏移
同樣在board_init_f中,調用init_sequence_f數組裡面的setup_reloc實作。
static int setup_reloc(void)
{
#ifdef CONFIG_SYS_TEXT_BASE
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
// gd->relocaddr表示新的uboot代碼空間的起始位址,CONFIG_SYS_TEXT_BASE表示舊的uboot代碼空間的起始位址,二者算起來就是偏移了。
#endif
}
(4)relocate舊的global_data到新的global_data的空間上
同樣在board_init_f中,調用init_sequence_f數組裡面的setup_reloc實作。
static int setup_reloc(void)
{
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));
// 直接把gd的位址空間拷貝到gd->new_gd中
}
(5)relocate舊的uboot代碼空間到新的空間上去
代碼在relocate_code中,上述(1)中可以知道此時的r0是uboot的新的位址空間。
主要目的是把__image_copy_start到__image_copy_end的代碼空間拷貝到新的uboot位址空間中。
關于__image_copy_start和__image_copy_end可以看《[uboot] (第四章)uboot流程——uboot編譯流程》
ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
// 擷取uboot代碼空間的首位址
subs r4, r0, r1 /* r4 <- relocation offset */
// 計算新舊uboot代碼空間的偏移
beq relocate_done /* skip relocation */
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
// 擷取uboot代碼空間的尾位址
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
// 把舊代碼空間複制到新代碼空間中。
(6)修改relocate之後全局變量的label
需要先完全了解第二節““位置無關代碼”介紹及其原理”
主要目的是修改label中的位址。
這裡複習一下:
* 絕對位址符号的位址會放在label中提供位置無關代碼使用
* label的位址會放在.rel.dyn段中
綜上,當uboot對自身進行relocate之後,此時全局變量的絕對位址已經發生變化,如果函數按照原來的label去擷取全局變量的位址的時候,這個位址其實是relocate之前的位址。是以,在relocate的過程中需要對全局變量的label中的位址值進行修改,是以uboot将這些label的位址全部維護在.rel.dyn段中,然後再統一對.rel.dyn段指向的label進行修改。後續代碼可以看出來。
.rel.dyn段部分示例如下:
23e37b88: 23e0844c mvncs r8, #76, 8 ; 0x4c000000
23e37b8c: 00000017 andeq r0, r0, r7, lsl r0
23e37b90: 23e084b4 mvncs r8, #180, 8 ; 0xb4000000
23e37b94: 00000017 andeq r0, r0, r7, lsl r0
23e37b98: 23e084d4 mvncs r8, #212, 8 ; 0xd4000000
23e37b9c: 00000017 andeq r0, r0, r7, lsl r0
23e37ba0: 23e0854c mvncs r8, #76, 10 ; 0x13000000
23e37ba4: 00000017 andeq r0, r0, r7, lsl r0
可以看出.rel.dyn段用了8個位元組來描述一個label,其中,高4位元組是label位址辨別0x17,低4位元組就是label的位址。
是以需要先判斷label位址辨別是否正确,然後再根據第四位元組擷取label,對label中的符号位址進行修改。
代碼如下:
ENTRY(relocate_code)
/*
* fix .rel.dyn relocations
*/
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
// __rel_dyn段是由連結器生成的。
// 把__rel_dyn_start放到r2中,把__rel_dyn_end放到r3中
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
// 從__rel_dyn_start開始,加載兩個位元組到r0和r1中,高位元組存在r1中表示标志,低位元組存在r0中,表示label位址。
and r1, r1, #0xff
cmp r1, #23 /* relative fixup? */
// 比較高4位元組是否等于0x17
bne fixnext
// 不等于的話,說明不是描述label位址,進行下一次循環
// label在relocate uboot的時候也已經複制到了新的uboot位址空間了!!!
// 這裡要注意,是對新的uboot位址空間label進行修改!!!
/* relative fix: increase location by offset */
add r0, r0, r4
// 擷取新的uboot位址空間的label位址,
// 因為r0存的是舊位址空間的label位址,而新位址空間的label位址就是在舊位址空間的label位址加上偏移得到
// r4就是relocate offset,也就是新舊位址空間的偏移
ldr r1, [r0]
// 從label中擷取絕對位址符号的位址,存放在r1中
add r1, r1, r4
str r1, [r0]
// 根據前面的描述,我們的目的就是要fix label中絕對位址符号的位址,也就是将其修改為新位址空間的位址
// 是以為r1加上偏移之後,重新存儲到label中。
// 後面CPU就可以根據LABEL在新uboot的位址空間中尋址到正确的符号。
fixnext:
cmp r2, r3
blo fixloop
(7)relocate中斷向量表
前面在《[uboot] (第四章)uboot流程——uboot編譯流程》中已經分析了,異常中斷向量表的定義如下
arch/arm/lib/vectors.S
.globl _undefined_instruction
.globl _software_interrupt
.globl _prefetch_abort
.globl _data_abort
.globl _not_used
.globl _irq
.globl _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
我們知道arm的異常中斷向量表需要複制到0x00000000處或者0xFFFF0000處(不知道的建議網上度娘一下)。
當uboot進行relocate之後,其異常處理函數的位址也發生了變化,是以,我們需要把新的異常中斷向量表複制到0x00000000處或者0xFFFF0000處。
這部分操作就是在relocate_vectors中進行。
異常中斷向量表在uboot代碼空間中的位址如下:
23e00000 <__image_copy_start>:
23e00000: ea0000be b 23e00300 <reset>
23e00004: e59ff014 ldr pc, [pc, #20] ; 23e00020 <_undefined_instruction>
23e00008: e59ff014 ldr pc, [pc, #20] ; 23e00024 <_software_interrupt>
23e0000c: e59ff014 ldr pc, [pc, #20] ; 23e00028 <_prefetch_abort>
23e00010: e59ff014 ldr pc, [pc, #20] ; 23e0002c <_data_abort>
23e00014: e59ff014 ldr pc, [pc, #20] ; 23e00030 <_not_used>
23e00018: e59ff014 ldr pc, [pc, #20] ; 23e00034 <_irq>
23e0001c: e59ff014 ldr pc, [pc, #20] ; 23e00038 <_fiq>
// 可以看出以下是異常終端向量表
23e00020 <_undefined_instruction>:
23e00020: 23e00060 mvncs r0, #96 ; 0x60
// 其中,23e00020存放的是未定義指令處理函數的位址,也就是23e00060
// 以下以此類推
23e00024 <_software_interrupt>:
23e00024: 23e000c0 mvncs r0, #192 ; 0xc0
23e00028 <_prefetch_abort>:
23e00028: 23e00120 mvncs r0, #8
23e0002c <_data_abort>:
23e0002c: 23e00180 mvncs r0, #32
23e00030 <_not_used>:
23e00030: 23e001e0 mvncs r0, #56 ; 0x38
23e00034 <_irq>:
23e00034: 23e00240 mvncs r0, #4
23e00038 <_fiq>:
23e00038: 23e002a0 mvncs r0, #10
23e0003c: deadbeef cdple 14, 10, cr11, cr13, cr15, {7}
23e00040 <IRQ_STACK_START_IN>:
是以異常中斷向量表就是從偏移0x20開始的32個位元組。
代碼如下(去除掉無關代碼部分):
ENTRY(relocate_vectors)
/*
* Copy the relocated exception vectors to the
* correct address
* CP15 c1 V bit gives us the location of the vectors:
* 0x00000000 or 0xFFFF0000.
*/
@@ 注意看注釋,通過cp15協處理器的c1寄存器的V标志來判斷cpu從什麼位置擷取中斷向量表,
@@ 換句話說,就是中斷向量表應該被複制到什麼地方!!!
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
@@ 擷取uboot新位址空間的起始位址,存放到r0寄存器中
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 */
@@ 擷取cp15協處理器的c1寄存器的V标志,當V=0時,cpu從0x00000000擷取中斷向量表,當V=1時,cpu從0xFFFF0000擷取中斷向量表
@@ 将該位址存在r1中
ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
@@ 前面說了異常中斷向量表就是從偏移0x20開始的32個位元組。
@@ 是以這裡是過濾掉前面的0x20個位元組(32個位元組,8*4)
@@ 但是不明白為什麼還要stmia r1!, {r2-r8,r10},理論上隻需要讓r0的值産生0x20的偏移就可以了才對???不明白。
@@ 經過上述兩行代碼之後,此時r0的值已經偏移了0x20了
ldmia r0!, {r2-r8,r10}
stmia r1!, {r2-r8,r10}
@@ 繼續從0x20開始,擷取32個位元組,存儲到r1指向的位址,也就是cpu擷取中斷向量表的位址
@@ r2-r8,r10表示從r2到r8寄存器和r10寄存器,一個8個寄存器,每個寄存器有4個位元組,是以就從r0指向的位址處擷取到了32個位元組
@@ 再把 {r2-r8,r10}的值存放到r1指向的位址,也就是cpu擷取中斷向量表的位址
bx lr
@@ 傳回
ENDPROC(relocate_vectors)
經過上述,uboot relocate就完成了。