Linux启动lumerical服务,u-boot启动流程分析(2)_板级(board)部分

u-boot启动流程分析(2)_板级(board)部分

作者:wowo 发布于:2016-6-7 22:06

分类:u-boot分析

1. 前言

书接上文(u-boot启动流程分析(1)_平台相关部分),本文介绍u-boot启动流程中和具体版型(board)有关的部分,也即board_init_f/board_init_r所代表的、board有关初始化过程。该过程将持续u-boot的整个生命周期,直到main_loop(即传说中的命令行)。

注1:由于u-boot后初始化过程,基本上涉及到了所有的软件模块,因此本文不能一一分析,基本原则就是捡看着顺眼的、熟的下手了~。

2. Generic Board

我们在“u-boot启动流程分析(1)_平台相关部分”中,介绍过board_init_f接口,并在“X-003-UBOOT-基于Bubblegum-96平台的u-boot移植说明”中,通过在SPL image中的board_init_f点亮了一个LED灯。

u-boot的基本策略,就是声明一系列的API(如low_level_init、board_init_f、board_init_r等等),并在u-boot的核心逻辑中调用它们。平台的移植和开发者,所需要做的,就是根据实际情况,实现它们。

与此同时,为了减少开发的工作量,u-boot为大部分API提供了通用实现(一般通过CONFIG配置项或者若定义去控制是否编译)。以board_init_f和board_init_r两个板级的初始化接口为例,u-boot分别在common/board_f.c和common/board_r.c两个文件中提供了通用实现。查看common/Makefile可知: ifndef CONFIG_SPL_BUILD

# boards

obj-$(CONFIG_SYS_GENERIC_BOARD) += board_f.o

obj-$(CONFIG_SYS_GENERIC_BOARD) += board_r.o

endif # !CONFIG_SPL_BUILD

这两个通用接口是由CONFIG_SYS_GENERIC_BOARD配置项控制的,并且只会在u-boot image中被编译。再通过arch/Kconfig中ARM平台有关的配置可知: config ARM

bool "ARM architecture"

select CREATE_ARCH_SYMLINK

select HAVE_PRIVATE_LIBGCC if !ARM64

select HAVE_GENERIC_BOARD

select SYS_GENERIC_BOARD

select SUPPORT_OF_CONTROL

ARM平台自动使能了CONFIG_SYS_GENERIC_BOARD配置,因此u-boot image有关的板级启动流程,是由Generic Board的代码实现的,具体可参考后续的分析。

注2:由“u-boot启动流程分析(1)_平台相关部分”中的分析可知,SPL image的声明周期,在自定义的board_init_f执行完成后,便结束了,因此本文将只针对u-boot image。

3. _main

接着“u-boot启动流程分析(1)_平台相关部分”的表述,我们从_main函数重新分析(只不过此时不再是SPL build,代码不再贴出)。执行过程如下:

1)设置初始的堆栈

基址由CONFIG_SYS_INIT_SP_ADDR定义。

2)分配global data所需的空间

将堆栈16 bits对齐之后,调用board_init_f_alloc_reserve接口,从堆栈开始的地方,为u-boot的global data(struct global_data)分配空间。如下: /* common/init/board_init.c */

ulong board_init_f_alloc_reserve(ulong top)

{

/* Reserve early malloc arena */

#if defined(CONFIG_SYS_MALLOC_F)

top -= CONFIG_SYS_MALLOC_F_LEN;

#endif

/* LAST : reserve GD (rounded up to a multiple of 16 bytes) */

top = rounddown(top-sizeof(struct global_data), 16);

return top;

}

需要注意的是,如果定义了CONFIG_SYS_MALLOC_F_LEN,则会先预留出early malloc所需的空间。

3)初始化global data

global data的空间分配后,调用board_init_f_init_reserve,初始化global data。所谓的初始化,无非就是一些清零操作,不过有几个地方需要注意: 1)如果不是ARM平台(!CONFIG_ARM),则可以调用arch_setup_gd接口,进行arch级别的设置。当然,前提是,对应的arch应该实现这个接口。

2)如果定义了CONFIG_SYS_MALLOC_F,则会初始化gd->malloc_base。

4)执行前置的(front)初始化操作

调用board_init_f接口,执行前置的初始化操作,会再后面的章节详细说明。

5)执行relocation操作,后面会详细说明。

6)清除BBS段

7)执行后置的(rear)初始化操作

调用board_init_r接口,执行前置的初始化操作,会再后面的章节详细说明。

4. global data介绍以及背后的思考

4.1 背景知识

要理解global data的意义,需要先理解如下的事实: u-boot是一个bootloader,有些情况下,它可能位于系统的只读存储器(ROM或者flash)中,并从那里开始执行。

因此,这种情况下,在u-boot执行的前期(在将自己copy到可读写的存储器之前),它所在的存储空间,是不可写的,这会有两个问题:

1)堆栈无法使用,无法执行函数调用,也即C环境不可用。

2)没有data段(或者正确初始化的data段)可用,不同函数或者代码之间,无法通过全局变量的形式共享数据。

对于问题1,通常的解决方案是: u-boot运行起来之后,在那些不需要执行任何初始化动作即可使用的、可读写的存储区域,开辟一段堆栈(stack)空间。

一般来说,大部分的平台(如很多ARM平台),都有自己的SRAM,可用作堆栈空间。如果实在不行,也有可借用CPU的data cache的方法(不再过多说明)。

对于问题2,解决方案要稍微复杂一些: 首先,对于开发者来说,在u-boot被拷贝到可读写的RAM(这个动作称作relocation)之前,永远不要使用全局变量。

其次,在relocation之前,不同模块之间,确实有通过全局变量的形式传递数据的需求。怎么办?这就是global data需要解决的事情。

4.2 global data

为了在relocation前通过全局变量的形式传递数据,u-boot设计了一个巧妙的方法:

1)定义一个struct global_data类型的数据结构,里面保存了各色各样需要传递的数据 该数据结构的具体内容,后面用到的时候再一个一个解释,这里不再详细介绍。具体可参考:include/asm-generic/global_data.h

2)堆栈配置好之后,在堆栈开始的位置,为struct global_data预留空间(可参考第3章中相关的说明),并将开始地址(就是一个struct global_data指针)保存在一个寄存器中,后续的传递,都是通过保存在寄存器中的指针实现

对arm64平台来说,该指针保存在了X18寄存器中,如下: bl      board_init_f_alloc_reserve

mov     sp, x0

/* set up gd here, outside any C code */

mov     x18, x0

bl      board_init_f_init_reserve

上面board_init_f_alloc_reserve的返回值(x0)就是global data的指针。 #ifdef __clang__

#define DECLARE_GLOBAL_DATA_PTR

#define gd      get_gd()

static inline gd_t *get_gd(void)

{

gd_t *gd_ptr;

#ifdef CONFIG_ARM64

__asm__ volatile("mov %0, x18\n" : "=r" (gd_ptr));#else

}

#else

#ifdef CONFIG_ARM64

#define DECLARE_GLOBAL_DATA_PTR         register volatile gd_t *gd asm ("x18")#else

#endif

5. 前置的板级初始化操作

global data准备好之后,u-boot会执行前置的板级初始化动作,即board_init_f。所谓的前置的初始化动作,主要是relocation之前的初始化操作,也就是说: 执行board_init_f的时候,u-boot很有可能还在只读的存储器中。大家记住这一点就可以了!

注3:大家可能会觉得这里的f(front?)和r(rear?)的命名有点奇怪,我猜这个软件工程师应该是车迷,是不是借用了前驱和后驱的概念?不得而知啊。

对于ARM等平台来说,u-boot提供了一个通用的board_init_f接口,该接口使用u-boot惯用的设计思路: u-boot将需要在board_init_f中初始化的内容,抽象为一系列API。这些API由u-boot声明,由平台的开发者根据实际情况实现。具体可参考本章后续的描述。

5.1 board_init_f

位于common/board_f.c中的board_init_f接口的实现非常简单,如下(省略了一些无用代码): void board_init_f(ulong boot_flags)

{

gd->flags = boot_flags;

gd->have_console = 0;

if (initcall_run_list(init_sequence_f))

hang();

}

对global data进行简单的初始化之后,调用位于init_sequence_f数组中的各种初始化API,进行各式各样的初始化动作。后面将会简单介绍一些和ARM平台有关的、和平台的移植工作有关的、比较重要的API。其它API,大家可以参考source code自行理解。

注4:下面红色字体标注的API,是u-boot移植时有很大可能需要的API,大家留意就是。

5.2 fdtdec_setup #ifdef CONFIG_OF_CONTROL

fdtdec_setup,

#endif

如果打开了CONFIG_OF_CONTROL,则调用fdtdec_setup,配置gd->fdt_blob指针(即device tree所在的存储位置)。对ARM平台来说,u-boot的Makefile会通过连接脚本,将dtb文件打包到u-boot image的“__dtb_dt_begin”位置处,因此不需要特别关心。

5.3 trace_early_init #ifdef CONFIG_TRACE

trace_early_init,

#endif

由CONFIG_TRACE配置项控制,暂且不用关注,后面用到的时候再分析。

5.4 initf_malloc

如果定义了CONFIG_SYS_MALLOC_F_LEN,则调用initf_malloc,初始化malloc有关的global data,如gd->malloc_limit、gd->malloc_ptr。

5.5 arch_cpu_init

cpu级别的初始化操作,可以在需要的时候由CPU有关的code实现。

5.6 initf_dm

driver model有关的初始化操作。如果定义了CONFIG_DM,则调用dm_init_and_scan初始化并扫描系统所有的device。如果定义了CONFIG_TIMER_EARLY,调用dm_timer_init初始化driver model所需的timer。

5.7 board_early_init_f #if defined(CONFIG_BOARD_EARLY_INIT_F)

board_early_init_f,

#endif

如果定义CONFIG_BOARD_EARLY_INIT_F,则调用board_early_init_f接口,执行板级的early初始化。平台的开发者可以根据需要,实现board_early_init_f接口,以完成特定的功能。

5.8 timer_init

初始化系统的timer。

该接口应该由平台或者板级的代码实现,初始化成功后,u-boot会通过其它的API获取当前的timestamp,后面用到的时候再详细介绍。

5.9 get_clocks

获取当前CPU和BUS的时钟频率,并保存在global data中: gd->cpu_clk

gd->bus_clk

5.10 env_init

初始化环境变量有关的逻辑,不需要特别关注。

5.11 init_baud_rate gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);

获取当前使用串口波特率,可以有两个途径(优先级从高到低):从"baudrate"中获取;从CONFIG_BAUDRATE配置项获取。

5.12 serial_init

初始化serial,包括u-boot serial core以及具体的serial driver。该函数执行后,系统的串口(特别是用于控制台的)已经可用。

5.13 console_init_f /* Called before relocation - use serial functions */

int console_init_f(void)

{

gd->have_console = 1;

#ifdef CONFIG_SILENT_CONSOLE

if (getenv("silent") != NULL)

gd->flags |= GD_FLG_SILENT;

#endif

print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);

return 0;

}

初始化系统的控制台,之后串口输出可用。大家可留意CONFIG_SILENT_CONSOLE配置项,如果使能,可以通过“silent”环境变量,控制u-boot的控制台是否输出。

5.14  fdtdec_prepare_fdt #ifdef CONFIG_OF_CONTROL

fdtdec_prepare_fdt,

#endif

如果定义了CONFIG_OF_CONTROL,调用fdtdec_prepare_fdt接口,准备device tree有关的内容。后续device tree的分析文章会详细介绍。

5.15 display_options/display_text_info/print_cpuinfo/show_board_info

通过控制台,显示一些信息,可用于debug。

5.16 misc_init_f #if defined(CONFIG_MISC_INIT_F)

misc_init_f,

#endif

如果使能了CONFIG_MISC_INIT_F,则调用misc_init_f执行misc driver有关的初始化。

5.17 init_func_i2c #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)

init_func_i2c,

#endif

如果使能了CONFIG_HARD_I2C或者CONFIG_SYS_I2C,则调用init_func_i2c执行i2c driver有关的初始化。

5.18 init_func_spi #if defined(CONFIG_HARD_SPI)

init_func_spi,

#endif

如果使能了CONFIG_HARD_SPI,则调用init_func_spi执行spi driver有关的初始化。

5.19 announce_dram_init

宣布我们要进行DDR的初始化动作了(其实就是一行打印)。

5.20 dram_init #if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \

defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)

dram_init,              /* configure available RAM banks */

#endif

调用dram_init接口,初始化系统的DDR。dram_init应该由平台相关的代码实现。

如果DDR在SPL中已经初始化过了,则不需要重新初始化,只需要把DDR信息保存在global data中即可,例如: gd->ram_size = …

5.21 testdram #if defined(CONFIG_SYS_DRAM_TEST)

testdram,

#endif /* CONFIG_SYS_DRAM_TEST */

如果定义了CONFIG_SYS_DRAM_TEST,则会调用testdram执行DDR的测试操作。可以在开发阶段打开,系统稳定后关闭。

5.22 DRAM空间的分配

DRAM初始化完成后,就可以着手规划u-boot需要使用的部分,如下图:

ed280b642cd81b32aa54e929454b4278.gif

图片1 u-boot中DRAM的使用情况

总结如下: 1)考虑到后续的kernel是在RAM的低端位置解压缩并执行的,为了避免麻烦,u-boot将使用DRAM的顶端地址,即gd->ram_top所代表的位置。其中gd->ram_top是由setup_dest_addr函数配置的。

2)u-boot所使用的DRAM,主要分为三类:各种特殊功能所需的空间,如log buffer、MMU page table、LCD fb buffer、trace buffer、等等;u-boot的代码段、数据段、BSS段所占用的空间(就是u-boot relocate之后的执行空间),由gd->relocaddr标示;堆栈空间,从gd->start_addr_sp处递减。

3)特殊功能以及u-boot所需空间,是由reserve_xxx系列函数保留的,具体可参考source code,这里不再详细分析。

4)reserve空间分配完毕后,堆栈紧随其后,递减即可。

5.23 setup_dram_config

调用dram_init_banksize接口(由具体的平台代码实现),初始化DDR的bank信息。

5.24 reloc_fdt

如果没有定义CONFIG_OF_EMBED,则先将device tree拷贝到图片1 new_fdt所在的位置,也就是device tree的relocation操作。

5.25 setup_reloc

计算relocation有关的信息,主要是 gd->reloc_off,计算公式如下: gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;

其中CONFIG_SYS_TEXT_BASE是u-boot relocation之前在(只读)memory的位置(也是编译时指定的位置),gd->relocaddr是relocation之后的位置,因此gd->reloc_off代表u-boot relocation操作之后的偏移量,后面relocation时会用到。

同时,该函数顺便把global data拷贝到了图片1所示的“new global data”处,其实就是global data的relocation。

6. u-boot的relocation

前面讲过,u-boot是有可能在只读的memory中启动的。简单起见,u-boot假定所有的启动都是这样,因此u-boot的启动逻辑,都是针对这种情况设计的。在这种情况下,基于如下考虑: 1)只读memory中执行,代码需要小心编写(不能使用全局变量,等等)。

2)只读memory执行速度通常比较慢。

u-boot需要在某一个时间点,将自己从“只读memory”中,拷贝到可读写的memory(如SDRAM,后面统称RAM,注意和SRAM区分,不要理解错了)中继续执行,这就是relocation(重定位)操作。

relocation的时间点,可以是“系统可读写memory始化完成之后“的任何时间点。根据u-boot当前的代码逻辑,是在board_init_f执行完成之后,因为board_init_f中完成了很多relocation有关的准备动作,具体可参考第5章的描述。

u-boot relocation的代码如下(以arm64为例): ldr     x0, [x18, #GD_START_ADDR_SP]    /* x0 start_addr_sp */

bic     sp, x0, #0xf    /* 16-byte alignment for ABI compliance */

ldr     x18, [x18, #GD_BD]              /* x18 bd */

sub     x18, x18, #GD_SIZE              /* new GD is below bd */

adr     lr, relocation_return

ldr     x9, [x18, #GD_RELOC_OFF]        /* x9 reloc_off */

add     lr, lr, x9      /* new return address after relocation */

ldr     x0, [x18, #GD_RELOCADDR]        /* x0 relocaddr */

b       relocate_code

relocation_return:

逻辑比较简单:

1)从global data中取出relocation之后的堆栈基址,16-byte对齐后,保存到sp中。

2)将新的global data的指针,保存在x18寄存器中。

3)计算relocation之后的执行地址(relocation_return处),计算的方法就是当前的relocation_return位置加上gd->reloc_off。

4)以relocation的目的地址(gd->relocaddr)为参数,调用relocate_code执行实际的relocation动作,就是将u-boot的代码段、data段、bss段等数据,拷贝到新的位置(gd->relocaddr)。

7. 后置的板级初始化操作

relocate完成之后,真正的C运行环境才算建立了起来,接下来会执行“后置的板级初始化操作”,即board_init_r函数。board_init_r和board_init_f的设计思路基本一样,也有一个很长的初始化序列----init_sequence_r,该序列中包含如下的初始化函数(逻辑比较简单,这里不再涉及细节,权当列出index吧):

注5:老规矩,红色字体标注的函数是比较重要的函数。

1)initr_trace,初始化并使能u-boot的tracing system,涉及的配置项有CONFIG_TRACE。

2)initr_reloc,设置relocation完成的标志。

3)initr_caches,使能dcache、icache等,涉及的配置项有CONFIG_ARM。

4)initr_malloc,malloc有关的初始化。

5)initr_dm,relocate之后,重新初始化DM,涉及的配置项有CONFIG_DM。

6)board_init,具体的板级初始化,需要由board代码根据需要实现,涉及的配置项有CONFIG_ARM。

7)set_cpu_clk_info,Initialize clock framework,涉及的配置项有CONFIG_CLOCKS。

8)initr_serial,重新初始化串口(不太明白什么意思)。

9)initr_announce,宣布已经在RAM中执行,会打印relocate后的地址。

10)board_early_init_r,由板级代码实现,涉及的配置项有CONFIG_BOARD_EARLY_INIT_R。

11)arch_early_init_r,由arch代码实现,涉及的配置项有CONFIG_ARCH_EARLY_INIT_R。

12)power_init_board,板级的power init代码,由板级代码实现,例如hold住power。

13)initr_flash、initr_nand、initr_onenand、initr_mmc、initr_dataflash,各种flash设备的初始化。

14)initr_env,环境变量有关的初始化。

15)initr_secondary_cpu,初始化其它的CPU core。

16)stdio_add_devices,各种输入输出设备的初始化,如LCD driver等。

17)interrupt_init,中断有关的初始化。

18)initr_enable_interrupts,使能系统的中断,涉及的配置项有CONFIG_ARM(ARM平台u-boot实在开中断的情况下运行的)。

19)initr_status_led,状态指示LED的初始化,涉及的配置项有CONFIG_STATUS_LED、STATUS_LED_BOOT。

20)initr_ethaddr,Ethernet的初始化,涉及的配置项有CONFIG_CMD_NET。

21)board_late_init,由板级代码实现,涉及的配置项有CONFIG_BOARD_LATE_INIT。

22)等等…

23)run_main_loop/main_loop,执行到main_loop,开始命令行操作。

原创文章,转发请注明出处。蜗窝科技,www.wowotech.net。c6a6308114f401be7df747ae46f2b4db.png

评论:

renameit

2020-07-20 12:39

这篇文章exciting~,解答了我的疑惑。

三五十旅

2020-07-22 11:34

@renameit:这位兄弟的昵称。。。

VincentLi

2020-07-22 16:02

@三五十旅:学linux的后台很硬

zhoubo

2019-01-12 16:09

有个疑问,reloc之后,执行函数调用的时候,如何保证正确跳转的。比如:

initr_caches这个函数反汇编出来的结果,里面

8201c5ac:   97ff9649    bl  82001ed0

这一条,reloc之后,enable_caches的地址已经变了,如何跳转过去的。

是因为bl是相对跳转?

static int initr_caches(void)

{

8201c5a4:   a9bf7bfd    stp x29, x30, [sp,#-16]!

8201c5a8:   910003fd    mov x29, sp

/* Enable caches */

enable_caches();

8201c5ac:   97ff9649    bl  82001ed0

return 0;

}

8201c5b0:   52800000    mov w0, #0x0                    // #0

8201c5b4:   a8c17bfd    ldp x29, x30, [sp],#16

8201c5b8:   d65f03c0    ret

2019-01-14 09:11

@zhoubo:reloc之后的代码逻辑是不一样的,下面的留言有讨论这个问题,你可以搜索relocate_to_text看一下,然后自己跟一下代码。

fengzi

2018-07-19 09:55

大侠,求教一下。linux待机恢复的时候是从UBOOT哪里跳到Kernel去的啊,要跳到哪个地址去?

niufei

2018-03-21 17:22

你好!蜗蜗,我有好几个问题想请教一下,我用的是friendlyarm的uboot-nanopi2,这个uboot跟你们说的都不太一样。

下面是我粘贴的start.s代码

.globl reset

reset:

bl    save_boot_params

mrs    r0, cpsr

bic    r0, r0, #0x1f

orr    r0, r0, #0xd3

msr    cpsr,r0

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

bl    cpu_init_cp15

bl    cpu_init_crit

#endif

#ifdef CONFIG_RELOC_TO_TEXT_BASE

relocate_to_text:

/*

* relocate u-boot code on memory to text base

* for nexell arm core (add by jhkim)

*/

adr    r0, _stext                /* r0

ldr    r1, TEXT_BASE            /* test if we run from flash or RAM */

cmp r0, r1                  /* don't reloc during debug         */

beq clear_bss

ldr    r2, _bss_start_ofs

add    r2, r0, r2                /* r2

copy_loop_text:

ldmia    r0!, {r3-r10}        /* copy from source address [r0]    */

stmia    r1!, {r3-r10}        /* copy to   target address [r1]    */

cmp    r0, r2                    /* until source end addreee [r2]    */

ble    copy_loop_text

ldr    r1, TEXT_BASE            /* restart at text base */

mov pc, r1

clear_bss:

ldr    r0, _bss_start_ofs

ldr    r1, _bss_end_ofs

ldr    r4, TEXT_BASE            /* text addr */

add    r0, r0, r4

add    r1, r1, r4

mov    r2, #0x00000000            /* clear */

clbss_l:str    r2, [r0]            /* clear loop... */

add    r0, r0, #4

cmp    r0, r1

bne    clbss_l

#ifdef CONFIG_MMU_ENABLE

bl    mmu_turn_on

#endif

ldr    sp, =(CONFIG_SYS_INIT_SP_ADDR)

bic    sp, sp, #7                    /* 8-byte alignment for ABI compliance */

sub    sp, #GD_SIZE                /* allocate one GD above SP */

bic    sp, sp, #7                    /* 8-byte alignment for ABI compliance */

mov    r9, sp                        /* GD is above SP */

mov    r0, #0

bl    board_init_f

mov    sp, r9                        /* SP is GD's base address */

bic    sp, sp, #7                    /* 8-byte alignment for ABI compliance */

sub    sp, #GENERATED_BD_INFO_SIZE    /* allocate one BD above SP */

bic    sp, sp, #7                    /* 8-byte alignment for ABI compliance */

mov    r0, r9                        /* gd_t *gd */

ldr r1, TEXT_BASE                /* ulong text */

mov r2, sp                        /* ulong sp */

bl    gdt_reset

/* call board_init_r(gd_t *id, ulong dest_addr) */

mov    r0, r9                            /* gd_t */

ldr    r1,  =(CONFIG_SYS_MALLOC_END)    /* dest_addr for malloc heap end */

/* call board_init_r */

ldr    pc, =board_init_r                /* this is auto-relocated! */

#else    /* CONFIG_RELOC_TO_TEXT_BASE */

bl    _main

#endif

/*------------------------------------------------------------------------------*/

ENTRY(c_runtime_cpu_setup)

/*

* If I-cache is enabled invalidate it

*/

#ifndef CONFIG_SYS_ICACHE_OFF

mcr    p15, 0, r0, c7, c5, 0    @ invalidate icache

mcr     p15, 0, r0, c7, c10, 4    @ DSB

mcr     p15, 0, r0, c7, c5, 4    @ ISB

#endif

/*

* Move vector table

*/

/* Set vector address in CP15 VBAR register */

ldr     r0, =_stext

mcr     p15, 0, r0, c12, c0, 0  @Set VBAR

bx    lr

ENDPROC(c_runtime_cpu_setup)

ENTRY(save_boot_params)

bx    lr            @ back to my caller

ENDPROC(save_boot_params)

.weak    save_boot_params

ENTRY(cpu_init_cp15)

mov    r0, #0            @ set up for MCR

mcr    p15, 0, r0, c8, c7, 0    @ invalidate TLBs

mcr    p15, 0, r0, c7, c5, 0    @ invalidate icache

mcr    p15, 0, r0, c7, c5, 6    @ invalidate BP array

dsb

isb

mrc    p15, 0, r0, c1, c0, 0

bic    r0, r0, #0x00002000    @ clear bits 13 (--V-)

bic    r0, r0, #0x00000007    @ clear bits 2:0 (-CAM)

orr    r0, r0, #0x00000002    @ set bit 1 (--A-) Align

orr    r0, r0, #0x00000800    @ set bit 11 (Z---) BTB

#ifdef CONFIG_SYS_ICACHE_OFF

bic    r0, r0, #0x00001000    @ clear bit 12 (I) I-cache

#else

orr    r0, r0, #0x00001000    @ set bit 12 (I) I-cache

#endif

mcr    p15, 0, r0, c1, c0, 0

mov    pc, lr            @ back to my caller

ENDPROC(cpu_init_cp15)

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

ENTRY(cpu_init_crit)

b    lowlevel_init        @ go setup pll,mux,memory

ENDPROC(cpu_init_crit)

#endif

这里面有个配置项叫   CONFIG_RELOC_TO_TEXT_BASE  这个板子现在用的是SD卡启动,理论上是肯定要relocate的对吗???

再往下看,

bl    cpu_init_cp15

bl    cpu_init_crit

之后直接就进入了

copy_loop_text:

ldmia    r0!, {r3-r10}        /* copy from source address [r0]    */

stmia    r1!, {r3-r10}        /* copy to   target address [r1]    */

cmp    r0, r2                    /* until source end addreee [r2]    */

ble    copy_loop_text

ldr    r1, TEXT_BASE            /* restart at text base */

mov pc, r1

关键是最后两行ldr r1,TEXT_BASE mov pc,r1,上面几行就是将从stext开始的代码拷贝到了TEXT_BASE的地址,这两句的意思是说回到TEXT_BASE地址处执行代码,这样不就是把uboot(从reset 入口开始)重新运行了一遍吗?我感觉是这样,蜗蜗你说呢???我想确认一下。还有uboot那么多代码,不同的板子有各自的代码,uboot是通过mkfile和.mk文件来识别的吗?

谢谢了,新手linuxer 拜谢。

2018-03-22 19:09

@niufei:1. 是的。relocate之后会重新执行,然后代码会判断是否已经relocate了,如果是,正常执行即可。

2. 差不多是这样的,本质上就是使用GNU的Kconfig机制。

orange

2017-07-06 10:25

非常有参考价值!感谢分享

Sky

2017-06-02 10:45

你好,在这里问一个比较不搭边的问题。

请问楼主对手机的在线升级有了解吗?

有的话可以指导下吗?

2017-06-05 18:22

@Sky:抱歉啊,不是很了解这块儿的东西哦。

gavin

2017-05-31 22:35

hi wowo,"按理说可以不用再relocate了。不过u-boot(特别是ARM64平台的)并没有给出一个简单的开关,可以关闭relocate的动作"

relocate那定义了CONFIG_SPL_BUILD 就不会执行relocate了啊。没明白你说uboot没有开关关闭relocate动作。

#if !defined(CONFIG_SPL_BUILD)

/*

* Set up intermediate environment (new sp and gd) and call

* relocate_code(addr_moni). Trick here is that we'll return

* 'here' but relocated.

*/

ldr x0, [x18, #GD_START_ADDR_SP]    /* x0 start_addr_sp */

bic sp, x0, #0xf    /* 16-byte alignment for ABI compliance */

ldr x18, [x18, #GD_BD]    /* x18 bd */

sub    x18, x18, #GD_SIZE /* new GD is below bd */

#endif /* !CONFIG_SPL_BUILD */

2017-06-01 08:37

@gavin:如果定义了CONFIG_SPL_BUILD,会编出来两个bin,一个spl,一个uboot。spl bin不会relocate,uboot bin会relocate。

如果没有定义CONFIG_SPL_BUILD ,那就只有uboot bin,会relocate。

所以CONFIG_SPL_BUILD的用法不是你理解的那样。

2017-02-24 21:26

__dtb_dt_begin是在哪里赋值的?

grep遍整个源码,没看到在lds文件里赋值过,就看到下面这个信息:

__dtb_dt_begin:

.incbin "dts/dt.dtb"

这是啥意思?

2017-02-24 22:37

@callme_friend:等有时间了我帮你查查~~

BeDook

2020-05-31 00:45

@callme_friend:.incbin伪指令用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的存放在当前文件中,编译器从其后开始继续处理,"dts/dt.dtb"设备树文件,本质就是一二进制文件,且汇编指令中标签"_dtb_dt_begin"就表示地址,不用赋值的。。。

ooonebook

2016-08-16 22:50

hi,wowo

如果在SPL里面初始化了DDR之后,并且跳到了DDR上的对应位置开始执行uboot,那么relocation的操作可以直接无视,只需要实现gd中对应变量的设置以及内存的规划?

还有个问题:

1)考虑到后续的kernel是在RAM的低端位置解压缩并执行的,为了避免麻烦,u-boot将使用DRAM的顶端地址,即gd->ram_top所代表的位置。其中gd->ram_top是由setup_dest_addr函数配置的。

所以我uboot最好是使用DRAM的高地址空间吗?

如果我用uboot后面的未使用RAM来解压kernel,会有什么后果吗?

dts应该会传个memory的节点给kernel,kernel应该也可以以此来判断哪些空间可以使用哪些空间不能使用,感觉uboot之前使用的RAM空间还是可以被kernel使用的。

2016-08-17 09:41

@ooonebook:其实这几个地方都不是强制的。

关于relocation,说实话,现在大多嵌入式系统的u-boot,在relocation之前,已经位于DRAM中了,按理说可以不用再relocate了。不过u-boot(特别是ARM64平台的)并没有给出一个简单的开关,可以关闭relocate的动作,我的理解,原因有三:

1. 如果代码已经在u-boot中了,relocation的代价是极小的(就是一个ram copy而已),因此没有太大的必要做出两个分支,区别对待(因为赚不到什么好处)。

2. relocate的过程中,也一并做了其它事情,特别是很多空间的预留(可以参考本文图片1的总结),就算不需要relocate,这些事情也是避免不了的。

3. 就是你后一个疑问了,u-boot在加载kernel之前,要尽量把自己移到DRAM的高端地址。

另一个疑问,u-boot必须放在DRAM的高端地址吗?

我觉的不一定,但是linux kernel默认把DRAM的低地址map给自己使用了,u-boot要怎么做呢?尽量离它远点咯……

ooonebook

2016-08-13 14:28

hi,wowo,目前开始移植uboot.bin了,然后看到sp的设置,想对比一下uboot.spl的堆栈设置,但是发现有个问题实在搞不清楚。

在之前uboot.spl中还没有移植DDR初始化代码的时候,就已经在board_init_f点亮LED了。

但是在arch/arm/lib/crt0.S中

73 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)

74     ldr sp, =(CONFIG_SPL_STACK)

75 #else

76     ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

77 #endif

但是其实我现在发现CONFIG_SPL_STACK我并没有配置,编译uboot-spl的时候,也确实是ldr sp, =(CONFIG_SYS_INIT_SP_ADDR),但是这个地址其实是在DRAM空间,也就是我还没初始化的那部分DDR的空间,理论上堆栈的使用应该会有问题才对,C函数应该也不能调用,但是为什么board_init_f却可以正常调用呢???

这个问题昨天研究了一天了还是没搞清楚。

怎么去查这个堆栈地址呢?

ooonebook

2016-08-13 15:56

@ooonebook:通过arm-none-linux-gnueabi-objdump把uboot-spl反汇编出来看了一下,sp里的堆栈地址确实是有问题的

00000110 <_main>:

110:   e59fd01c    ldr sp, [pc, #28]   ; 134 <_main>

114:   e3cdd007    bic sp, sp, #7

118:   e1a0000d    mov r0, sp

11c:   eb000012    bl  16c

120:   e1a0d000    mov sp, r0

124:   e1a09000    mov r9, r0

128:   eb000012    bl  178

12c:   e3a00000    mov r0, #0

130:   eb000009    bl  15c

134:   20ffff40    rscscs  pc, pc, r0, asr #30

110:   e59fd01c    ldr sp, [pc, #28]   ; 134 <_main>

所以堆栈的地址是134:   20ffff40,这个确实是DRAM的地址,所以是有问题的。

但是从反汇编出来的文件中可以看出来,C函数可以正常的原因是因为没有汇编的过程中并没有被解析出push和pop指令:

0000015c :

15c:   e3a00001    mov r0, #1

160:   eafffff4    b   138

00000164 :

164:   e3a00004    mov r0, #4

168:   eafffff2    b   138

00000138 :

138:   e350000f    cmp r0, #15

13c:   c3a00000    movgt   r0, #0

140:   e59f3010    ldr r3, [pc, #16]   ; 158

144:   e3012111    movw    r2, #4369   ; 0x1111

148:   e1e00000    mvn r0, r0

14c:   e5832280    str r2, [r3, #640]  ; 0x280

150:   e5830284    str r0, [r3, #644]  ; 0x284

154:   e12fff1e    bx  lr

158:   e0200000    eor r0, r0, r0

并且反汇编出来的汇编文件中,都没有看到push和pop的指令,这也许就是sp设置错误但是还是可以正常调用C函数的原因吧。

不过我还是把CONFIG_SPL_STACK设置成正确的RAM地址比较靠谱吧。

2016-08-13 16:01

@ooonebook:对的,因为你的C函数调用被编译器优化了。你可以试试不让它优化,哈哈~

1 2

发表评论:

昵称

邮件地址 (选填)

个人主页 (选填)

d4e3789769c8ad44c7e403863bfc3822.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值