u-boot(五)内核启动

目录

  • u-boot(五)内核启动

    • 概述
    • 分区空间
    • 内核文件格式
    • 内核复制跳转
    • 内核启动
    • 机器ID
    • 启动参数
      • (起始tag)setup_start_tag
      • 内存设置
      • 根文件系统,启动程序,串口设备
      • (结束)setup_end_tag


title: u-boot(五)内核启动

tags: linux

date: 2018-09-26 19:58:05

---

u-boot(五)内核启动

概述

启动命令:bootcmd=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0,具体代码实现的重点是以下:

  1. s=getenv ("bootcmd") 获取环境变量
  2. run_command (s, 0); 启动内核,这个s=nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0
  3. s就是先读出内核,再启动内核了

备注 jffs2是一种文件格式,在这里并不需要文件格式,但是使用这个jffs2 可以不使用页对齐,如果使用nandread,需要考虑页对齐或者块对齐,最终会使用nand_read_opts

  • 我们也可以在u-boot 命令行直接输入boot来启动内核,但是实际的命令是一样的,是在cmd_bootm.c中调用do_bootd>run_command (getenv ("bootcmd"), flag)

分区空间

常见的内部空间布局如下:

Bootloader Boot parameters Kernel Root filesystem
u-boot,它会在内存的某个地方存放着内核启动的一些参数,也称为tag u-boot 参数,包含传递给内核的一些东西 内核 根文件系统

嵌入式的FLASH没有实际的分区,所谓分区只是一个名称,具体的地址是写死的. 在include/configs/100ask24x0.h

#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:[email protected](bootloader),"                             "128k(params),"                             "2m(kernel),"                             "-(root)"

这里定义了mtdparts分区,位于nandflash0bootloader大小是256k,从0开始,然后是128k大小的params,接下去是2M的kernel内核,剩余的都是root文件系统.

内核文件格式

Flash上存储的内核格式为uImage,包含了一个头部加真正的内核.

/*
 * all data in network byte order (aka natural aka bigendian)
 */
#define IH_NMLEN        32  /* Image Name Length        */

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表示加载地址,表示内核应该放在哪里
  • ih_ep表示入口地址,表示跳转的地址,也就是内核代码段的入口,广义上的main入口

内核复制跳转

bootm会先判断内核是否在加载地址,否则先移动内核到指定的加载地址,然后跳转。

命令中0x30007FC0 地址可以随便放,只要不破坏已经用到的信息就好, bootm命令如果发现当前内核并不在加载地址,需要移动内核到加载地址。do_bootm函数中memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len); 移动内核。

所以如果ih_load=我们内核的地址的时候,就不需要move,节省时间.jz2440 的加载地址是0x30008000,头部是64字节,所以,0x30008000-64=0x30007FC0,所以我们copy内核到0x30007FC0 .

内核启动

//在 bootm命令中有linux内核跳转,//lib_arm/armlinux.c-->do_bootm_linux
do_bootm_linux  (cmdtp, flag, argc, argv,addr, len_ptr, verify);
    //theKernel 就是uimage的头部中的入口地址
    -theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
    // 设置一些参数
    setup_start_tag (bd);
    setup_memory_tags (bd);
    setup_commandline_tag (bd, commandline);
    setup_end_tag (bd);
    // 所以内核的入口参数
    -theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

机器ID

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);中的第二个参数是机器ID,内核通过比对机器ID判断是否支持启动.gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

启动参数

内核跳转之前,同样需要设置内核的启动参数.内核的参数是按照tag组织的.也就是在某个地址(0x30000100,在100ask24x0.c中定义),按照某种格式存储,这种格式具体为【size....tagid....tag值】

do_bootm_linux中有设置内存,命令行参数等,代码片段如下

bd_t *bd = gd->bd;
//设置起始的头
setup_start_tag (bd);
//设置内存
setup_memory_tags (bd);
setup_commandline_tag (bd, commandline);
//....
// 设置结束的id
setup_end_tag (bd);

具体有以下几种tag,代码中以联合体定义,这样方便使用同一个指针指向它,方便之处见setup_start_tag分析.

//这个tag 就是一个包含了所有类型tag的一个联合体,是实际tag的内容值
struct tag {
    struct tag_header hdr;
    union {
        struct tag_core     core;
        struct tag_mem32    mem;
        struct tag_videotext    videotext;
        struct tag_ramdisk  ramdisk;
        struct tag_initrd   initrd;
        struct tag_serialnr serialnr;
        struct tag_revision revision;
        struct tag_videolfb videolfb;
        struct tag_cmdline  cmdline;

        /*
         * Acorn specific
         */
        struct tag_acorn    acorn;

        /*
         * DC21285 specific
         */
        struct tag_memclk   memclk;
    } u;
};

(起始tag)setup_start_tag

static void setup_start_tag (bd_t *bd)
{
    // 这个tag 就是一个包含了所有类型tag的一个联合体
    // 使用联合体之后,下面就可以使用 params->具体的tag类型
    params = (struct tag *) bd->bi_boot_params;

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

    //tag_core 也就是接下去这三个参数了
    //tag_size =zise + tag + 实际的值
    params->u.core.flags = 0;
    params->u.core.pagesize = 0;
    params->u.core.rootdev = 0;
    //指向下一个参数
    params = tag_next (params);
}

#define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)  ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
// 这里 tag_header就是 size+tag   , type 就是实际的tag的内容
// tag_size就是包含 id 和 size 和 内容的大小了

因为bd_t *bd = gd->bd;,所以搜索下gd->bd->bi_boot_params,也就是在board/100ask24x0/100ask24x0.c中定义,也就是说参数是放在0x30000100.

gd->bd->bi_boot_params = 0x30000100;

内存设置

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].size;

        params = tag_next (params);
    }
}

搜索下gd->bd->bi_dram[0],同样在board/100ask24x0/100ask24x0.c定义

int dram_init (void)
{
    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

    return 0;
}
#define PHYS_SDRAM_1        0x30000000 /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE   0x04000000 /* 64 MB */

这个函数是在lib_arm/board.c中的init_sequence调用,也就是start_armboot中调用,也就是在u-boot(三)第一阶段的C中使用的

根文件系统,启动程序,串口设备

char *commandline = getenv ("bootargs");
setup_commandline_tag (bd, commandline);

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

设置命令tag,多了参数commandline,源自环境变量bootargs查看下环境变量bootargs,使用print查看,也可搜索下代码

"bootargs=" CONFIG_BOOTARGS         "\0"
//include/configs/100ask24x0.h
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
  • root=/dev/mtdblock3表示根文件系统从第四个FLASH分区开始(从0开始计数)可以往上看分区空间
  • init=/linuxrc指示第一个应用程序
  • console=ttySAC0,内核打印信息从串口0 打印

(结束)setup_end_tag

设置结束标志

原文地址:https://www.cnblogs.com/zongzi10010/p/10023681.html

时间: 2024-10-18 13:09:43

u-boot(五)内核启动的相关文章

内核启动过程

具体要求如下: Grub的源码分析:Grub如何支持多个系统和内核的启动选择(MultiBoot机制). 一.GRUB 简介(介绍主流的系统管理程序grub) 什么是GRUB: GNU GRUB 是一个多重操作系统启动管理器.GNU GRUB 是由GRUB(GRand Unified Bootloader) 派生而来.GRUB 最初由Erich Stefan Boleyn 设计和应用: "boot loader"是计算机启动后第一个运行的软件.它负责加载一个操作系统的内核,并把控制权交

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Linux内核启动及根文件系统载入过程

上接博文<u-boot之u-boot-2009.11启动过程分析> Linux内核启动及文件系统载入过程 当u-boot開始运行bootcmd命令,就进入Linux内核启动阶段.与u-boot类似,普通Linux内核的启动过程也能够分为两个阶段,但针对压缩了的内核如uImage就要包含内核自解压过程了.本文以linux-2.6.37版源代码为例分三个阶段来描写叙述内核启动全过程.第一阶段为内核自解压过程,第二阶段主要工作是设置ARM处理器工作模式.使能MMU.设置一级页表等,而第三阶段则主要为

Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 【转】

原文地址:Linux内核源码分析--内核启动之(3)Image内核启动(C语言部分)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938390.html 在构架相关的汇编代码运行完之后,程序跳入了构架无关的内核C语言代码:init/main.c中的start_kernel函数,在这个函数中Linux内核开始真正进入初始化阶段, 下面我就顺这代码逐个函数的解释,但是这里并不会过于深入

ubuntu 更新内核切换内核启动

1. 查看需要更新的内核命令: apt-cache search linux 该命令将会显示所有可以获取的内核 2. 安装内核,假设你要安装的内核为2.6.39-0,则使用下面的命令 sudo apt-get install linux-headers-2.6.39-0-generic linux-image-2.6.39-0-generic 安装后,reboot即可,重启后,既是以新内核启动 ===================================================

Linux内核启动及文件系统加载过程

上接博文<u-boot之u-boot-2009.11启动过程分析> 当u-boot开始执行bootcmd命令,就进入Linux内核启动阶段,与u-boot类似,普通Linux内核的启动过程也可以分为两个阶段,但针对压缩了的内核如uImage就要包括内核自解压过程了.本文以项目中使用的linux-2.6.37版源码为例分三个阶段来描述内核启动全过程.第一阶段为内核自解压过程,第二阶段主要工作是设置ARM处理器工作模式.使能MMU.设置一级页表等,而第三阶段则主要为C代码,包括内核初始化的全部工作

轻松识破linux内核启动过程中的“”套路“”

内核启动流程相关的内容让很多热爱linux的小伙伴既爱又恨,因为这是了解linux系统基本构造的良好过程同时由于其本身复杂且底层,脑子中的脉络不是很清晰,本文就总结了一些优秀博文,以自己的理解来解构一下. 本文的环境是CentOS 6.8, 基本过程: 庖丁解牛: 1.POST BIOS的功能由两部分组成, 步骤1:上电自检POST(Power-on self test),主要负责检测系统外围关键设备(如:CPU.内                 存.显卡.I/O.键盘鼠标等)是否正常.例如,

Linux内核启动分析

张超<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 我的代码可见https://www.shiyanlou.com/courses/reports/986221 在这里我们用的是linux-3.18.6版本,以下简写成linux. start_kernel在 /linux/init/main.c中定义: 这个函数是内核由引导程序引导以后,由自解压程序解压以后执行的第一个函数,可以认为是整个内核的入口函数,以后我

I.MX6Q(TQIMX6Q/TQE9)学习笔记——内核启动与文件系统挂载

经过前面的移植,u-boot已经有能力启动内核了,本文主要来看下如何通过之前移植的u-boot来启动内核.如果按照前面的文章完成了LTIB 的编译,那么,Linux的内核应该就会出现rpm/BUILD/目录下,接下来,我们就开始移植这个3.0.35版本的内核到TQIMX6Q. 内核的编译 为了简化内核编译的过程,可以在内核目录下创建编译脚本,命名为build.sh,内容如下: [cpp] view plaincopy #!/bin/sh export ARCH=arm export CROSS_