Exynos4412 Uboot 移植(四)—— Uboot引导内核过程分析

bootloader 要想启动内核,可以直接跳到内核的第一个指令处,即内核的起始地址,这样便可以完成内核的启动工作了。但是要想启动内核还需要满足一些条件,如下所示:

1、cpu 寄存器设置

    * R0 = 0
    * R1 = 机器类型 id
    * R2 = 启动参数在内存中的起始地址

2、cpu 模式

    * 禁止所有中断
    * 必须为SVC(超级用户)模式

3、Cache、MMU

    * 关闭 MMU
    * 指令Cache可以开启或者关闭
    * 数据Cache必须关闭

4、设备

    * DMA 设备应当停止工作

5、PC为内核的起始地址

     

      这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。bootm 向将内核映像从各种媒介中读出,存放在指定的位置;然后设置标记列表给内核传递参数;最后跳到内核的入口点去执行。


Uboot版本:u-boot-2013.01

一、bootm命令用法介绍如下:

       在 common/cmd_bootm.c 中可以看到bootm 的定义:


可以看到 bootm 命令使调用了do_bootm 函数

do_bootm 函数

在cmd_bootm.c 第586行可以看到do_bootm函数的定义(为方便阅读,对其中一些代码进行了删减,完整代码请阅读uboot源码):

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. /*******************************************************************/  
  2. /* bootm - boot application image from image in memory */  
  3. /*******************************************************************/  
  4.   
  5. int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  
  6. {  
  7.     ulong       iflag;  
  8.     ulong       load_end = 0;  
  9.     int     ret;  
  10.     boot_os_fn  *boot_fn;  
  11.   
  12.     if (bootm_start(cmdtp, flag, argc, argv))// 获取镜像信息  
  13.         return 1;  
  14.   
  15.     iflag = disable_interrupts(); // 关闭中断  
  16.   
  17.     usb_stop();// 关闭usb设备  
  18.   
  19.     ret = bootm_load_os(images.os, &load_end, 1);//加载内核  
  20.   
  21.     lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));  
  22.   
  23.     if (images.os.type == IH_TYPE_STANDALONE) {//如有需要,关闭内核的串口  
  24.         if (iflag)  
  25.             enable_interrupts();  
  26.         /* This may return when 'autostart' is 'no' */  
  27.         bootm_start_standalone(iflag, argc, argv);  
  28.         return 0;  
  29.     }  
  30.   
  31.     boot_fn = boot_os[images.os.os];//获取启动参数  
  32.   
  33.     arch_preboot_os();//启动前准备  
  34.   
  35.     boot_fn(0, argc, argv, &images);//启动,不再返回  
  36.   
  37. #ifdef DEBUG  
  38.     puts("\n## Control returned to monitor - resetting...\n");  
  39. #endif  
  40.     do_reset(cmdtp, flag, argc, argv);  
  41.   
  42.     return 1;  
  43. }  
该函数的实现分为 3 个部分:

a -- 首先通过 bootm_start 函数分析镜像的信息;

b -- 如果满足判定条件则进入 bootm_load_os 函数进行加载;

c -- 加载完成后就可以调用 boot_fn 开始启动。

1、bootm_start

在cmd_bootm.c 第193行可以看到bootm_start函数的定义, 主要作用是填充内核相关信息

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  
  2. {  
  3.     void        *os_hdr;  
  4.     int     ret;  
  5.   
  6.     memset((void *)&images, 0, sizeof(images));  
  7.     images.verify = getenv_yesno("verify");//获取环境变量  
  8.   
  9.     boot_start_lmb(&images);  
  10.   
  11.     bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");  
  12.   
  13.     /*获取镜像头,加载地址,长度 */  
  14.     os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,  
  15.             &images, &images.os.image_start, &images.os.image_len);  
  16.     if (images.os.image_len == 0) {  
  17.         puts("ERROR: can't get kernel image!\n");  
  18.         return 1;  
  19.     }  
  20.   
  21.     /*获取镜像参数*/  
  22.     switch (genimg_get_format(os_hdr)) {  
  23.     case IMAGE_FORMAT_LEGACY:  
  24.         images.os.type = image_get_type(os_hdr);//镜像类型  
  25.         images.os.comp = image_get_comp(os_hdr);//压缩类型  
  26.         images.os.os = image_get_os(os_hdr);//系统类型  
  27.   
  28.         images.os.end = image_get_image_end(os_hdr);//镜像结束地址  
  29.         images.os.load = image_get_load(os_hdr);/加载地址  
  30.         break;  
  31.   
  32.     /* 查询内核入口地址*/  
  33.     if (images.legacy_hdr_valid) {  
  34.         images.ep = image_get_ep(&images.legacy_hdr_os_copy);  
  35.   
  36.     } else {  
  37.         puts("Could not find kernel entry point!\n");  
  38.         return 1;  
  39.     }  
  40.   
  41.     if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {  
  42.         images.os.load = images.os.image_start;  
  43.         images.ep += images.os.load;  
  44.     }  
  45.   
  46.     if (((images.os.type == IH_TYPE_KERNEL) ||  
  47.          (images.os.type == IH_TYPE_KERNEL_NOLOAD) ||  
  48.          (images.os.type == IH_TYPE_MULTI)) &&  
  49.         (images.os.os == IH_OS_LINUX)) {  
  50.         /* 查询是否存在虚拟磁盘 */  
  51.         ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,  
  52.                 &images.rd_start, &images.rd_end);  
  53.         if (ret) {  
  54.             puts("Ramdisk image is corrupt or invalid\n");  
  55.             return 1;  
  56.         }  
  57.   
  58. #if defined(CONFIG_OF_LIBFDT)  
  59.         /* 找到设备树,设备树是linux 3.XX版本特有的 */  
  60.         ret = boot_get_fdt(flag, argc, argv, &images,  
  61.                    &images.ft_addr, &images.ft_len);  
  62.         if (ret) {  
  63.             puts("Could not find a valid device tree\n");  
  64.             return 1;  
  65.         }  
  66.   
  67.         set_working_fdt_addr(images.ft_addr);  
  68. #endif  
  69.     }  
  70.   
  71.     images.os.start = (ulong)os_hdr;//赋值加载地址  
  72.     images.state = BOOTM_STATE_START;//更新状态  
  73.   
  74.     return 0;  
  75. }  
该函数主要进行 镜像的有效性判定、校验、计算入口地址 等操作,大部分工作通过  boot_get_kernel -> image_get_kernel  完成。


2、bootm_load_os

      在cmd_bootm.c 第317行可以看到bootm_load_os函数的定义, 这个函数主要判断镜像是否需要解压,并且将镜像移动到加载地址

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)    
  2. {    
  3.     uint8_t comp = os.comp;         /* 压缩格式 */    
  4.     ulong load = os.load;           /* 加载地址 */    
  5.     ulong blob_start = os.start;    /* 镜像起始地址 */    
  6.     ulong blob_end = os.end;        /* 镜像结束地址 */    
  7.     ulong image_start = os.image_start;    /* 镜像起始地址 */    
  8.     ulong image_len = os.image_len;        /* 镜像长度 */    
  9.     uint unc_len = CONFIG_SYS_BOOTM_LEN;   /* 镜像最大长度 */    
  10.     
  11.     const char *type_name = genimg_get_type_name (os.type);  /* 镜像类型 */    
  12.     
  13.     switch (comp) {  /* 选择解压格式 */    
  14.     case IH_COMP_NONE:  /* 镜像没有压缩过 */    
  15.         if (load == blob_start) {   /* 判断是否需要移动镜像 */    
  16.             printf ("   XIP %s ... ", type_name);    
  17.         } else {    
  18.             printf ("   Loading %s ... ", type_name);    
  19.     
  20.             if (load != image_start) {    
  21.                 memmove_wd ((void *)load, (void *)image_start, image_len, CHUNKSZ);    
  22.             }    
  23.         }    
  24.         *load_end = load + image_len;    
  25.         puts("OK\n");    
  26.         break;    
  27.     case IH_COMP_GZIP:  /* 镜像采用 gzip 解压 */    
  28.         printf ("   Uncompressing %s ... ", type_name);    
  29.         if (gunzip ((void *)load, unc_len, (uchar *)image_start, &image_len) != 0) {  /* 解压 */    
  30.             puts ("GUNZIP: uncompress, out-of-mem or overwrite error "    
  31.                 "- must RESET board to recover\n");    
  32.             return BOOTM_ERR_RESET;    
  33.         }    
  34.     
  35.         *load_end = load + image_len;    
  36.         break;    
  37.     ...    
  38.     default:    
  39.         printf ("Unimplemented compression type %d\n", comp);    
  40.         return BOOTM_ERR_UNIMPLEMENTED;    
  41.     }    
  42.     puts ("OK\n");    
  43.     debug ("   kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);    
  44.     
  45.     if ((load < blob_end) && (*load_end > blob_start)) {    
  46.         debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end);    
  47.         debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end);    
  48.         return BOOTM_ERR_OVERLAP;    
  49.     }    
  50.     
  51.     return 0;    
  52. }    


3、do_bootm_linux

在bootm_load_os 执行结束后,回到do_bootm 函数,调用boot_fn 运行linux 内核;


boot_os 为函数指针数组,在cmd_bootm.c 136行有定义


可以看出 boot_fn 函数指针指向的函数是位于 arch/arm/lib/bootm.c的 do_bootm_linux,这是内核启动前最后的一个函数,该函数主要完成启动参数的初始化,并将板子设定为满足内核启动的环境,代码如下:


可以看到 do_bootm_linux 实际调用的是 boot_jump_linux 函数


4、boot_jump_linux 

在arch/arm/lib/bootm.c 下第326行有定义

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. /* Subcommand: GO */  
  2. static void boot_jump_linux(bootm_headers_t *images)  
  3. {  
  4.     unsigned long machid = gd->bd->bi_arch_number;//获取机器码  
  5.     char *s;  
  6.     void (*kernel_entry)(int zero, int arch, uint params);//内核入口函数  
  7.     unsigned long r2;  
  8.   
  9.     kernel_entry = (void (*)(intint, uint))images->ep;  
  10.   
  11.     s = getenv("machid");//从环境变量中获取机器码  
  12.     if (s) {  
  13.         strict_strtoul(s, 16, &machid);  
  14.         printf("Using machid 0x%lx from environment\n", machid);  
  15.     }  
  16.   
  17.     debug("## Transferring control to Linux (at address %08lx)" \  
  18.         "...\n", (ulong) kernel_entry);  
  19.     bootstage_mark(BOOTSTAGE_ID_RUN_OS);  
  20.     announce_and_cleanup();  
  21.   
  22. #ifdef CONFIG_OF_LIBFDT  
  23.     if (images->ft_len)  
  24.         r2 = (unsigned long)images->ft_addr;  
  25.     else  
  26. #endif  
  27.         r2 = gd->bd->bi_boot_params;//将启动参数地址赋给 r2   
  28.   
  29.     kernel_entry(0, machid, r2);  
  30. }  

kernel_entry(0, machid, r2) 

真正将控制权交给内核, 启动内核;

满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 ;第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址 。


二、为内核设置启动参数

        Uboot 也是通过标记列表向内核传递参数,标记在源代码中定义为tag,是一个结构体,在arch/arm/include/asm/setup.h 中定义。


tag_header 结构体定义如下:



     在一些内存标记、命令行标记的示例代码就是取自Uboot 中的 setup_memory_tags、setup_commandline_tag函数,他们都是在 arch/arm/lib/bootm.c中定义。

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #if defined(CONFIG_SETUP_MEMORY_TAGS) || \  
  2.     defined(CONFIG_CMDLINE_TAG) || \  
  3.     defined(CONFIG_INITRD_TAG) || \  
  4.     defined(CONFIG_SERIAL_TAG) || \  
  5.     defined(CONFIG_REVISION_TAG)  
  6. static void setup_start_tag (bd_t *bd)  
  7. {  
  8.     params = (struct tag *)bd->bi_boot_params;  
  9.   
  10.     params->hdr.tag = ATAG_CORE;  
  11.     params->hdr.size = tag_size (tag_core);  
  12.   
  13.     params->u.core.flags = 0;  
  14.     params->u.core.pagesize = 0;  
  15.     params->u.core.rootdev = 0;  
  16.   
  17.     params = tag_next (params);  
  18. }  
  19. #endif  
  20.   
  21. #ifdef CONFIG_SETUP_MEMORY_TAGS  
  22. static void setup_memory_tags(bd_t *bd)  
  23. {  
  24.     int i;  
  25.   
  26.     for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {  
  27.         params->hdr.tag = ATAG_MEM;  
  28.         params->hdr.size = tag_size (tag_mem32);  
  29.   
  30.         params->u.mem.start = bd->bi_dram[i].start;//物理内存起始地址  
  31.         params->u.mem.size = bd->bi_dram[i].size;//物理内存结束地址  
  32.   
  33.         params = tag_next (params);  
  34.     }  
  35. }  
  36. #endif  
  37.   
  38. #ifdef CONFIG_CMDLINE_TAG  
  39. static void setup_commandline_tag(bd_t *bd, char *commandline)  
  40. {  
  41.     char *p;  
  42.   
  43.     if (!commandline)  
  44.         return;  
  45.   
  46.     /* eat leading white space */  
  47.     for (p = commandline; *p == ' '; p++);  
  48.   
  49.     /* skip non-existent command lines so the kernel will still 
  50.      * use its default command line. 
  51.      */  
  52.     if (*p == '\0')  
  53.         return;  
  54.   
  55.     params->hdr.tag = ATAG_CMDLINE;  
  56.     params->hdr.size =  
  57.         (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;  
  58.   
  59.     strcpy (params->u.cmdline.cmdline, p);  
  60.   
  61.     params = tag_next (params);  
  62. }  
  63. #endif  


一般有 setup_memory_tags、setup_commandline_tag 这两个标记就可以了,在配置文件Include/configs/fs4412.h中定义:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Exynos4412 Uboot 移植(三)—— Uboot添加自定义命令

Uboot添加自定义命令&#xff1a;uboot中的命令使用U_BOOT_CMD这个宏声明来注册进系统&#xff0c;链接脚本会把所有的cmd_tbl_t结构体放在相邻的地方。 UBoot版本&#xff1a;u-boot-2013.01 一、U-Boot命令的格式 即使是内核的启动&#xff0c;也是通过U-Boot命令来实现的。…

Exynos4412 Uboot 移植(二)—— Uboot 启动流程分析

uboot启动流程分析如下&#xff1a; 第一阶段&#xff1a; a -- 设置cpu工作模式为SVC模式 b -- 关闭中断&#xff0c;mmu,cache v -- 关看门狗 d -- 初始化内存&#xff0c;串口 e -- 设置栈 f -- 代码自搬移 g -- 清bss h -- 跳c 第二阶段 a -- 初始化外设&#xff0c;进入超…

Linux内核学习四库全书

http://blog.csdn.net/21aspnet/article/details/6585602 关于内核学习我建议不要上来就读内核而是先了解内核的构成和特性&#xff0c;然后通过思考发现疑问这时再去读内核源码。即先了解概貌在读局部细节。而且内核分成好多部分&#xff0c;不要只是按照顺序去读&#xff0c;…

Exynos4412 Uboot 移植(一)—— Uboot 编译流程分析

Uboot 所用版本 u-boot-2013.01 u-boot-2013.01 中有上千文件&#xff0c;要想了解对于某款开发板&#xff0c;使用哪些文件、哪些文件首先执行、可执行文件占用内存的情况&#xff0c;最好的方法就是阅读它的Makefile。 根据顶层Readme文件的说明&#xff1a; 可以知道如果使…

Exynos4412 所用内存 —— DDR2

一、SDRAM 二、DDR 三、DDR2 四、DDR2的配置

Exynos4412启动过程分析

学习Exynos4412启动流程前&#xff0c;我们先看看三星4412芯片启动框图&#xff1a; 我们从图中可以看到4412内部有64K的ROM和256K SRAM&#xff0c;在ROM中已经固化好了一段代码&#xff0c;当硬件上电后首先运行的就是这段代码&#xff0c;这段代码三星起名为BLO&#xff08;…

Exynos4412 所用外存 —— eMMC

Exynos4412所用外存不是原来的Nand Flash 与 Nor Flash&#xff0c;而是eMMC。eMMC是什么呢&#xff1f;和Nand Flash有什么区别呢&#xff1f; 一、eMMC概述 eMMC&#xff08;Embeded MultiMedia Card&#xff09;&#xff1a;它并非是一种全新尺寸的存储卡&#xff0c;而…

mini-uboot 启动过程简单分析

单片机有最小系统&#xff0c;所谓最小系统&#xff0c;就是单片机能正常工作所需要的最少外设。对于Uboot来说&#xff0c;同样有个最小系统&#xff0c;因为Uboot最主要的功能就是引导内核。下面我们通过一个简单的Mini-Uboot来分析Uboot的启动加载过程。&#xff08;只是分析…

MySql中的varchar类型

2019独角兽企业重金招聘Python工程师标准>>> MySql中的varchar类型&#xff08;转&#xff09; 今天新开始的项目在做数据库设计&#xff0c;发现自己对MySql的varchar类型还不熟悉&#xff0c;故又上网收集资料整理如下。 1.varchar类型的变化 MySQL 数据库的va…

进入保护模式(三)内存的分页

2019独角兽企业重金招聘Python工程师标准>>> 一、分页 先入为主理解的分页和系统中内存内应的分页 1.弄过数据库数据查找展示&#xff0c;有个数据分页展示的概念&#xff0c;这个的目的是为了速度、和展示效果上的提升 2.内存的分页又是怎么回事哪&#xff1f; …

C#实现简单的 Ping 的功能,用于测试网络是否已经联通

1 /// <summary>2 /// 是否能 Ping 通指定的主机3 /// </summary>4 /// <param name"ip">ip 地址或主机名或域名</param>5 /// <returns>true 通&#xff0c;false 不通</returns>6 …

Binutils工具集 解析

对于嵌入式系统开发&#xff0c;掌握相应的工具至关重要&#xff0c;它能使我们解决问题的效率大大提高。目前&#xff0c;可以说嵌入式系统的开发工具是GNU的天下&#xff0c;因为来自GNU的GCC编译器支持大量的目标处理器。除了GCC&#xff0c;还有一个非常重要的、同样来自于…

ARM 软中断指令SWI

前面我们学习ARM工作模式中&#xff0c;处理器模式切换可以通过软件控制进行切换&#xff0c;即修改CPSR模式位&#xff0c;但这是在特权模式下&#xff0c;当我们处于用户模式下&#xff0c;是没有权限实现模式转换的。若想实现模式切换&#xff0c;只能由另一种方法来实现&am…

yum install 失败

https://blog.csdn.net/weixin_45621658/article/details/110734514 原因&#xff1a;centos6的默认源在2012年-12月左右被官方搞掉了 下列是错误详情 Bash [rootc8-20 ~]# yum makecache Loaded plugins: fastestmirror Loading mirror speeds from cached hostfile YumRepo …

sphinx

2019独角兽企业重金招聘Python工程师标准>>> ./configure --prefix/usr/local/sphinx --with-mysql/usr/local/mysql make && make install cd /usr/local/sphinx/etc cp sphinx.conf.dist sphinx.conf vim sphinx.conf mysql -u test < /usr/local/sphi…

Exynos4412裸机开发综合练习

下面是一个案例需求&#xff1a; 1、编写一段程序,该程序的主要功能是监控电路板上的电压值,若电压值超过当前的电压限制则通过蜂鸣器报警,通过按键解除报警; 2、其具体要求如下; a) 程序下载20s后,进入电压采集状态(使用RTC ALARM功能完成), 要求1s采集1次电路板电压值;(采用…

Exynos4412 裸机开发 —— IIC总线

前言&#xff1a; I2C(Inter-Integrated Circuit)总线(也称 IIC 或 I2C) 是有PHILIPS公司开发的两线式串行总线&#xff0c;用于连接微控制器及外围设备&#xff0c;是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式&#xff0c;具有接口线少、控制方式…

Exynos4412裸机开发 —— A/D转换器

一、Exynos4412 A/D转换器概述 1、简述 10位或12位CMOS再循环式模拟数字转换器&#xff0c;它具有10通道输入&#xff0c;并可将模拟量转换至10位或12位二进制数。5Mhz A/D 转换时钟时&#xff0c;最大1Msps的转换速度。A/D转换具备片上采样保持功能&#xff0c;同时也支持待机…

Exynos4412裸机开发 —— UART

一、Exynos4412 UART 的特性 Exynos4412 中UART&#xff0c;有4 个独立的通道&#xff0c;每个通道都可以工作于中断模式或DMA 模式&#xff0c;即 UART 可以发出中断或 DMA 请求以便在UART 、CPU 间传输数据。UART 由波特率发生器、发送器、接收器和控制逻辑组成。 使用系统时…

Exynos4412裸机开发 —— 看门狗定时器

一、看门狗定时器概述 看门狗&#xff08;WatchDog Timer) 定时器和PWM的定时功能目的不一样。它的特点是&#xff0c;需要不同的接收信号&#xff08;一些外置看门狗芯片&#xff09;或重新设置计数器&#xff0c;保持计数值不为0。一旦一些时间接收不到信号&#xff0c;或计数…