am335x linux 的uboot工作流程.doc,ARM板移植Linux系统启动(三)UBOOT移植

经过这些年的演变,U-boot已经从一个简单的loader慢慢发展成了一个小小系统,硬件上原生支持各种平台、外设,软件上支持Fat&ext234等文件系统、传输协议、测试工具,可以说相当完善了。这种进化,负面影响是让整体变得更复杂臃肿,但也有更多的正面影响,是让我们的移植工作更加简单、模块化。

Uboot代码结构

代码结构是了解uboot的基础,作为一个成熟的开源软件,uboot的代码结构很清晰。绝大多数的代码都是通用的,所以在移植过程中,基本上只需要找到自己的对应平台,修改板子特有的部分即可。你看到的是非授权版本!爬虫凶猛,请尊重知识产权!

转载请注明出处:http://conanwhf.github.io/2017/06/09/bootup-3-uboot/

├── api uboot提供的API接口

├── arch 与体系结构相关的代码

│ ├── arm

│ │ ├── mach-omap2 BeagleBoneBlack使用的架构

│ ├── x86

├── board 根据不同的具体开发板而定制的代码

│ ├── ti 厂商:TI

│ │ ├── am335x BeagleBoneBlack使用的平台,主要的改动应该在这里,包括内存初始化

│ │ ├── beagle

│ │ ├── common

│ │ ├── evm

├── cmd 通用命令行处理工具

├── common 通用核心代码,主要给uboot使用,部分SPL

│ ├── spl SPL的通用代码

├── configs 配置文件

├── disk 磁盘分区相关

├── doc 文档

├── drivers 各种驱动,uboot使用

├── dts dtb文件的编译脚本

├── examples 范例

├── fs 文件系统代码

├── include 公用头文件

│ ├── configs 不同板子配置选项的头文件

├── lib 通用库

├── net 网络相关

├── post Power On Self Test,开机自检程序

├── scripts 编译脚本

├── test 测试程序

├── tools 相关小工具

如果是想要重新编译和优化uboot,那么通常修改配置文件即可;如果是一块硬件上有修改的板子,那么则需要另外关注board/XXX/部分,基于原有的初始化代码进行修改;如果是完全新设计的芯片,则必须根据芯片不同模块的datasheet自己添加一套初始化代码,只有arm平台的一些基本内容可以通用了。

启动流程

前一篇讲SPL的启动的时候提到,最初从start.S call到了crt0.S。crt0.S会先后调用board_init_f()和board_init_r()两个函数,当作为SPL编译时board_init_f()是直接返回的,而在uboot时是有用的。大致上来说,board_init_f()侧重于基础硬件的初始化,以及软件堆栈等方面的准备,更底层一点;而board_init_r()则更多的是软件方面的初始化,并且最终完成uboot启动过程或进入命令行。

common/board_f.c

这是uboot(实际上是uboot第二阶段,u-boot.img所包含的内容,下文不再区分)的入口函数board_init_f()所在的文件。可以看到函数board_init_f()很简单,初始化全局变量后,就根据一个init_sequence_f依次运行不同的函数,失败则把系统hang住,成功当然就是进入正常的启动流程中去了。init_sequence_f在同一个文件中也有定义,很长,我删减无关平台的内容大致看看:

static init_fnc_t init_sequence_f[] = {

//==========基本数据init,malloc数据块

setup_mon_len,

#ifdef CONFIG_OF_CONTROL

fdtdec_setup,

#endif

#ifdef CONFIG_TRACE

trace_early_init,

#endif

initf_malloc,

initf_console_record,

//初始化CPU

arch_cpu_init, /* basic arch cpu dependent setup */

mach_cpu_init, /* SoC/machine dependent CPU setup */

initf_dm,

arch_cpu_init_dm,

mark_bootstage, /* need timer, go after init dm */

#if defined(CONFIG_BOARD_EARLY_INIT_F)

board_early_init_f,

#endif

//==========Timer

#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \

defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \

defined(CONFIG_SH) || defined(CONFIG_SPARC)

timer_init, /* initialize timer */

#endif

//==========初始化环境变量

env_init, /* initialize environment */

//==========初始化串口,并打印信息

init_baud_rate, /* initialze baudrate settings */

serial_init, /* serial communications setup */

console_init_f, /* stage 1 init of console */

display_options, /* say that we are here */

display_text_info, /* show debugging info if required */

print_cpuinfo, /* display cpu info (and speed) */

#if defined(CONFIG_DISPLAY_BOARDINFO)

show_board_info,

#endif

//==========基本外设初始化,包括I2C,SPI,WatchDog等

INIT_FUNC_WATCHDOG_INIT

#if defined(CONFIG_MISC_INIT_F)

misc_init_f,

#endif

INIT_FUNC_WATCHDOG_RESET

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

init_func_i2c,

#endif

#if defined(CONFIG_HARD_SPI)

init_func_spi,

#endif

//==========内存初始化

announce_dram_init,

/* TODO: unify all these dram functions? */

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

defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || \

defined(CONFIG_SH)

dram_init, /* configure available RAM banks */

#endif

INIT_FUNC_WATCHDOG_RESET

#if defined(CONFIG_SYS_DRAM_TEST)

testdram,

#endif /* CONFIG_SYS_DRAM_TEST */

INIT_FUNC_WATCHDOG_RESET

//==========一堆显示相关的初始化,略

/*

* Now that we have DRAM mapped and working, we can

* relocate the code and continue running from DRAM.

*

* Reserve memory at end of RAM for (top down in that order):

* - area that won't get touched by U-Boot and Linux (optional)

* - kernel log buffer

* - protected RAM

* - LCD framebuffer

* - monitor code

* - board info struct

*/

//==========将环境变量重置,并打印相关信息

setup_machine,

reserve_global_data,

reserve_fdt,

reserve_arch,

reserve_stacks,

setup_dram_config,

show_dram_config,

display_new_sp,

#ifdef CONFIG_SYS_EXTBDINFO

setup_board_extra,

#endif

INIT_FUNC_WATCHDOG_RESET

reloc_fdt,

setup_reloc,

NULL,

};

common/board_r.c

类似的,在board_init_r()中也会按顺序调用这么一个函数列表,函数名都很容易看懂,具体就不列出来了。跟board_init_f()不同的是,它最后有一个run_main_loop:

init_fnc_t init_sequence_r[] = {

initr_trace,

initr_reloc,

......

run_main_loop,

};

这个函数会进入一个死循环,尝试自动启动系统或者进入uboot的命令行:

static int run_main_loop(void)

{

ifdef CONFIG_SANDBOX

sandbox_main_loop_init();

endif

/* main_loop() can return to retry autoboot, if so just run it again */

for (;;)

main_loop();

return 0;

}

common/main.c

函数main_loop()的实现在main.c,可以看到,做好最后的准备工作后,uboot会尝试用autoboot_command(s)来启动kernel,如果失败,则进入cli_loop()函数中。

void main_loop(void)

{

const char *s;

bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");

#ifdef CONFIG_VERSION_VARIABLE

setenv("ver", version_string); /* set version variable */

#endif /* CONFIG_VERSION_VARIABLE */

cli_init();

run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)

update_tftp(0UL, NULL, NULL);

#endif /* CONFIG_UPDATE_TFTP */

s = bootdelay_process();

if (cli_process_fdt(&s))

cli_secure_boot_cmd(s);

autoboot_command(s);

cli_loop();

panic("No CLI available");

}

cli_loop()

这个函数比较简单,追踪code可以看到,common/cli.c, cli_loop()->common/cli_simple.c, cli_simple_loop(), cli_simple_loop()就是uboot的命令行了,不断读取用户输入来做出反应,同样也是个死循环。所以绝大部分的时候,main_loop()中的循环并不会进行多次,而是一旦自动启动失败就进入了命令行,不会再次自动尝试启动。

修改移植

一般简单的板子移植,主要是内存初始化代码,和硬件配置选项,以及启动变量。硬件的驱动是不需要自己写的,但要配置准确。这些修改主要集中在两个地方:配置头文件和平台对应的board.c。以BeagleBoneBlack为例,它的特别内容都在以下两个文件中:

board/ti/am335x/board.c

include/configs/am335x_evm.h

这是编译时脚本根据config中CONFIG_TARGET_AM335X_EVM=y来对应的,不同的平台文件会有所不同,但都可以通过查看Makefile和Kconfig来找到正确的文件。下文均以BeagleBoneBlack为例。

启动方式

当uboot的启动流程进入到autoboot,其历史使命基本上也就完成了。但对于一个移植的板子来说,配置自动启动的选项却是一个很重要的问题。启动方式的列表定义在在am335x_evm.h中:

define BOOT_TARGET_DEVICES(func) \

func(MMC, mmc, 0) \

func(LEGACY_MMC, legacy_mmc, 0) \

func(MMC, mmc, 1) \

func(LEGACY_MMC, legacy_mmc, 1) \

func(NAND, nand, 0) \

func(PXE, pxe, na) \

func(DHCP, dhcp, na)

endif

很容易看到这个启动方式是emmc,nand,PXE,DHCP,可以自己按照需要调整顺序或者添删。

内存初始化

在board/ti/am335x/board.c中的函数sdram_init()就是内存的初始化函数,它会根据EEPROM得到的板子型号,对应不同的DRAM配置。阅读代码不难发现,配置具体信息是定义在arch/arm/include/asm/arch-am33xx/ddr_defs.h。如果需要修改或者添加不同的内存配置,修改这两个文件即可。

其他硬件调整

绝大多数硬件的配置定义都在am335x_evm.h中,包括各种模块的Base address、初始化值、硬件定义等等,读者可以自行阅读。这些设置有的是根据芯片的手册而定,比如说memory的初始化值;有的是根据硬件设计而定,比如USB的Host & PERIPHERAL设置;有的是可以根据需要自行决定,比如CONFIG_SYS_BOOTM_LEN。具体改什么、怎么改,要根据情况分析而定。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值