第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > uboot启动第二阶段分析

uboot启动第二阶段分析

时间:2020-01-06 09:42:20

相关推荐

uboot启动第二阶段分析

之前介绍了 uboot 启动第一阶段,现在介绍启动的第二阶段

启动阶段的工作

启动第一阶段的主要工作是对处理器的内部资源(如时钟、串口)、内存(ddr)初始化,并进行 uboot 的重定位,并跳转到启动第二阶段

启动第二阶段的主要工作则是对处理器的外部资源(iNand、网卡芯片…)、uboot环境(uboot命令、环境变量…)等初始化,并等待命令输入

工作流程

正常情况下,在 uboot 的初始化工作完毕后,会启动内核,在启动内核后结束 uboot 程序。

但是用户可以阻止 uboot 的结束,进入 uboot 的命令行模式,就是一个 uboot 中的死循环;uboot 在死循环中不断接受命令、解析命令、执行命令

start_armboot

如果说启动第一阶段主要工作是 lowlevel_init 完成的,那么启动第二阶段的主要工作是 start_armboot 完成的

相关变量介绍

init_fnc_ptr

它是一个 init_fnc_t 类型的函数指针数组,通过typedef int (init_fnc_t) (void);定义

gd

#define DECLARE_GLOBAL_DATA_PTRregister volatile gd_t *gd asm ("r8")

gd 这个变量通过 DECLARE_GLOBAL_DATA_PTR 定义,这个变量的作用是用来存储 uboot 需要使用到的全局变量,这样可以减少全局变量的数量,方便他人阅读代码。具体的作用可以从结构体成员中得知

typedefstructglobal_data {bd_t*bd;unsigned longflags;unsigned longbaudrate;unsigned longhave_console;/* serial_init() was called */unsigned longreloc_off;/* Relocation Offset */unsigned longenv_addr;/* Address of Environment struct */unsigned longenv_valid;/* Checksum of Environment valid? */unsigned longfb_base;/* base address of frame buffer */#ifdef CONFIG_VFDunsigned charvfd_type;/* display type */#endifvoid**jt;/* jump table */} gd_t;

gd 变量的结构体成员的功能基本可以从名字和注释中得知,不过对第一个成员 bd 的描述很少,这里再从 bd_t 中获取信息

typedef struct bd_info {intbi_baudrate;/* serial console baudrate */unsigned longbi_ip_addr;/* IP Address */unsigned charbi_enetaddr[6]; /* Ethernet adress */struct environment_s *bi_env;ulong bi_arch_number;/* unique id for this board */ulong bi_boot_params;/* where this board expects params */struct/* RAM configuration */{ulong start;ulong size;}bi_dram[CONFIG_NR_DRAM_BANKS];#ifdef CONFIG_HAS_ETH1/* second onboard ethernet port */unsigned char bi_enet1addr[6];#endif} bd_t;

从 bd_t 的定义可以看出, bd 这个成员变量的主要作用是用来保存一些和板级相关的信息,如波特率、ip 地址等

因为 gd 这个变量需要被频繁访问,所以使用了 volatile ,避免编译器做出不适当的优化;另外使用 register asm (“r8”) 加速访问

内存排布

上面提到的 gd bd 指针变量只是声明,此时还不能使用,需要给他们分配内存空间

分配内存的原则就是够用、紧凑,所以首先需要做的是为 gd bd 分配基地址

在 uboot 中计算 gd 和 bd 的基地址的方式如下

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);#ifdef CONFIG_USE_IRQgd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);#endif...gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

其中 CFG_UBOOT_BASE=0x33e00000 CFG_UBOOT_SIZE=2MB CFG_MALLOC_LEN=912KB CFG_STACK_SIZE=512KB gd_t=36字节 bd_t≈44字节

从而可以得到内存分配如图

部分外设初始化工作

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}

这里初始化工作是通过一个 for 循环完成的,使用先前定义的 init_fnc_ptr 变量去指向一个预先定义的函数指针数组 init_sequence ,在 init_sequence 中存储了用来初始化外设的函数指针。通过 for 循环依次执行每一个初始化函数,一旦返回的值不为0,就停止程序的运行并报错

init_sequence 的定义如下

init_fnc_t *init_sequence[] = {cpu_init,/* basic cpu dependent setup */board_init,/* basic board dependent setup */interrupt_init,/* set up exceptions */env_init,/* initialize environment */init_baudrate,/* initialze baudrate settings */serial_init,/* serial communications setup */console_init_f,/* stage 1 init of console */display_banner,/* say that we are here */#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo,/* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard,/* display board info */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)init_func_i2c,#endifdram_init,/* configure available RAM banks */display_dram_config,NULL,};

cpu_init

cpu_init 只有当允许了中断的情况下才会做中断栈的设置工作,因为我们 uboot 中没有允许中断,所以这里相当于什么都没做

board_init

这里进行了网卡的初始化,本人使用的开发板使用的网卡芯片是 dm9000 ,所以使用了 dm9000_pre_init 进行预初始化操作

网卡的驱动是不需要修改的,关键是移植,dm9000_pre_init 需要做的事就是移植相关,就像 stm32 一样,使用 GPIO 前,需要进行初始化的工作,这里就是做这些的工作

除了网卡的初始化,这里还在 bd 中添加了机器码和参数内存地址

机器码是用来软件和开发板之间配对用的,避免将软件下载到错误的开发板导致损失。机器码是唯一的,需要向 uboot 进行申请,当然学习 uboot 的时候可以随意填写,只要 uboot 中配置的机器码和 linux 内核中的机器码一致即可

这里的参数地址中存放的是需要想内核中传送的字符串参数(bootargs)的地址, uboot 启动的时候是通过 r0 r1 r2 来传递参数,其中一个寄存器放的就是 bd->bi_boot_params 中的值

interrupt_init

这里是对定时器4做的相关初始化,进行了10ms的定时

env_init

这里是环境变量的初始化,通过 x210_sd.h 中定义的宏 CFG_ENV_IS_IN_AUTO 分析可以知道,env_init 应该使用的是 env_auto.c 中定义的 env_init

init_baudrate

首先是从 uboot 环境变量中读取波特率,如果环境变量中没有定义波特率就使用 x210_sd.h 定义的波特率

uboot 环境变量可以在 uboot 中使用 print 查看

serial_init

串口初始化,主要工作在汇编中完成了,这里没有做什么事

console_init_f

控制台初始化第一阶段,一般第一阶段后缀为 _f,第二阶段后缀为 _r

display_banner

用来输出 uboot 的 logo

const char version_string[] =U_BOOT_VERSION" (" __DATE__ " - " __TIME__ ")"CONFIG_IDENT_STRING;

uboot 启动时输出的 version_string 是定义在主 Makefile 中的,在编译时自动生成的 include/version_autogenerated.h 就会包含 U_BOOT_VERSION 相关信息

print_cpuinfo

这里可以输出 CPU 的相关信息

CPU: S5PV210@1000MHz(OK)APLL = 1000MHz, HclkMsys = 200MHz, PclkMsys = 100MHzMPLL = 667MHz, EPLL = 96MHzHclkDsys = 166MHz, PclkDsys = 83MHzHclkPsys = 133MHz, PclkPsys = 66MHzSCLKA2M = 200MHz

dram_init

这里做的是 ddr 软件方面的初始化

因为嵌入式设备是定制性的,不像 PC 机都是标准化的,可以自动获取 ddr 的片数、大小

因此在 uboot 中,需要人为添加 ddr 的相关配置信息,相当于是将 ddr 的配置信息写入 gd->bd->bi_dram 这个数组中

display_dram_config

打印 dram 信息

堆管理器初始化

mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);

这样堆的地址如下图

开发板专有初始化

为了保证 uboot 的通用性,三星使用了宏来判断当前的开发板的型号,并做对应的初始化

这里的初始化是针对 MMC 的,通过 Makefile 中的脚本,配置开发板是 sd 还是 onenand 启动;然后对 MMC 的初始化根据宏做相应的处理

环境变量重定位

uboot 的环境变量需要重定位到 ddr 中才能使用

在 env_relocate 中,先是通过宏判读环境变量是否嵌入在代码段中;本人的开发板的环境变量在 SD 卡中,所以直接跳过,直接分配一个缓存准备用来存放环境变量

uboot 第一次启动时,因为没有烧录 env 分区,gd->env_valid 应当为0,这时候会使用 uboot 的默认系统变量,并写入到 SD 卡的 env 分区中

控制台初始化

console_init_r 做的是控制台的第二阶段的初始化,主要是进行控制台的软件方面的配置

重定位了 stdin stdout stderr

网卡芯片初始化

eth_initialize 对网卡芯片本身进行初始化

开机logo显示

x210_preboot_init 进行开发板启动前的初始化,开启 lcd 显示开机 logo

logo 一般是使用软件制作的数组

通过sd卡烧录系统

通过 check_menu_update_from_sd 会判断开发部的 left 按键是否按下,开发板会在按键按下的时候,进入更新模式,读取 SD 卡中的镜像烧录系统

使用 SD 卡烧录比 fastboot 的方式简单、高效,一般用于量产的场景

死循环

主要是有三个功能

命令解析器开机倒计时命令补全

总结

以下来自朱老师总结的 uboot 的工作

init_sequencecpu_init空的board_init网卡、机器码、内存传参地址dm9000_pre_init网卡gd->bd->bi_arch_number机器码gd->bd->bi_boot_params内存传参地址interrupt_init定时器env_initinit_baudrategd数据结构中波特率serial_init空的console_init_f空的display_banner打印启动信息print_cpuinfo打印CPU时钟设置信息checkboard检验开发板名字dram_initgd数据结构中DDR信息display_dram_config打印DDR配置信息表mem_malloc_init初始化uboot自己维护的堆管理器的内存mmc_initializeinand/SD卡的SoC控制器和卡的初始化env_relocate环境变量重定位gd->bd->bi_ip_addrgd数据结构赋值gd->bd->bi_enetaddrgd数据结构赋值devices_init空的jumptable_init不用关注的console_init_r真正的控制台初始化enable_interrupts空的loadaddr、bootfile 环境变量读出初始化全局变量board_late_init空的eth_initialize空的x210_preboot_initLCD初始化和显示logocheck_menu_update_from_sd检查自动更新main_loop主循环

我的博客

公众号:greedyhao

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。