通江网站建设,wordpress搬家缩略图都没,企业查询卡,网站添加百度地图以下内容源于朱有鹏嵌入式课程的学习与整理#xff0c;如有侵权请告知删除。
一、uboot作用简介 uboot的主要作用是用来启动linux内核。 CPU不能直接从块设备中执行代码#xff0c;因此需要把块设备中的程序复制到内存中#xff0c;而复制之前还需要进行很多初始化工作如有侵权请告知删除。
一、uboot作用简介 uboot的主要作用是用来启动linux内核。 CPU不能直接从块设备中执行代码因此需要把块设备中的程序复制到内存中而复制之前还需要进行很多初始化工作如时钟、串口、dram等。这些初始化工作由uboot完成。初始化工作完成后uboot把块设备中的内核代码复制到内存地址0x30008000地址处然后再执行bootm 0x30008000命令以启动内核代码。 二、uboot启动内核的流程总结 开机时uboot出现倒计时当没有按键操作时uboot会读取bootcmd这个环境变量并使用rum_command函数来执行这个命令。 实质执行“movi read kernel 30008000bootm 30008000”。其中“movi read kernel”表示把sd卡中的kernel分区赋值到30008000内存处“bootm 30008000”表示真正的传参以及跳转到linux内核中执行实际执行的是do_bootm()函数。 函数do_bootm()首先判断内核镜像类型zImage、uImage、设备树然后再把必需的信息linux操作系统、ep的值等储存起来最后调用do_bootm_linux函数来对内核传参并且启动内核。 三、vmlinuz、Image、zImage、uImage文件 1vmlinuz、vmlinux文件 Linux内核代码经过编译链接生成一个elf格式的可执行文件即vmlinuz或者vmlinux。 此文件不能直接烧录。 vmlinuz.elf文件大小为70M以上。 关于vmlinuz与vmlinux的区别见博客 Linux内核之vmlinux与vmlinuz - ldxsuanfa - 博客园 vmlinux vmlinuz zimage uimage 区别_suo_guang的博客-CSDN博客 vmlinux,vmlinuz,bzimage,zimage,initrd.img的区别与联系 - 走看看 2zImage文件 vmlinuz文件经过arm-linux-objcopy后生成一个Image镜像文件其大小为7M左右。 Image文件经过进一步压缩并添加解压缩代码形成zImage文件。 因此zImage文件中除了linux内核镜像以外还有一些头文件以及解压代码所以内核实际处在addr地址再加一个偏移量的位置。当zImage文件作为启动镜像时首先要解压可以由uboot解压或者zImage文件本身自解压。 3uImage文件 uImage是uboot专用的启动内核镜像基本上属于过时的技术。 新一点的技术是设备树的启动方式。 四、uboot启动内核的源码分析
1、分析start_armboot()函数末尾的main_loop()函数 main_loop()函数中有一段代码如下。 通过getenv()函数获取环境变量bootcmd的值然后通过run_command()函数来执行。其中bootcmdmovi read kernel 30008000;bootm 30008000。movi read kernel 30008000表示把sd卡中kernel分区复制到30008000内存地址处。bootm 30008000表示到内存地址30008000处执行代码。执行bootm命令实际执行do_bootm函数。2、分析do_bootm()函数 do_bootm()函数位于uboot的/common/cmd_bootm.c文件中。片段代码如下。 1上述代码分析 因为 “bootm 30008000”所以argc2走的是else路线argv[1]30008000这是字符串要将字符串转化为ulong数字simple_strtoul()完成此功能。接着判断0x30008000偏移36字节以后的地址中的值如果为0x016f2818说明启动镜像为zImage则输出“boot with zImage”。因为没有开启虚拟地址映射功能所以virt_to_phys(x)其实就是地址x。接着填充结构体变量hdr。主要填充两个成员ih_osIH_OS_LINUXih_epntohl(addr)。前者表示内核的操作系统类型后者表示内核代码的地址。ntohl()函数将一个无符号长整形数从网络字节顺序转换为主机字节顺序比如将0x12345678转换成0x78563412。然后把hdr复制一份到image.legacy_hdr_os_copy中即把内存地址0x30008000处设置好的zImage头复制一份到uboot的data段。然后直接跳转到after_header_check处 判断操作系统调用do_bootm_linux函数。2关于结构体变量hdr其类型是image_header_t 该结构体描述的是内核镜像的头部信息。 3关于结构体变量image其类型是bootm_header_t 3、分析do_bootm_linux()函数 do_bootm_linux()函数位于uboot的/lib_arm/bootm.c文件中。 1代码片段1与分析 获取环境变量bootargs的值。判断全局变量images中的legacy_hdr_valid是否为1为1则获取ep值。把ep强制类型换成函数指针类型赋值给thekernel。获取环境变量machid的值赋值给s如果s不为空则变量machid 环境变量中machid的值打印machid。2代码片段2与分析配置传参信息 这段代码是uboot在配置将来传递给内核的信息。uboot把与硬件有关的信息传给linux内核比如memory信息多少bank、size、起始地址、命令行信息、lcd 串口、initrd、MTD等信息。如要定义了任意一个CONFIG_XXXXX则执行setup_start_tag(bd)。A、初始化第一块tag 1代码流程分析 params (struct tag *) bd-bi_boot_params; 这句代码把bd-bi_boot_params强制转换为stuct tag* 类型然后赋值给params。bd是uboot中的全局变量gd的成员gd-bd-bi_boot_params (PHYS_SDRAM_10x100)这说明把“PHYS_SDRAM_10x100”这个地址设置为传参的起始地址。然后给hdr.tag 与hdr.size赋值接着给联合体中的结构体参数赋值。从#define tag_next(t) ((struct tag *)((u32 *)(t) (t)-hdr.size))可以看出params指向下一个params即把params移动sizeof(tag_core)大小继续赋值。总结这块tag表示一个开始除了说明传参的起始地址没什么其他实际的信息。2上面的变量bd的类型是bd_t。 3上面的变量params的类型是“static struct tag * ”。 由此可知此结构体由一个stuct tag_header类型的结构体加上一个由一系列结构体组成的union联合体组成。这一系列结构体中存放的就是与board有关的参数。 结构体struct tag_header定义如下。 struct tag_header {u32 size;u32 tag;
}; B、传递内存参数setup_memory_tags函数 记录内存中每个bank的信息包括每个bank的起始地址和大小。 C、传递命令行参数setup_commandline_tag函数 commandline是一个char *类型指向环境变量中的bootargs的值即 #define CONFIG_BOOTARGS root/dev/mtdblock4 rootfstypeyaffs2 init/init consolettySAC0,115200 D、初始化最后一块tag即结束传参。 3代码片段3与分析uboot的最后一句代码 这句代码通过执行thekernel函数直接启动linux内核传递了三个参数0、machid、传参的首地址。这三个参数是通过r0、r1、r2三个寄存器来传递的其中r0传递0r1传递machidr2传递传参的首地址。 注意theKernel()函数定义与赋值的过程如下 void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],bootm_headers_t *images)
{//省略部分代码void (*theKernel)(int zero, int arch, uint params);//定义theKernel函数//省略部分代码if (images-legacy_hdr_valid) {ep image_get_ep (images-legacy_hdr_os_copy);}//省略部分代码theKernel (void (*)(int, int, uint))ep;//将theKernel函数指针赋值为ep//省略部分代码theKernel (0, machid, bd-bi_boot_params);//执行theKernel函数}