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_PARAMS
str 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模式:

    mrs    r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0

设置中断向量表:

#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 Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL Register /* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif

底层初始化:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
bl cpu_init_crit
#endif

在编译SPL程序时,没有定义CONFIG_SKIP_LOWLEVEL_INIT这个宏,所以下面两个函数会执行,而编译u-boot时,会跳过,这也比较合理,只需要初始化一次嘛.

  cpu_init_cp15主要是操作CP15寄存器,关掉缓存,关掉MMU,然后跳到cpu_init_crit:

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)

跳到lowlevel_init中,这里是"b"指令跳转,不会改变lr寄存器的值:

ENTRY(lowlevel_init)
/*
* Setup a temporary stack
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp, #GD_SIZE
bic sp, sp, #7
mov 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_init
pop {ip, pc}
ENDPROC(lowlevel_init)

设置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版本号,编译时间

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
}

最后将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中:

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)
#else
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
bic 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, #0
bl board_init_f

再次赋值sp,仍然指向0x4030FF40,然后向下留出gd结构体的空间,并让R9指向这里,跳到board_init_f,位于arch/arm/lib/spl.c中,具有weak属性,在u-boot阶段有不同的实现:

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);
}

清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模式,将执行一下分支:

else if (boot_mode == MMCSD_MODE_FAT) {
debug("boot mode - FAT\n");
#ifdef CONFIG_SPL_OS_BOOT
if (spl_start_uboot() || spl_load_image_fat_os(&mmc->block_dev,
CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION))
#endif
err = spl_load_image_fat(&mmc->block_dev,
CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION,
CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME);
#endif

spl_load_image_fat这个函数将u-boot.img文件读到它该去的地方,即DRAM中,具体是怎么实现的我也不太懂了,执行完毕之后回到spl.c中,根据镜像文件的类型执跳到u-boot中或者linux中:

    switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("Jumping to U-Boot\n");
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
jump_to_image_no_args(&spl_image);

SPL程序自然是跳到u-boot中了,执行下面的jump_to_image_no_args(&spl_image),位于arch/arm/cpu/armv7/omap-common/boot-common中:

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);
}

貌似是跳到了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.

Beagleboneblack的MLO文件干了些啥的更多相关文章

  1. BeagleboneBlack上u-boot的MLO文件是哪里来的

    在玩BeagleboneBlack一段时间之后不可避免地接触到了u-boot,之前的玩耍过程大致上是这样的: 在MATLAB下耍,因为MATLAB提供了它的硬件支持,可以直接在命令行与之交互,也可在s ...

  2. loadView在App启动时到底都干了些什么?

    loadView在App启动时到底都干了些什么? 查阅苹果官方文档如下: 1. 当你访问一个ViewController的view属性时,如果此时view的值是nil,那么,ViewControlle ...

  3. swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?

    date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...

  4. 输入url后浏览器干了些什么(详解)

    输入url后浏览器干了些什么(详解) DNS(Domain Name System, 域名系统) 解析 DNS解析的过程就是寻找哪台机器上有你真正需要的资源过程.但你在浏览器张红输入一个地址时,例如: ...

  5. /proc/sysrq-trigger该文件能做些什么事情-转载

    /proc/sysrq-trigger该文件能做些什么事情呢? # 立即重新启动计算机 (Reboots the kernel without first unmounting file system ...

  6. RocketMQ Consumer 启动时都干了些啥?

    可能我们对 RocketMQ 的消费者认知乍一想很简单,就是一个拿来消费消息的客户端而已,你只需要指定对应的 Topic 和 ConsumerGroup,剩下的就是只需要: 接收消息 处理消息 就完事 ...

  7. 看看C# 6.0中那些语法糖都干了些什么(中篇)

    接着上篇继续扯,其实语法糖也不是什么坏事,第一个就是吃不吃随你,第二个就是最好要知道这些糖在底层都做了些什么,不过有一点 叫眼见为实,这样才能安心的使用,一口气上五楼,不费劲. 一:字符串嵌入值 我想 ...

  8. 看看C# 6.0中那些语法糖都干了些什么(上篇)

    今天没事,就下了个vs2015 preview,前段时间园子里面也在热炒这些新的语法糖,这里我们就来看看到底都会生成些什么样的IL? 一:自动初始化属性 确实这个比之前的版本简化了一下,不过你肯定很好 ...

  9. mysqldump备份过程中都干了些什么

    mysqldump备份方便,易读,功能丰富,相信大家都有 使用过这个命令进行备份,但是这个命令在备份的过程中都做了写什么呢,下面打开general_log进行查看: 1.登录mysql命令行客户端: ...

随机推荐

  1. nodejs基础学习

    一:复制官网的代码,建立一个简单的服务器 const http = require('http'); const hostname = '127.0.0.1'; const port = 3000; ...

  2. 2. socket结构体——表示socket地址

    一.两种通用socket结构体 1. sockaddr struct sockaddr { sa_family_t sa_family; // 地址族 char sa_data[14]; // 存放s ...

  3. Thunder团队第六周 - Scrum会议2

    Scrum会议2 小组名称:Thunder 项目名称:i阅app Scrum Master:宋雨 工作照片: 参会成员: 王航:http://www.cnblogs.com/wangh013/ 李传康 ...

  4. 11.22Daily Scrum(2)

    人员 任务分配完成情况 明天任务分配 王皓南 实现网页上视频浏览的功能.研究相关的代码和功能.984 数据库测试 申开亮 实现网页上视频浏览的功能.研究相关的代码和功能.985 实现视频浏览的功能 王 ...

  5. android仿win8

    在 eoe上偶然发现已经有人实现了这个功能的源码(地址:http://www.eoeandroid.com /forum.php?mod=viewthread&tid=327557),马上下载 ...

  6. 算法与数据结构3.1 stack

    ★实验任务 一天,小 L 发现了一台支持一下操作的机器: IN x:将整数 x 入栈 POP:将栈顶元素出栈 ASUB:出栈两个数,将两数差的绝对值入栈 COPY:将栈顶元素(如果有的话)复制一份,入 ...

  7. 分页查询es时,返回的数据不是自己所期望的问题

    在进行es分页查询时,一般都是用sql语句转成es查询字符串,在项目中遇到过不少次返回的数据不是自己所期望的那样时,多半原因是自己的sql拼接的有问题. 解决办法:务必要保证自己的sql语句拼接正确.

  8. javaweb引用jar类库的问题

    JAVAWEB中,需要引用的jar类库必须放在/WebContent/WEB-INF/lib文件夹中.不然就会各种报错.

  9. WCF服务的建立以及调用

    WCF对我来说既陌生又熟悉,陌生是因为没怎么接触过,熟悉是听得太多,今天抽出点时间看了一下WCF,并且自己也写了一WCF的小程序以及调用WCF.步骤为: 1.创建一个解决方案WCF,和一个控制台项目W ...

  10. bzoj 1934: [Shoi2007]Vote 善意的投票 (最小割)

    原来是赞同的连源,原来是反对的连汇,然后是朋友的就连在一起,这样最小割就是割掉违背和谐的吧 type arr=record toward,next,cap:longint; end; const ma ...