目录

u-boot(五)内核启动

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

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 可以不使用页对齐,如果使用nand read,需要考虑页对齐或者块对齐,最终会使用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:256k@0(bootloader)," \"128k(params)," \"2m(kernel)," \"-(root)"

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

内核文件格式

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

/** 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表示加载地址,表示内核应该放在哪里,加载地址为0x30008000
  • 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

u-boot分析之内核启动(五)相关推荐

  1. 通过从代码层面分析Linux内核启动来探知操作系统的启动过程

    通过从代码层面分析Linux内核启动来探知操作系统的启动过程 前言说明 本篇为网易云课堂Linux内核分析课程的第三周作业,我将围绕Linux 3.18的内核中的start_kernel到init进程 ...

  2. linux 内核load addr,linux2.4启动分析(1)---内核启动地址的确定 vmlinux LOAD_ADDR ZRELADDR...

    http://blog.chinaunix.net/u/31100/showart_244622.html ================================ Author: taoyu ...

  3. linux内核参数分析,linux内核启动第一阶段分析

    linux内核启动第一阶段分析 http://blog.csdn.net/aaronychen/article/details/2838341 本文的很多内容是参考了网上某位大侠的文章写的<&l ...

  4. Linux内核分析实验3——分析linux内核启动过程

    本文大量内容引用自孟宁老师在<LINUX操作系统分析>课程中的内容 <Linux内核分析>MOOC课程 http://www.xuetangx.com/courses/cour ...

  5. 实验三:跟踪分析Linux内核启动过程

    钟晶晶 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 1.使用qemu ...

  6. 韦东山衔接班——3.4_linux内核启动流程分析之内核启动

    作者:GWD 时间:2018.8.25 一.汇编部分到第一个C之前准备 1.问:内核的工作 答 2.问:为什么有两个head.S 答:其中一个是自解压代码,运行时先解压内核 3.uboot中将参数写入 ...

  7. Linux内核启动流程(待完善)

    文章目录 一.Linux内核自解压过程 二.Linux内核启动第二阶段stage1 2.1.linux系统启动入口函数(stext) 2.2.内核初始化阶段(start_kernel) 2.3.2 r ...

  8. Linux内核启动流程(简介)

    1. vmlinux.lds 首先分析 Linux 内核的连接脚本文件 arch/arm/kernel/vmlinux.lds,通过链接脚本可以找到 Linux 内核的第一行程序是从哪里执行的: 第 ...

  9. 内核启动流程分析(四)源码浅析

    目录 kernel(四)源码浅析 建立工程 启动简析 head.s 入口点 查询处理器 查询机器ID 启动MMU 其他操作 start_kernel 处理命令行 分区 kernel(四)源码浅析 建立 ...

最新文章

  1. 科普长文揭秘生命为何会具有主观能动性
  2. 常用数据库语句(更新)
  3. springboot 使用webflux响应式开发教程(一)
  4. GraphPad Prism 9.0.2 for MacOS 2021最新完美版科研绘图统计软件 附安装使用教程
  5. 一个简单的if else优化
  6. 常用的python测试脚本_详解Python的单元测试
  7. java代码生成器 快速开发平台 二次开发 外包项目利器 springmvc SSM后台框架源码...
  8. windows安装卸载mysql
  9. aps后缀是什么文件_今日份知识分享:什么是源文件?
  10. ElasticSearch讲解
  11. Ninject学习(一) - Dependency Injection By Hand
  12. 按键精灵和python功能对比_AutoIt3和按键精灵的功能对比第2/2页
  13. Monte Carlo(MC) Policy Evaluation 蒙特·卡罗尔策略评估
  14. 北京林业大学matlab公选课,北京林业大学教务处
  15. html 在线测试 鱼缸,研究员试图用AR鱼缸欺骗鱼的感官系统,结果反被鱼识破
  16. win7右键菜单管理_电脑右键新建不见了怎么办 电脑右键新建不见了解决方法【详解】...
  17. 基于SG90舵机(伺服电机)的操作笔记
  18. 阿里云部署Django项目
  19. 北京晚报:谷歌中国访问量陡增
  20. VTK 学习----VTK对象绘制-点(vtkPoints、vtkSphereSource)、线(vtkLine、vtkLineSource)

热门文章

  1. 面向对象深入:继承03——抽象类
  2. (转) Java线程同步阻塞, sleep(), suspend(), resume(), yield(), wait(), notify()
  3. Spring命名空间引入方法
  4. Android 应用程序之间内容分享详解(二)
  5. T-Sql 实现类似访问数组变量的操作
  6. JavaScript存在的原因
  7. matlab矩阵连接图解
  8. 精简版开发工具使用手记2(图解)
  9. java 图片上传
  10. 使用python处理实验数据-yechen_pro_20171231