SPL ARMv8源代码分析

  • 分析SPL armv8 源代码(以rk3399分析为例)

1.SPL 链接脚本

arch/arm/cpu/armv8/u-boot-spl.lds:

 14 MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE,
 15         LENGTH = IMAGE_MAX_SIZE }
 16 MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,
 17         LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
 18 
 19 OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
 20 OUTPUT_ARCH(aarch64)
 21 ENTRY(_start)                       //spl 入口地址
 22 SECTIONS
 23 {
 24     .text : {
 25         . = ALIGN(8);
 26         *(.__image_copy_start)
 27         CPUDIR/start.o (.text*)    //启动文件
 28         *(.text*)
 29     } >.sram
 30 
 31     .rodata : {
 32         . = ALIGN(8);
 33         *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
 34     } >.sram
 35 
 36     .data : {
 37         . = ALIGN(8);
 38         *(.data*)
 39     } >.sram
 40 
 41     .u_boot_list : {
 42         . = ALIGN(8);
 43         KEEP(*(SORT(.u_boot_list*)));
 44     } >.sram
 45 
 46     .image_copy_end : {
 47         . = ALIGN(8);
 48         *(.__image_copy_end)
 49     } >.sram
 50 
 51     .end : {
 52         . = ALIGN(8);
 53         *(.__end)
 54     } >.sram
 55 
 56     _image_binary_end = .;
 57 
 58     .bss_start (NOLOAD) : {
 59         . = ALIGN(8);
 60         KEEP(*(.__bss_start));
 61     } >.sdram
 62 
 63     .bss (NOLOAD) : {
 64         *(.bss*)
 65          . = ALIGN(8);
 66     } >.sdram
 67 
 68     .bss_end (NOLOAD) : {
 69         KEEP(*(.__bss_end));
 70     } >.sdram
 71 
 72     /DISCARD/ : { *(.dynsym) }
 73     /DISCARD/ : { *(.dynstr*) }
 74     /DISCARD/ : { *(.dynamic*) }
 75     /DISCARD/ : { *(.plt*) }
 76     /DISCARD/ : { *(.interp*) }
 77     /DISCARD/ : { *(.gnu*) }
 78 }  

重点分析:

首先定义两块物理内存区域,分别为
.sram 存放spl 的.text和.data内容,即.text和.data链接地址从CONFIG_SPL_TEXT_SIZE开始。
.sdram存放spl的.bss 内容,即.bss链接地址从CONFIG_SPL_BSS_START_ADDR开始。

1>.SPL Text section 起始地址和大小:

  • IMAGE_TEXT_BASE = CONFIG_SPL_TEXT_SIZE
  • IMAGE_MAX_SIZE = CONFIG_SPL_MAX_SIZE

  执行链接时,指定链接地址为CONFIG_SPL_TEXT_SIZE,空间大小为CONFIG_SPL_MAX_SIZE,宏定义在include/configs/xxx.h:

scripts/Makefile.spl:
152 ifneq ($(CONFIG_$(SPL_TPL_)MAX_SIZE),)
153 LDPPFLAGS += -DIMAGE_MAX_SIZE=$(CONFIG_$(SPL_TPL_)MAX_SIZE)
154 endif
155 ifneq ($(CONFIG_$(SPL_TPL_)TEXT_BASE),)
156 LDPPFLAGS += -DIMAGE_TEXT_BASE=$(CONFIG_$(SPL_TPL_)TEXT_BASE)                                            
157 endif

include/configs/rk3399_common.h :
#define CONFIG_SPL_MAX_SIZE     0x100000

configs/rock960-rk3399_defconfig :
CONFIG_SPL_TEXT_BASE=0xff8c2000

2>.SPL Bss section

  • CONFIG_SPL_BSS_START_ADDR
  • CONFIG_SPL_BSS_MAX_SIZE
   include/configs/rk3399_common.h:
   28 #define CONFIG_SPL_STACK        0xff8effff
   29 #define CONFIG_SPL_MAX_SIZE     0x30000 - 0x2000
   31 #define CONFIG_SPL_BSS_START_ADDR       0xff8e0000                                                     
   32 #define CONFIG_SPL_BSS_MAX_SIZE         0x10000

2.SPL 启动分析

  上面可知,spl的入口地址为_start,汇编文件为start.S。

2.1.start.S( arch/arm/cpu/armv8/start.S )

 18 
 19 .globl  _start
 20 _start:
 21 #if defined(LINUX_KERNEL_IMAGE_HEADER)
 22 #include <asm/boot0-linux-kernel-header.h>
 23 #elif defined(CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK)
 24 /*
 25  * Various SoCs need something special and SoC-specific up front in
 26  * order to boot, allow them to set that in their boot0.h file and then
 27  * use it here.
 28  */
 29 #include <asm/arch/boot0.h>
 30 #else
 31     b   reset                                                                                            
 32 #endif
  • LINUX_KERNEL_IMAGE_HEADER用于控制是否在镜像头部增加linux kernel镜像头。
  • CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK用于控制在SPL头部是否添加soc厂商自定义的一些header(refer to arch/arm/Kconfig);rk3399在boot0.h中预留了16k空间给ATF(arm trusted firmware).

  然后进入reset,执行save_boot_params ,该定义用weak修饰,表示厂商可以重新实现,如果不定义,则执行默认操作。rk3399重新定义了这个函数,实现读取特定寄存器(pmugrf模块)来判断是否需要重新进入bootrom,如果不需要,则返回继续执行 save_boot_params_ret。

  第61-84行:对spl代码进行符号修正,实现位置无关,rk3399没有定义CONFIG_POSITION_INDEPENDENT,因为bootrom会将spl直接load到其链接地址上,即没有必要进行修正,从而加快了启动速度。

 55 reset:  
 56     /* Allow the board to save important registers */
 57     b   save_boot_params  
 58 .globl  save_boot_params_ret
 59 save_boot_params_ret:
 60 
 61 #if CONFIG_POSITION_INDEPENDENT
 66 pie_fixup:
 67     adr x0, _start      /* x0 <- Runtime value of _start */
 68     ldr x1, _TEXT_BASE      /* x1 <- Linked value of _start */
 69     sub x9, x0, x1      /* x9 <- Run-vs-link offset */
 70     adr x2, __rel_dyn_start /* x2 <- Runtime &__rel_dyn_start */
 71     adr x3, __rel_dyn_end   /* x3 <- Runtime &__rel_dyn_end */
 72 pie_fix_loop:
 73     ldp x0, x1, [x2], #16   /* (x0, x1) <- (Link location, fixup) */
 74     ldr x4, [x2], #8        /* x4 <- addend */
 75     cmp w1, #1027       /* relative fixup? */
 76     bne pie_skip_reloc
 77     /* relative fix: store addend plus offset at dest location */
 78     add x0, x0, x9
 79     add x4, x4, x9
 80     str x4, [x0]
 81 pie_skip_reloc:
 82     cmp x2, x3
 83     b.lo    pie_fix_loop                                                                                 
 84 pie_fixup_done:
 85 #endif

  最后执行 bl _main,跳转到arch/arm/lib/crt0_64.S:

 66 ENTRY(_main)
 71 #if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
 72     ldr x0, =(CONFIG_TPL_STACK)
 73 #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
 74     ldr x0, =(CONFIG_SPL_STACK)
 75 #elif defined(CONFIG_SYS_INIT_SP_BSS_OFFSET)
 76     adr x0, __bss_start
 77     add x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET
 78 #else
 79     ldr x0, =(CONFIG_SYS_INIT_SP_ADDR)
 80 #endif
 81     bic sp, x0, #0xf    /* 16-byte alignment for ABI compliance */                                       
 82     mov x0, sp
 83     bl  board_init_f_alloc_reserve
 84     mov sp, x0       //设置栈指针栈底地址
 85     /* set up gd here, outside any C code */
 86     mov x18, x0     //arm64 gd对应的寄存器为x18,arm32为r9
  87     bl  board_init_f_init_reserve
 88 
 89     mov x0, #0
 90     bl  board_init_f
 91 
 92 #if !defined(CONFIG_SPL_BUILD)
 93 /*
 94  * Set up intermediate environment (new sp and gd) and call
 95  * relocate_code(addr_moni). Trick here is that we'll return
 96  * 'here' but relocated.
 97  */
 98     ldr x0, [x18, #GD_START_ADDR_SP]    /* x0 <- gd->start_addr_sp */
 99     bic sp, x0, #0xf    /* 16-byte alignment for ABI compliance */
100     ldr x18, [x18, #GD_NEW_GD]      /* x18 <- gd->new_gd */                                              
101 
102     adr lr, relocation_return
103 #if CONFIG_POSITION_INDEPENDENT
104     /* Add in link-vs-runtime offset */
105     adr x0, _start      /* x0 <- Runtime value of _start */
  • 首先建立c语言运行环境,根据宏定义设置栈指针,第74行设置spl的栈指针,CONFIG_SPL_STACK(指向栈底),即 0xff8effff;第79行设置uboot栈指针,CONFIG_SYS_INIT_SP_ADDR(定义在DRAM空间),因为u-boot运行时,DDR已经被初始化了,并且uboot本身也是运行在ddr中。
  • 第81-82行,将x0低4位清零,表示16字节对齐,然后赋值为栈指针sp。
  • 第83行,为malloc和gd预留空间。如下所示,top存放的是x0,即当前sp的值,对于rk3399,CONFIG_SPL_SYS_MALLOC_F_LEN=0x4000,即16k;然后再预留一个global_data的大小,并对top进行16字节对齐,最后返回top,可以通过x0获得该值。
  45 ulong board_init_f_alloc_reserve(ulong top)                                                            
   46 { 
   47     /* Reserve early malloc arena */
  48 #if CONFIG_VAL(SYS_MALLOC_F_LEN)
   49     top -= CONFIG_VAL(SYS_MALLOC_F_LEN);
   50 #endif
   51     /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
   52     top = rounddown(top-sizeof(struct global_data), 16);
   54     return top;
   55 } 
  • 第84行,设置新的栈指针sp;
  • 第86行,将gd的首地址赋值为x18;

此时spl在SRAM栈的布局如下所示:
在这里插入图片描述

-第87行,在调用 board_init_f_init_reserve时,此时x0保存的是gd的基地址;调用之后,如下图所示:
在这里插入图片描述

-第90行,跳转到 bl board_init_f,该函数是一个weak函数,rk3399重定义该函数,主要实现功能:

  • 初始化uart;
  • 初始化uboot DM,解析device tree;
  • 安全timer的初始化;
  • 初始化pinctrl;
  • 初始化ddr.

  到此,spl的所有子系统基本初始化完毕,DDR也可以使用了。执行完board_init_f返回后(Note:spl和uboot调用的board_init_f完全不同,spl调用的是rk3399-board-spl.c中的,而uboot调用的是common/board_f.c)。

  接着spl 执行 board_init_r函数(common/spl/spl.c):

  • 进行memory的malloc池的初始化
  • timer_init() 初始化时钟
  • spl_board_init() //board 相关初始化
  • boot_from_devices //Try loading a booting U-Boot to ram from a list of devices,example RAM,MMC, NAND;
  • jump_to_image_no_args(&spl_image) // 跳转u-boot入口地址 entry_point,entry_point是由u-boot.img头部信息提供的
  • SPL结束其生命,将控制权交给u-boot/Linux.

至此,spl分析结束。

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值