u-boot引导内核过程

  目标板:2440

  u-boot引导内核启动时,传入内核的参数为bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0

  一、nand read.jffs2 0x30007FC0 kernel

  从nand Flash中将内核读取出来,写到地址为0x30007FC0的内存区域。在嵌入式系统中,Flash是没有分区的,但是为什么在应用过程中,我们总是说Flash已经被分区,那是因为在u-boot源码中,将Flash的空间地址绑定了,比如Flash分为boot区,kernel区,fs区,但是在源码中,只是将boot变量赋予一个地址,将kernel这个变量指向了另一个地址。在代码中的具体体为:/include/configs/

100ask24x0.h

#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k @0(bootloader),"                             "128k(params),"                             "2m(kernel),"                             "-(root)"

  那么内核是如何被读出来的,在/common/cmd_nand.c中do_nand函数中

 /* read write */
 if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0)

  当内核被读取出来之后,就进入下边的过程了。

  二、bootm 0x30007FC0

  在/common/cmd_bootm.c中do_bootm函数中启动内核。启动内核的时候,首先对内核的头部要进行操作,原因是
在Flash中保存的内核是由两部分构成的,第一部分是头部,第二部分是真正的内核。而头部的结构如下:

typedef struct image_header {
    uint32_t    ih_magic;    /* Image Header Magic Number    */
    uint32_t    ih_hcrc;    /* Image Header CRC Checksum    */
    uint32_t    ih_time;    /* Image Creation Timestamp    */
    uint32_t    ih_size;    /* Image Data Size        */
    uint32_t    ih_load;    /* Data     Load  Address        */
    uint32_t    ih_ep;        /* Entry Point Address        */
    uint32_t    ih_dcrc;    /* Image Data CRC Checksum    */
    uint8_t        ih_os;        /* Operating System        */
    uint8_t        ih_arch;    /* CPU architecture        */
    uint8_t        ih_type;    /* Image Type            */
    uint8_t        ih_comp;    /* Compression Type        */
    uint8_t        ih_name[IH_NMLEN];    /* Image Name        */
} image_header_t;

  ih_load是加载地址,内核在运行的时候先要将内核放在加载地址,比如参数为bootm 0x30007FC0,如果内核不在0x30007FC0地址,那么u-boot在执行bootm 0x30007FC0命令的时候就重新将内核加载到0x30007FC0地址。

  ih_ep是入口地址,若要运行内核,直接跳到ih_ep这个地址就可以运行了。如0x30008000就是内核运行地址。

  在/common/cmd_bootm.c中

  memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);

  该函数是将内核移到地址为ih_load(加载地址)的内存中。

if(ntohl(hdr->ih_load) == data) {
            printf ("   XIP %s ... ", name);
        } else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
            size_t l = len;
            void *to = (void *)ntohl(hdr->ih_load);
            void *from = (void *)data;

            printf ("   Loading %s ... ", name);

            while (l > 0) {
                size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
                WATCHDOG_RESET();
                memmove (to, from, tail);
                to += tail;
                from += tail;
                l -= tail;
            }

  这段代码的解释如下:

  在ih_load和 ih_ep之间,存在的是内核的头部image_header_t。如果ih_load和 ih_ep之间的大小恰好等于内核的头部的大小的话,那么真正的内核就开始从ih_ep运行。不用再次移动内核,这样就可以加快启动速度。如果ih_load和 ih_ep之间的大小不等于内核的头部的大小那么内核就必须重新移动到ih_ep地址,这样就延长启动时间。

  bootm的第一个作用:根据头部将内核移到合适的地方。

  当内核移动到正确的地址之后,启动内核的函数为

case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
        fixup_silent_linux();
#endif
        do_bootm_linux  (cmdtp, flag, argc, argv,
                 addr, len_ptr, verify);
        break;

  进入/lib_arm/armLinux.c文件do_bootm_linux函数

  do_bootm_linux的作用:

  1.启动时会检测内存、Flash……并且将相应的参数告诉内核,设置启动参数。

  启动参数的格式为TAG类型。

  代码中要设置好几个参数,这儿只是选择性的说几个

  setup_start_tag (bd);

  setup_memory_tags (bd); 设置内存的参数

  setup_commandline_tag (bd, commandline);设置启动时传入内核的参数

  setup_end_tag (bd);设置完毕

  跳入函数可得:

static void setup_start_tag (bd_t *bd)
{
    params = (struct tag *) bd->bi_boot_params;

    params->hdr.tag = ATAG_CORE;
    params->hdr.size = tag_size (tag_core);

    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;

    params = tag_next (params);
}

  由代码可以得到tag是一个结构体,bi_boot_params为0x300000100,ATAG_CORE为54410001

static void setup_memory_tags (bd_t *bd)
{
    int i;

    for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
        params->hdr.tag = ATAG_MEM;
        params->hdr.size = tag_size (tag_mem32);

        params->u.mem.start = bd->bi_dram[i].start;
        params->u.mem.size = bd->bi_dram[i].start;;

        params = tag_next (params);
    }
} 

  bi_dram[i].start;内存的初始地址
  bi_dram[i].start;内存的大小

  这两个参数的初始值在start_armboot()函数中dram_init可以设置。

static void setup_commandline_tag (bd_t *bd, char *commandline)
{
    char *p;
    if (!commandline)
        return;
    /* eat leading white space */
    for (p = commandline; *p == ‘ ‘; p++);
    /* skip non-existent command lines so the kernel will still
     * use its default command line.
     */
    if (*p == ‘\0‘)
        return;
    params->hdr.tag = ATAG_CMDLINE;
    params->hdr.size =
        (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;
    strcpy (params->u.cmdline.cmdline, p);
    params = tag_next (params);
} 

  *commandlinechar 的来源为*commandline = getenv ("bootargs");
  那么在终端所获得的信息是bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
  root=/dev/mtdblock3   根文件系统在Flash中的第三分区
  init=/linuxrc         第一个进程为linuxrc
  console=ttySAC0      内核打印信息从串口输出

static void setup_end_tag (bd_t *bd)
{
    params->hdr.tag = ATAG_NONE;
    params->hdr.size = 0;
}

  参数设置结束的时候将tag设置为空,size设置为0.

  2.跳到入口地址(还是在do_bootm_linux中)。

void (*theKernel)(int zero, int arch, uint params);
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

  thekernel为函数指针指向ih_ep头部的入口地址。

theKernel (0, bd->bi_arch_number, bd->bi_boot_params); 

  调用thekernel直接到入口地址执行内核,此时控制权交给kernel,u-boot的任务就完成了。
  bi_arch_number 为机器ID,内核在启动时会比对是否支持硬件平台。

  bd->bi_boot_params为传入的参数。

  

时间: 2024-08-04 16:27:19

u-boot引导内核过程的相关文章

The Kernel Boot Process.内核引导过程

原文标题:The Kernel Boot Process 原文地址:http://duartes.org/gustavo/blog/ [注:本人水平有限,只好挑一些国外高手的精彩文章翻译一下.一来自己复习,二来与大家分享.] 上一篇文章解释了计算机的引导过程,正好讲到引导装载程序把系统内核镜像塞进内存,准备跳转到内核入口点去执行的时刻.作为引导启动系列文章的最后一篇,就让我们深入内核,去看看操作系统是怎么启动的吧.由于我习惯以事实为依据讨论问题,所以文中会出现大量的链接引用Linux 内核2.6

使用centos引导内核错误:kernel: pnp 00:0b: can&#39;t evaluate _CRS: 8

CentOS系统在开机过程中,一直遇到黑屏提示:“kernel: pnp 00:0b: can't evaluate _CRS: 8”,不理会它仍能启动系统并正常工作,未知何故. 经查,这是内核引导的错误,需要修改内核参数,才能避免开机出现这个错误: pnpacpi=off 禁用ACPI的即插即用功能,转而使用古董的PNPBIOS来代替. 具体操作为: [[email protected] ~]# sudo vi /boot/grub/grub.conf # 在内核引导参数的最后,加上这个参数(

mini2440 uboot使用nfs方式引导内核,文件系统

mini2440 uboot使用nfs方式引导内核,文件系统 成于坚持,败于止步 看了一段时间的u-boot了,到今天才真正完全实现u-boot引导内核和文件系统,顺利开机,在此记录完整过程 1.首先如果你u-boot还没有一直也没有关系,这里上传了一个已经移植好的u-boot,版本问2010.03,多谢tekkaman分享的这个源码 分享下载地址:u-boot-2010.03 编译uboot的过程就不在多说了,解压到你希望的目录下执行make,当时事前安装好交叉编译工具,这个相信大家都会 编译

u-boot-2011.06在基于s3c2440开发板的移植之引导内核与加载根文件系统

http://www.linuxidc.com/Linux/2012-09/70510.htm  来源:Linux社区  作者:赵春江 uboot最主要的功能就是能够引导内核启动.本文就介绍如何实现该功能,并组成一个最简单的系统,这不仅要移植uboot,还要移植linux内核及创建一个根文件系统. 首先我们对nandflash进行分区,规划好每个文件存放在nandflash的位置.下面是nandflash的分区: 第0分区:0x000000000000-0x000000080000为uboot区

Tiny4412 u-boot分析(3)u-boot 引导内核流程

在u-boot中,通过bootm命令启动内核.bootm命令的作用是将内核加载到指定的内存地址,然后通过R0.R1.R2寄存器传递启动参数之后启动内核.在启动内核之前需要对环境做一些初始化工作,主要有如下几个方面: (1).cpu 寄存器设置 * R0 = 0 * R1 = 板级 id * R2 = 启动参数在内存中的起始地址 (2).cpu 模式 * 禁止所有中断 * 必须为SVC(超级用户)模式 (3).缓存.MMU * 关闭 MMU * 指令缓存可以开启或者关闭 * 数据缓存必须关闭并且不

&lt;linux是怎么跑的?&gt;傻瓜视角看linux引导启动过程

每天开机关机,除了“等”之外,你得了解你的操作系统开机的时候真正做了什么? 一. 书上都是这么讲的 CPU自身初始化:硬件初始工作,以PC/IP寄存器跳转到BIOS首地址为结束标志. ->加电自检(Power On Self Test):硬件检测,内存检测,系统总线检测,以开始从总线读取第一段程序为结束标志. ->加载内核引导程序:这里是由BIOS确定了引导设备之后,从设备的第一个扇区启动的程序,GRUB的工作就是属于这个过程,以选择完一个启动的系统为结束标志. ->主引导程序:由操作系

rhel7引导启动过程修复

一.首先对MBR和分区表进行备份,并将备份的mbr文件存放在FTP或者U盘中,备份内核文件安装包 [[email protected] ~]#dd if=/dev/sda of=/root/mbr bs=512 count=1 [[email protected] ~]# cd /boot [[email protected] boot]# ls config-3.10.0-123.el7.x86_64                             symvers-3.10.0-123

刷新grub2配置文件,修复Linux默认引导内核

Cent OS 7 服务器重启后发现bbr内核模块没有加载,而这个模块是新版本内核中引入的,使用uname -a查看,系统加载的还是旧版内核. 查看grub.cfg文件,发现系统顺序被打乱了,执行以下命令重新生成该文件 1 grub2-mkconfig -o /boot/grub2/grub.cfg 重启服务器,成功加载新内核,bbr模块启动成功. 参考资料:https://wiki.centos.org/zh/HowTos/Grub2

Uboot 引导内核时加载地址与入口地址问题

如果使用 mkimage 生成内核镜像文件的话,会在内核的前头加上了 64 bytes 的信息头,供建立 tag 之用.bootm 命令会首先判断 bootm xxx 这个指定的地址 xxx 与 -a 指定的加载地址是否相同. 如果不同的话会从这个地址开始提取出这个 64 bytes 的头部,对其进行分析,然后把去掉头部的内核复制到 -a 指定的加载地址去运行; 如果相同的话那就让其原封不同的放在那,但 -e 指定的入口地址会推后 64 bytes,以跳过这 64 bytes 的头部. 我们来看