Beagleboneblack的MLO文件干了些啥
Beagleboneblack在启动linux之前还有三个启动阶段:
ROM code --> MLO --> u-boot --> kernel
先看看ROM code干了些什么
ROM code是TI固化在芯片内部的,处理器上电之后会先跑到这里执行一部分代码,看看这部分代码在哪:
Boot ROM一共有128K+48K,其中后48K具有可读可执行属性,那估计就应该在0x4002_0000的地方了.这48K的空间又被分成了这样子:
不过程序不应该是在RAM里运行的吗,怎么可以在ROM里运行呢.回想以前用STM32时,程序在静态时肯定被烧写到内部的Flash上了,上电之后也是直接从Flash里面执行代码,之后也只是把部分代码分散加载到RAM里,那这样说来程序也不是在RAM里运行的.
这个问题让我很困惑,我只能这么解释:如果CPU能直接取到指令,那它就能译码,执行对于这些芯片内部的Flash也好,ROM也好,RAM也好,这些存储器都是直接挂在CPU三总线上的,CPU可以直接从这些地方取到指令,然后执行,只是有的存储器不能写.而对于有些存储器,如EEPROM,SD卡,它们一般并不直接挂在总线上,中间还要有控制器和相应的驱动时序,所以CPU不能直接取到指令,程序也就不能在那里面运行.如果我说的不对,网友们一定予以指正.
回到这个ROM code上来,看看它干了些什么:
它说上电之后CPU执行公共端的初始化和堆栈设置,然后配置WDT1为3分钟,执行系统时钟配置,最终跳到booting的处理程序中.
首先根据软件配置或者SYSBOOY引脚生成启动设备列表,如果不按卡旁边的按钮,启动顺序是MMC1(eMMC) -> MMC0 -> UART0 -> USB0,按下则为SPI0 -> MMC0(SD card) -> USB0 -> UART0,然后根据设备类型执行存储器启动或者是外设启动初始化,假如我们从SD卡启动,
初始化MMC/SD控制器 -> 检测设备 -> 判断启动模式 -> 获取启动文件,即找到SD卡上的MLO文件,然后加载到SRAM去,同时跳到MLO程序中.至于它是怎么加载的,想必是读取MLO文件头信息,通过目的地址和文件大小复制过去的吧.
所以ROM code就做了这么些事情,那MLO程序从哪个地方开始执行呢,前面说过MLO文件的编译过程,在编译SPL程序时链接器的脚本是u-boot-spl.lds
MEMORY { .sram : ORIGIN = 0x402F0400, LENGTH = (0x4030B800 - 0x402F0400) }
其中给出了SRAM的起始地址和大小(109K),而且在makefile的配置文件autoconf.mk中,也给出了基地址:
CONFIG_SPL_TEXT_BASE=0x402F0400
所以MLO程序就从0x4020F0400的地方开始执行,再看看SRAM的内存分布:
109K的Image空间,6K的stack空间,上面还有xxxx,我的u-boot(2014.04)在编译之后MLO有76.5K,还是装得下的,u-boot.img有329K,自然就装不下的,所以才要MLO来引导u-boot.
接下来看看MLO程序干了些什么:
从start.S开始,位于arch/arm/cpu/armv7下,首先跳到reset:
_start: b reset
跳到save_boot_params:
reset:bl save_boot_params
save_boot_params具有weak属性,在start.S中有定义,如果其他地方没有定义的话,它就直接返回,由于这里是SPL程序,想必还是要save一下的,实际上在arch/arm/cpu/armv7/am33xx/lowlevel_init.S中有它的定义:
ENTRY(save_boot_params)ldr r1, =OMAP_SRAM_SCRATCH_BOOT_PARAMSstr r0, [r1]bx lr ENDPROC(save_boot_params)
将R0的值保存在这个地方OMAP_SRAM_SCRATCH_BOOT_PARAMS,即0x4030B824处,位于6K的stack空间中,R0是什么东西呢:
R0保存了Booting Parameters Structure这样一个结构体的地址,包括启动设备描述符,当前启动设备,重启原因这些,这个结构体在哪,启动设备描述符又是个什么东西我也不知道了.
回到start.S中,接着关中断,切换值SVC模式:
![](/assets/blank.gif)
mrs r0, cpsrand r1, r0, #0x1f @ mask mode bitsteq r1, #0x1a @ test for HYP modebicne r0, r0, #0x1f @ clear all mode bitsorrne r0, r0, #0x13 @ set SVC modeorr r0, r0, #0xc0 @ disable FIQ and IRQmsr cpsr,r0
![](/assets/blank.gif)
设置中断向量表:
![](/assets/blank.gif)
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Registerbic r0, #CR_V @ V = 0mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register/* Set vector address in CP15 VBAR register */ldr r0, =_startmcr p15, 0, r0, c12, c0, 0 @Set VBAR #endif
![](/assets/blank.gif)
底层初始化:
#ifndef CONFIG_SKIP_LOWLEVEL_INITbl cpu_init_cp15bl cpu_init_crit #endif
在编译SPL程序时,没有定义CONFIG_SKIP_LOWLEVEL_INIT这个宏,所以下面两个函数会执行,而编译u-boot时,会跳过,这也比较合理,只需要初始化一次嘛.
cpu_init_cp15主要是操作CP15寄存器,关掉缓存,关掉MMU,然后跳到cpu_init_crit:
![](/assets/blank.gif)
ENTRY(cpu_init_crit)/** Jump to board specific initialization...* The Mask ROM will have already initialized* basic memory. Go here to bump up clock rate and handle* wake up conditions.*/b lowlevel_init @ go setup pll,mux,memory ENDPROC(cpu_init_crit)
![](/assets/blank.gif)
跳到lowlevel_init中,这里是"b"指令跳转,不会改变lr寄存器的值:
![](/assets/blank.gif)
ENTRY(lowlevel_init)/** Setup a temporary stack*/ldr sp, =CONFIG_SYS_INIT_SP_ADDRbic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #ifdef CONFIG_SPL_BUILDldr r9, =gdata #elsesub sp, sp, #GD_SIZEbic sp, sp, #7mov r9, sp #endif/** Save the old lr(passed in ip) and the current lr to stack*/push {ip, lr}/** go setup pll, mux, memory*/bl s_initpop {ip, pc} ENDPROC(lowlevel_init)
![](/assets/blank.gif)
设置sp=0x4030FF40,即将运行C代码,让R9指向gdata,gdata是寄存器变量,在arch/arm/include/asm/global_data.h中声明,定义在arch/arm/lib/spl.c中,位于data段
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
然后讲ip,lr寄存器入栈跳到s_init中,位于arch/arm/cpu/am33xx/board.c中,主要调用函数如下:
-> save_omap_boot_params();
-> watchdog_disable();
-> timer_init();
-> set_uart_mux_conf();
-> setup_clocks_for_console();
-> uart_soft_reset();
-> gd = &gdata;
-> preloader_console_init();
-> prcm_init();
-> set_mux_conf_regs();
-> rtc32k_enable();
-> sdram_init();
这些函数就不再一一展开了,因为展开看了一下发现很难看懂,C语言功底远远不够(⊙﹏⊙)b
值得注意的是save_omap_boot_params函数,它将前面说的R0所保存的启动信息保存到gdata结构体中,以加载u-boot镜像文件.
preloader_console_init函数,位于common/spl/spl.c中,初始化了一个串口,输出了第一行信息,即u-boot版本号,编译时间
![](/assets/blank.gif)
void preloader_console_init(void) { gd->bd = &bdata; gd->baudrate = CONFIG_BAUDRATE; serial_init(); /* serial communications setup */ gd->have_console = 1; puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \ U_BOOT_TIME ")\n");//wlg: now we print our first information #ifdef CONFIG_SPL_DISPLAY_PRINT spl_display_print(); #endif }
![](/assets/blank.gif)
最后将DRAM初始化,便于加载u-boot到DRAM中.
之后返回到lowlevel_init.S中,
push {ip, lr}
pop {ip, pc}
将之前的lr弹给pc,也就是跳到前一个bl的下面,也就是start.S中:
bl _main
继续跳到_main中,位于arch/arm/lib/crt0.S中:
![](/assets/blank.gif)
ENTRY(_main)/** Set up initial C runtime environment and call board_init_f(0).*/#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)ldr sp, =(CONFIG_SPL_STACK) #elseldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endifbic sp, sp, #7 /* 8-byte alignment for ABI compliance */sub sp, 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, #0bl board_init_f
![](/assets/blank.gif)
再次赋值sp,仍然指向0x4030FF40,然后向下留出gd结构体的空间,并让R9指向这里,跳到board_init_f,位于arch/arm/lib/spl.c中,具有weak属性,在u-boot阶段有不同的实现:
![](/assets/blank.gif)
void __weak board_init_f(ulong dummy) {/* Clear the BSS. */memset(__bss_start, 0, __bss_end - __bss_start);/* Set global data pointer. */gd = &gdata;board_init_r(NULL, 0); }
![](/assets/blank.gif)
清BSS段,再次让gd,即R9指向gdata,跳到board_init_r,位于common/spl/spl.c中,主要执行函数如下:
-> spl_board_init();
-> boot_device = spl_boot_device();
根据boot_device的值在相应的地方加载镜像文件,比如是MMC/SD的话,将执行spl_mmc_load_image();位于common/spl/spl_mmc.c中,主要执行函数如下:
-> mmc_initialize(gd->bd);
-> mmc = find_mmc_device(0);
-> err = mmc_init(mmc);
-> boot_mode = spl_boot_mode();
根据boot_mode的值使用不同的方式读取镜像,比如是FAT模式,将执行一下分支:
![](/assets/blank.gif)
else if (boot_mode == MMCSD_MODE_FAT) {debug("boot mode - FAT\n"); #ifdef CONFIG_SPL_OS_BOOTif (spl_start_uboot() || spl_load_image_fat_os(&mmc->block_dev,CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION)) #endiferr = spl_load_image_fat(&mmc->block_dev,CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION,CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME); #endif
![](/assets/blank.gif)
spl_load_image_fat这个函数将u-boot.img文件读到它该去的地方,即DRAM中,具体是怎么实现的我也不太懂了,执行完毕之后回到spl.c中,根据镜像文件的类型执跳到u-boot中或者linux中:
![](/assets/blank.gif)
switch (spl_image.os) {case IH_OS_U_BOOT:debug("Jumping to U-Boot\n");break; #ifdef CONFIG_SPL_OS_BOOTcase IH_OS_LINUX:debug("Jumping to Linux\n");spl_board_prepare_for_linux();jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR); #endifdefault:debug("Unsupported OS image.. Jumping nevertheless..\n");}jump_to_image_no_args(&spl_image);
![](/assets/blank.gif)
SPL程序自然是跳到u-boot中了,执行下面的jump_to_image_no_args(&spl_image),位于arch/arm/cpu/armv7/omap-common/boot-common中:
![](/assets/blank.gif)
void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image) {typedef void __noreturn (*image_entry_noargs_t)(u32 *);image_entry_noargs_t image_entry =(image_entry_noargs_t) spl_image->entry_point;debug("image entry point: 0x%X\n", spl_image->entry_point);/* Pass the saved boot_params from rom code */image_entry((u32 *)&gd->arch.omap_boot_params); }
![](/assets/blank.gif)
貌似是跳到了image_entry那个地方,具体是怎么来的我就不晓得了╮(╯▽╰)╭
直接用file命令看一下最后生成的u-boot.img镜像文件:
u-boot.img: u-boot legacy uImage, U-Boot 2014.04 for am335x board, Firmware/ARM, Firmware Image (Not compressed), 329036 bytes, Tue Feb 28 05:56:24 2017, Load Address: 0x80800000, Entry Point: 0x00000000, Header CRC: 0xBA1D5896, Data CRC: 0x697BF780
看到加载地址和入口点,加载地址貌似比DRAM的基址高出了8M.
转载于:https://www.cnblogs.com/fengliu-/p/10654382.html
Beagleboneblack的MLO文件干了些啥相关推荐
- C专家编程 第6章 运动的诗章:运行时数据结构 6.3 操作系统在a.out文件里干了些什么
操作系统在a.out文件里干了些什么 为什么a.out要以段的形式组织.段可以方便地映射到链接器在运行时可以直接载入的对象中!载入器只是取文件中的每个段的映像,并直接将它们放入到内存中.从本 ...
- Pytorch(GPU)配环境原理:cuda+cudnn+pytorch配环境的每一步到底干了些什么?
作者:18届cyl 时间:2022.5.11 参考文章:https://blog.csdn.net/qq_42406643/article/details/109545766 最近帮舍友配pytorc ...
- 转载:IP-Guard都干了些什么(其他如威盾等亦差不多)
##IP-Guard 整个一裹着信息安全软件外衣的超级流氓,下面来看一下它对我们的系统都干了什么. 首先是生成的文件,别看它安装程序那么小,其实生成的文件很多也一点都不小 C:\Program Fil ...
- 【一周读书】哲学家,你们都干了些什么?
书籍:<哲学家们都干了些什么> 在读这本书之前,我对哲学的印象是这样的: 哲学似乎和宗教有点关系?似乎在解决人的精神痛苦方面的问题?哲学就是一大堆难懂并且无用的理论!我要是和同学谈哲学肯定 ...
- 什么是哲学?《哲学家们都干了些什么?》读后感
<哲学家们都干了些什么?> 前言 自从听到哲学这两个字开始,其实多年来心中有个问题,对,什么是哲学,哲学有什么用,直到我读了,<哲学家们都干了些什么?>林欣浩,林欣浩 并不是什 ...
- 【哲学问题】-《哲学家们都干了些什么?》
引言 本文来源于<哲学家们都干了些什么?>这本书以及本人阅读此书之后的一些想法.这本书虽通熟易懂,但贯穿了哲学所涉及的大部分内容,我将挑出书中涉及的比较重要的内容并融入我的部分思考分几次来 ...
- 西西弗的石头----读《哲学家都干了些什么》有感
<西西弗的神话>里讲述了一个希腊神话,说西西弗被众神惩罚,把一个巨石推向山顶.但石头一到山顶又会自己滚下来,西西弗必须重复这样的苦役,直到永远.加缪用这个例子来说明我们生活的荒谬. 西西弗 ...
- 《哲学家们都干了些什么》读后感
上帝既然知道亚当和夏娃会偷吃禁果,为什么一开始不去阻止他们? 奥古斯丁的解释是,关键在于自由.上帝给了亚当和人类自由意志,所以也必须让人类拥有作恶的可能. 更具体地说,上帝是善的,而上帝的善表现在上帝 ...
- 卫哲加盟阿里巴巴满百日:他干了些什么?
卫哲这位最年轻的跨国公司中国区CEO,在加盟阿里巴巴刚满百日的时间里,他做了三件事情,主要是明确战略:参与阿里巴巴新的三年.五年的发展规划:将买家体验提升到战略高度,重新推出买家发展和服务部门,增加海 ...
- 读书:哲学家们都干了些什么
哲学本质上是人理解人.人认识人的理性活动,是对世界基本和普遍之问题研究的学科,是关于世界观的理论体系.--百度百科 这本书应该是想用不那么严肃的方式串起整个哲学史,可能有一些地方不那么详实和严谨,但对 ...
最新文章
- 解决IE8,7下设置背景图片 background-size 不支持问题
- CSDN的Markdown编辑器常用语句
- 从 Vue 1.x 迁移 — Vue.js
- mac eclipse tomcat mysql_mac下使用eclipse+tomcat+mysql开发 j2ee(一)
- JavaScript(三)——对象与数组基础及API
- java导_java——导入功能
- tcp云服务器连接,云服务器可以tcp通讯吗
- 在free bsd上跑JMeter 的 plugin PerfMon Server Agent
- matlab7.0窗口教程,MATLAB7.0实用教程
- 146条经典偏方(祖传秘方)
- CocosCreator之绳索摆动效果
- SQL函数StDev与StDevP区别
- springBoot thymeleaf 属性为空时报错:EL1007E
- 000 Python教程
- 如何使用 COMSOL 进行电热分析?
- MindMapper屏幕捕获功能该如何使用
- Unity3D-实战-01坦克大战-day03箱式对撞机、钢体、2D渲染层级问题、重力问题(出现坠落现象)、旋转问题、左右上下冲突问题、抖动问题
- java斐波那契优化_用HashMap优化斐波那契数列 java算法
- MacOS系统安装淘宝 TAE SDK 开发环境
- CBTC中ATP的功能需求与应用举例