热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

UBOOT引导LINUX内核过程卡死STARTINGKERNEL...(下载地址,加载地址,入口地址的修改)(UIMAGE和ZIMAGE的区别)

1、UIMAGE和ZIMAGE的区别make之后会生成三个文件,一个vmlinux,一个Image,一个zImage,vm

1、UIMAGE和ZIMAGE的区别

make 之后会生成三个文件,一个vmlinux ,一个Image,一个zImage,vmlinux是ELF格式的不能直接运行,Image 就是使用 objcopy 取消掉 vmlinux 中的一些其他信息得到的,而zImage为内核的一种进一步映像压缩文件,Image大约为4M,而zImage不到2M。

然后make uImage就会生成 uImage文件,uImage顾名思义,u代表uboot,
所以uImage是uboot引导linux的镜像文件,使用bootm命令是只支持uImage镜像的 ,
如果要引导zImage镜像需要修改uboot do_bootm函数

uImage是用mkimage工具根据zImage制作而来的,其实就是加了如下64字节头部信息 ,

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;

2、下载地址,加载地址,入口地址,移植内核重点


下载地址

我们在uboot环境变量里面一般会有这么一句,
boocmd=movi read kernel 0x46000000,bootm 0x46000000

它的意思就是把内核下载(读)到0x46000000地方,0x46000000就是下载地址


入口地址,和加载地址

typedef struct image_header {uint32_t ih_load; /* Data Load Address加载地址 */ uint32_t ih_ep; /* Entry Point Address入口地址 */
} image_header_t;

编译的时候可以指定加载地址

图中可以看出加载地址 = 入口地址


入口地址,和加载地址相同的情况下

我们知道,uboot在引导linux内核前,会检查下载地址和加载地址是否相同,
如果不同,就需要把内核拷贝从下载地址拷贝到加载地址,注意,拷贝部分是去了掉头部的zImage,
如果相同,就不作拷贝

1、在加载地址和入口地址相同 且 下载地址和加载地址不同情况下:

此时可以正常启动

2、在加载地址和入口地址相同 且 下载地址和加载地址相同情况下:

此时无法正常启动,因为此时uboot不会去掉头部64字节,如果直接从入口地址(==加载地址)运行肯定会失败,正确的做法就是不要让下载地址和加载地址相同

注意:下载地址和加载地址不同时,两个地址差值尽量大一点,

否则在uboot重新定位内核的时候可能会出现覆盖的情况


入口地址,加载地址不相同的情况下

图片来源:https://www.cnblogs.com/Oude/p/12217750.html

(180----->140)

1、入口地址和加载地址不同(正常是相差64直接),下载地址和加载地址相同

正常启动,因为此时uboot不会去掉头部也不做拷贝,然后正好从加载地址+64字节的入口地址启动内核

2、入口地址和加载地址不同,下载地址和加载地址不同的情况

无法正常启动,因为此时uboot会去掉头部64字节拷贝到加载地址,但是去掉64字节已经就是zImage了,在从加载地址+64字节地方启动就不是正在的入口地址了


入口地址,加载地址修改方式

/tools/mkimage.c解析


-A ==> set architecture to ‘arch’

-O ==> set operating system to ‘os’

-T ==> set image type to ‘type’ “kernel或是ramdisk”

-C ==> set compression type ‘comp’

−a==>setloadaddressto′addr′(hex)指定加载地址

−e==>setentrypointto′ep′(hex)指定内核入口运行地址

-n ==> set image name to ‘name’

-d==> use image data from ‘datafile’

-x ==> set XIP (execute in place,即不进行文件的拷贝,在当前位置执行)


 

 


vim ./scripts/Makefile.lib 修改Makfile.lib文件


译信息如下:
说明上面的改法是对的


3、UBOOT引导UIMAGE过程

​​​​在这里插入图片描述
​​

int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
#ifdef CONFIG_NEEDS_MANUAL_RELOCstatic int relocated = 0;return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |BOOTM_STATE_LOADOS |
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGHBOOTM_STATE_RAMDISK |
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_MIPS)BOOTM_STATE_OS_CMDLINE |
#endifBOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |BOOTM_STATE_OS_GO, &images, 1);
}

bootm_start(cmdtp, flag, argc, argv); 对内核头部分析

bootm_find_other(cmdtp, flag, argc, argv); 查找内存中是否有设备树文件或者是文件系统

bootm_load_os(images, &load_end, 0); 加载内核到加载地址处,如果下载地址和加载地址相同则不需要加载


int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],int states, bootm_headers_t *images, int boot_progress)
{if (states & BOOTM_STATE_START)ret = bootm_start(cmdtp, flag, argc, argv);if (!ret && (states & BOOTM_STATE_FINDOS))ret = bootm_find_os(cmdtp, flag, argc, argv);if (!ret && (states & BOOTM_STATE_FINDOTHER))ret = bootm_find_other(cmdtp, flag, argc, argv); //包括查找设备树,和文件系统/* Load the OS */if (!ret && (states & BOOTM_STATE_LOADOS)) {ret = bootm_load_os(images, &load_end, 0); }/* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGHif (!ret && (states & BOOTM_STATE_RAMDISK)) {ret = boot_ramdisk_high(&images->lmb, images->rd_start,}}
#endif
#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)if (!ret && (states & BOOTM_STATE_FDT)) {boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,&images->ft_len); //设备树方式启动}
#endif/* From now on, we need the OS boot function */if (ret)return ret;boot_fn = bootm_os_get_boot_func(images->os.os);ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); //调用do_bootm_linux函数跳转}}int do_bootm_linux(int flag, int argc, char * const argv[],bootm_headers_t *images)
{boot_prep_linux(images);boot_jump_linux(images, flag);return 0;
}

kernel e​ntry传递参数启动内核

static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,void *res2);int fake = (flag & BOOTM_STATE_OS_FAKE_GO);kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,void *res2))images->ep; //得到入口函数地址if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)r2 = (unsigned long)images->ft_addr; //得到设备树地址elser2 = gd->bd->bi_boot_params;kernel_entry(0, machid, r2); //给内核传递三个参数,机器码,设备树地址}
#endif
}

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------


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

https://www.cnblogs.com/GyForever1004/p/8485079.html

如果使用 mkimage 生成内核镜像文件的话,会在内核的前头加上了 64 bytes 的信息头,供建立 tag 之用。bootm 命令会首先判断 bootm xxx 这个指定的地址 xxx 与 -a 指定的加载地址是否相同。

如果不同的话会从这个地址开始提取出这个 64 bytes 的头部,对其进行分析,然后把去掉头部的内核复制到 -a 指定的加载地址去运行;
如果相同的话那就让其原封不同的放在那,但 -e 指定的入口地址会推后 64 bytes,以跳过这 64 bytes 的头部。
我们来看看这两种不同的情况:

1) mkimage -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -n linux-3.0.2 -d zImage uImage

这种情况,只能把 uImage download 到 0x30008000 的位置上,否则从 0x30008040 是启动不了的。

原因:如果将 uImage (加了头的镜像文件)下载到不同于指定加载地址的地方,则会进行上面的操作,将去掉头部的内核拷贝到指定的加载地址,此时加载地址和入口地址需要是相同的,因为已经没有镜像头了,所以此时入口地址也应该为 0x30008000,而不应该再加上 64 个字节。所以在构建镜像头部中的加载地址和入口地址时千万要考虑下载的地址,否则将会启动失败。

2) mkimage -A arm -O linux -T kernel -C none -a 30008000 -e 30008000 -n linux-3.0.2 -d zImage uImage

这种情况 download 地址随便。 还是按上面说的,因为将加载地址和入口地址设置成同样的地址,在下载到任意地址时,将去掉头部的内核镜像拷贝到指定加载地址后,可以直接从加载地址开始启动。但是要是下载地址和指定加载地址相同呢?也就是下面的:

如果 tftp 下载地址为 0x30008000 , 此时因为下载地址和指定加载地址相同,所以就不会搬动,内核直接从指定加载地址自解压,但是因为指定的入口地址也是 0x30008000,这样的话内核就不会正常启动,所以还得将入口地址往后推后 64 个字节从 0x30008040 启动就能 OK 。

所以在配置下载地址和入口地址是就有以下两种情况:

1) mkimage -n 'linux' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008000 -d zImage uImage加载地址和入口地址相同tftp 0x31000000 uImagebootm 0x31000000下载地址可以任意放(除了下载地址与加载地址相同的情况)。2) mkimage -n 'linux' -A arm -O linux -T kernel -C none -a 0x30008000 -e 0x30008040 -d zImage uImage入口地址在加载地址后面64个字节tftp 0x30008000 uImagebootm 0x30008000下载地址一定要在指定的加载地址上。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

uboot引导linux内核镜像(uImage)启动时,会有2个地址


  • 加载地址(Load Address),即内核镜像整体要放置的内存空间位置
  • 入口地址(Entry Point),即从内核镜像中开始执行的地址

示意图如下,
在这里插入图片描述
其中,内核镜像的加载地址是100,入口地址是180,也就是说内核镜像本身要加载到内存地址为100的地方,然后从地址180开始执行内核代码(一般是_start指示的tag位置)。

uboot启动内核的步骤是


  • 先把内核镜像uImage拷贝到内存某个位置上
  • 然后使用bootm命令去启动内存里的内核镜像

如果我们没有把内核镜像uImage拷贝到指定的加载地址,那么bootm就会把内核镜像在内存中移动到指定的加载地址上,然后再去从内核的入口地址开始执行。

加载地址和入口地址在uImage的开头部分有定义,这是编译内核镜像时指定的。


推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • Linux磁盘的分区、格式化的观察和操作步骤
    本文介绍了如何观察Linux磁盘的分区状态,使用lsblk命令列出系统上的所有磁盘列表,并解释了列表中各个字段的含义。同时,还介绍了使用parted命令列出磁盘的分区表类型和分区信息的方法。在进行磁盘分区操作时,根据分区表类型选择使用fdisk或gdisk命令,并提供了具体的分区步骤。通过本文,读者可以了解到Linux磁盘分区和格式化的基本知识和操作步骤。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 本文介绍了求解gcdexgcd斐蜀定理的迭代法和递归法,并解释了exgcd的概念和应用。exgcd是指对于不完全为0的非负整数a和b,gcd(a,b)表示a和b的最大公约数,必然存在整数对x和y,使得gcd(a,b)=ax+by。此外,本文还给出了相应的代码示例。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了一些好用的搜索引擎的替代品,包括网盘搜索工具、百度网盘搜索引擎等。同时还介绍了一些笑话大全、GIF笑话图片、动态图等资源的搜索引擎。此外,还推荐了一些迅雷快传搜索和360云盘资源搜索的网盘搜索引擎。 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
author-avatar
程璇
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有