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:256k@0(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

设置结束标志

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

  1. Linux内核启动流程分析(一)【转】

    转自:http://blog.chinaunix.net/uid-25909619-id-3380535.html 很久以前分析的,一直在电脑的一个角落,今天发现贴出来和大家分享下.由于是word直接 ...

  2. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  3. 第三节 - centos 内核启动、救援模式、 ls 、目录结构

    Linux 第三节一.CentOS 启动: 1.内核引导: 1.win/linux 通电,2.BISO自检(CPU,内存,硬盘等 | U盘.光驱.网卡.硬盘启动 通过MBR知道内核内存硬件驱动位置并加 ...

  4. ARM-Linux内核移植之(一)——内核启动流程分析

    内核版本:2.6.22  为什么要采用这样一个较低的版本进行移植了,因为韦东山大牛说了,低版本的才能学到东西,越是高版本需要移植时做的工作量越少,学的东西越少. 内核启动分为三个阶段,第一是运行hea ...

  5. linux内核启动修复

    linux内核启动修复 首先看一下linux内核重要文件grub.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # gru ...

  6. v87.01 鸿蒙内核源码分析 (内核启动篇) | 从汇编到 main () | 百篇博客分析 OpenHarmony 源码

    本篇关键词:内核重定位.MMU.SVC栈.热启动.内核映射表 内核汇编相关篇为: v74.01 鸿蒙内核源码分析(编码方式) | 机器指令是如何编码的 v75.03 鸿蒙内核源码分析(汇编基础) | ...

  7. linux内核启动以及文件系统的加载过程

    Linux 内核启动及文件系统加载过程 当u-boot 开始执行 bootcmd 命令,就进入 Linux 内核启动阶段.普通 Linux 内核的启动过程也可以分为两个阶段.本文以项目中使用的 lin ...

  8. Linux内核启动分析过程-《Linux内核分析》week3作业

    环境搭建 环境的搭建参考课件,主要就是编译内核源码和生成镜像 start_kernel 从start_kernel开始,才真正进入了Linux内核的启动过程.我们可以把start_kernel看做平时 ...

  9. 【转载】linux内核启动android文件系统过程分析

    主要介绍linux 内核启动过程以及挂载android 根文件系统的过程,以及介绍android 源代码中文件系统部分的浅析. 主要源代码目录介绍Makefile (全局的Makefile)bioni ...

随机推荐

  1. Qt QTimer

    QTimer类提供了重复和单次触发信号的定时器. QTimer类为定时器提供了一个高级别的编程接口.很容易使用:首先,创建一个QTimer,连接timeout()信号到适当的槽函数,并调用start( ...

  2. mysql 测试php连接问题

    <?php$servername = "shuhua.dbhost";$username = "shuhua_user";$password = &quo ...

  3. visual studio 显示引用关系 作者更改项

    visual studio 2017中,每个类或者方法顶部会显示此方法的引用关系或者作者更改项:这个功能极大了提高了我们代码的定位效率:不过有时候却发现每一行代码都有,会增加我们屏幕显示内容,有时候看 ...

  4. 洛谷P3381 最小费用最大流

    费用流板子 还是一道板子题..先练练手 #include <bits/stdc++.h> #define INF 0x3f3f3f3f #define full(a, b) memset( ...

  5. 用递归方法判断字符串是否是回文(Recursion Palindrome Python)

    所谓回文字符串,就是一个字符串从左到右读和从右到左读是完全一样的.比如:"level" .“aaabbaaa”. "madam"."radar&quo ...

  6. JS 实现DIV 滚动至顶部后固定

    JS 实现DIV 滚动至顶部后固定 <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8" ...

  7. virtio,macvtap,sriov

    本文转自http://xiaoli110.blog.51cto.com/1724/1558984 一个完整的数据包从虚拟机到物理机的路径是: 虚拟机--QEMU虚拟网卡--虚拟化层--内核网桥--物理 ...

  8. [2017-8-02]Android Learning Day8

    自定义动画效果 新建一个customAnim类 package com.liwenchi.myapplication; import android.view.animation.Animation; ...

  9. 【Linux】linux中文本操作利器grep,awk,sed

    grep命令 grep(global search regular expression)是一种强大的文本搜索工具,它可以使用正则表达式搜索文本,并把匹配的行打印出来.平时搜索文本中内容的时候是非常方 ...

  10. 解决mysql配置文件my.cnf添加max_connections不生效

    问题描述: 最新为了方便测试,通过mysql官方指定的yum源安装了mysql5.6.40,在向mysql的配置文件my.cnf添加max_connections=3600后,重启mysql后发现不生 ...