6-uboot relocation介绍

2024-08-30 17:38
文章标签 uboot 介绍 relocation

本文主要是介绍6-uboot relocation介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

[uboot] (番外篇)uboot relocation介绍

以下例子都以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)
  • 1

重点关注“-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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 生成PIE可执行文件 
    在《[uboot] (第四章)uboot流程——uboot编译流程》中,我们知道ld的连接选项如下:
LDFLAGS_u-boot=-pie --gc-sections -Bstatic -Ttext 0x23E00000
  • 1

-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_fvoid board_init_f(ulong boot_flags)
{if (initcall_run_list(init_sequence_f))
// 这里使用了全局变量init_sequence_fhang();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

通过如下命令对编译生成的u-boot

arm-none-linux-gnueabi-objdump -D u-boot > uboot_objdump.txt
  • 1

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中的绝对变量地址进行整理修改。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 各个符号的地址意义

    • 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
logbufferLOGBUFF_RESERVE
pram区域CONFIG_PRAM<<10
round_4k用于4kb对齐
mmu页表区域PGTABLE_SIZE
video buffer不关心。但是是确定的。不会随着代码变化
lcd buffer不关心。但是是确定的。不会随着代码变化
trace bufferCONFIG_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, hereldr 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中断向量表
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

注意上面的注释,从relocate_code返回之后就已经在新的uboot代码空间中运行了。

这里简单地说明一下board_init_f:

static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOXsetup_ram_buf,
#endifsetup_mon_len,
#ifdef CONFIG_OF_CONTROLfdtdec_setup,
#endif
#ifdef CONFIG_TRACEtrace_early_init,
...
}
// 可以看出init_sequence_f是一个函数指针数组void board_init_f(ulong boot_flags)
{if (initcall_run_list(init_sequence_f))
// 在这里会init_sequence_f里面的函数hang();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

(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_PRAMreserve_pram,
#endifreserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \defined(CONFIG_ARM)reserve_mmu,
#endif
#ifdef CONFIG_DM_VIDEOreserve_video,
#else
# ifdef CONFIG_LCDreserve_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_BUILDreserve_malloc,reserve_board,
#endifsetup_machine,reserve_global_data,reserve_fdt,reserve_arch,reserve_stacks,
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

代码里面都是一些简单的减法以及指针的设置。可以参考上述“区域布局”和指针设置自己看一下代码,这里不详细说明。 
这里说明一下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_BASEgd->ram_top = CONFIG_SYS_SDRAM_BASE;
#endifgd->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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

(3)计算uboot代码空间到relocation的位置的偏移 
同样在board_init_f中,调用init_sequence_f数组里面的setup_reloc实现。

static int setup_reloc(void)
{
#ifdef CONFIG_SYS_TEXT_BASEgd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
// gd->relocaddr表示新的uboot代码空间的起始地址,CONFIG_SYS_TEXT_BASE表示旧的uboot代码空间的起始地址,二者算起来就是偏移了。
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(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中
}
  • 1
  • 2
  • 3
  • 4
  • 5

(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
// 把旧代码空间复制到新代码空间中。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

(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 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看出.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, #0xffcmp r1, #23         /* relative fixup? */
// 比较高4字节是否等于0x17bne 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, r4str r1, [r0]
// 根据前面的描述,我们的目的就是要fix label中绝对地址符号的地址,也就是将其修改为新地址空间的地址
// 所以为r1加上偏移之后,重新存储到label中。
// 后面CPU就可以根据LABEL在新uboot的地址空间中寻址到正确的符号。fixnext:cmp r2, r3blo fixloop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

(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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我们知道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, #3223e00030 <_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>:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

所以异常中断向量表就是从偏移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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

经过上述,uboot relocate就完成了。

这篇关于6-uboot relocation介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1121493

相关文章

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

图神经网络模型介绍(1)

我们将图神经网络分为基于谱域的模型和基于空域的模型,并按照发展顺序详解每个类别中的重要模型。 1.1基于谱域的图神经网络         谱域上的图卷积在图学习迈向深度学习的发展历程中起到了关键的作用。本节主要介绍三个具有代表性的谱域图神经网络:谱图卷积网络、切比雪夫网络和图卷积网络。 (1)谱图卷积网络 卷积定理:函数卷积的傅里叶变换是函数傅里叶变换的乘积,即F{f*g}

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G

FreeRTOS-基本介绍和移植STM32

FreeRTOS-基本介绍和STM32移植 一、裸机开发和操作系统开发介绍二、任务调度和任务状态介绍2.1 任务调度2.1.1 抢占式调度2.1.2 时间片调度 2.2 任务状态 三、FreeRTOS源码和移植STM323.1 FreeRTOS源码3.2 FreeRTOS移植STM323.2.1 代码移植3.2.2 时钟中断配置 一、裸机开发和操作系统开发介绍 裸机:前后台系

nginx介绍及常用功能

什么是nginx nginx跟Apache一样,是一个web服务器(网站服务器),通过HTTP协议提供各种网络服务。 Apache:重量级的,不支持高并发的服务器。在Apache上运行数以万计的并发访问,会导致服务器消耗大量内存。操作系统对其进行进程或线程间的切换也消耗了大量的CPU资源,导致HTTP请求的平均响应速度降低。这些都决定了Apache不可能成为高性能WEB服务器  nginx:

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds  总结  fd_set操作接口  timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充  获取新连接 注意点 -- 通信时的调用函数 添加新fd到

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器