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. redis 编译安装错误问题

    编译redis安装的时候报错如下: make[1]: [persist-settings] Error 2 (ignored) CC adlist.o/bin/sh: cc: command not ...

  2. 左值&右值

    一.引子 我们所谓的左值.右值,正确的说法应该是左值表达式.右值表达式. 因为C++的表达式不是左值就是右值. 在C中,左值指的是既能够出现在等号左边也能出现在等号右边的表达式,右值指的则是只能出现在 ...

  3. Internet History,Tecchnology and Security

    Internet History Internet Technologe Internet Secure

  4. Where to go from here

    Did you get through all of that content? Congratulations! You've learnt the fundamentals of algorith ...

  5. HDU 2148 Score

    http://acm.hdu.edu.cn/showproblem.php?pid=2148 Problem Description 转眼又到了一年的年末,Lele又一次迎来了期末考试.虽然说每年都要 ...

  6. Java多线程中的join方法

    新建一个Thread,代码如下: package com.thread.test; public class MyThread extends Thread { private String name ...

  7. dwarf是如何处理栈帧的?

    dwarf是如何处理栈帧的? DW_AT_frame_base 表明函数栈帧的起始点 95 < 1><0x000000ca>    DW_TAG_subprogram 96   ...

  8. 使用oledb读取excel表

    string path = "C:\\Users\\aaa\\Desktop\\aa.xls"; string conn = "Provider = Microsoft. ...

  9. Sparse autoencoder implementation 稀疏自编码器实现

    任务:在这个问题中,你将实现稀疏自编码器算法,并且展示它怎么发现边缘是自然图像的一个好的表示. 在文件 sparseae_exercise.zip中,我们已经提供了一些Matlab中的初始代码,你应该 ...

  10. CSS定义input disabled样式

    disabled 属性规定应该禁用 input 元素.被禁用的 input 元素既不可用,也不可点击.可以设置 disabled 属性,直到满足某些其他的条件为止(比如选择了一个复选框等等).然后,就 ...