分享

zImage和uImage的区别(转)

 WUCANADA 2013-03-24

zImage和uImage的区别(转)

(2012-02-03 10:29:18)

zImage和uImage的区别

摘自: http://user.qzone.qq.com/85221810/blog/1247317141
一、vmlinuz
vmlinuz是可引导的、压缩的内核。“vm”代表“Virtual Memory”。Linux 支持虚拟内存,不像老的操作系统比如DOS有640KB内存的限制。Linux能够使用硬盘空间作为虚拟内存,因此得名“vm”。
vmlinuz 的建立有两种方式。一是编译内核时通过“make zImage”创建,然后通过:“cp /usr/src/linux-2.4/arch/i386/linux/boot/zImage/boot/vmlinuz”产生。zImage适用于 小内核的情况,它的存在是为了向后的兼容性。
二 是内核编译时通过命令make bzImage创建,然后通过:“cp/usr/src/linux-2.4/arch/i386/linux/boot/bzImage /boot/vmlinuz”产生。bzImage是压缩的内核映像,需要注意,bzImage不是用bzip2压缩的,bzImage中的bz容易引起 误解,bz表示“big zImage”。 bzImage中的b是“big”意思。 zImage(vmlinuz)和bzImage(vmlinuz)都是用gzip压缩的。它们不仅是一个压缩文件,而且在这两个文件的开头部分内嵌有 gzip解压缩代码。所以你不能用gunzip 或 gzip –dc解包vmlinuz。
二、initrd-x.x.x.img
initrd是“initial ramdisk”的简写。initrd一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。
initrd 映象文件是使用mkinitrd创建的。mkinitrd实用程序能够创建initrd映象文件。这个命令是RedHat专有的。其它Linux发行版或 许有相应的命令。这是个很方便的实用程序。具体情况请看帮助:man mkinitrd下面的命令创建initrd映象文件。

最后生成的内核镜象有两种zImage以及uImage。其中zImage下载到目标板中后,可以直接用uboot的命令go来进行直接跳转。这时候内核直接解压启动。但是无法挂载文件系统,因为go命令没有将内核需要的相关的启动参数传递给内核。传递启动参数我们必须使用命令bootm来进行跳转。Bootm命令跳转只处理uImage的镜象。
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置,入口点在内存的那个位置以及映象名是什么
用法如下:
./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)
参数说明:

-A 指定CPU的体系结构:

取值表示的体系结构
alpha Alpha
arm A RM
x86 Intel x86
ia64 IA64
mips MIPS
mips64 MIPS 64 Bit
ppc PowerPC
s390 IBM S390
sh SuperH
sparc SPARC
sparc64 SPARC 64 Bit
m68k MC68000

-O 指定操作系统类型,可以取以下值:
openbsdnetbsdfreebsd4_4bsdlinuxsvr4esixsolarisirixscodellncrlynxosvxworkspsosqnxu-bootrtemsartos

-T 指定映象类型,可以取以下值:
standalonekernelramdiskmultifirmwarescriptfilesystem

-C 指定映象压缩方式,可以取以下值:
none 不压缩
gzip gzip的压缩方式
bzip2 bzip2的压缩方式

-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载

-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)

-n 指定映象名

-d 指定制作映象的源文件
我在编译时用到的命令如下:
# make zImage      //生成zImage镜象
/usr/local/arm/k9uboot/tools/mkimage -n 'Linux 2.4.27' -A arm -O linux -T
kernel -C none -a 0x20007fc0 -e 0x20008000 -d zImage uImage

内核镜象已经准备好了,这个时候我们就要来准备文件系统了。由于时间缘故,本人暂时采用的是其他人已经好的文件系统k9.img.gz。这个时候我们要做的是,自己写一个简单hello.c的程序,编译通过后加入到该文件系统中,然后下载到目标板中运行。
先编写hello.c
编译:
#/usr/local/arm/2.95.3/bin/arm-linux-gcc –o start-hello hello.c
编译后生成可执行文件start-hello
下面我们就必须把该执行文件加入到文件系统中去,步骤如下:
#gunzip k9.img.gz                     //解压缩
#mount –o loop k9.img /mnt/new_disk    //挂载
#cp start-hello /mnt/new_disk           //将文件拷贝到文件系统中
#cd /mnt/new_disk
#umount /mnt/new_disk                 //卸载
#gzip –c –v9 k9.img > k9.img.gz          //压缩生成最终的文件系统

下面我们就要下载内核以及准备好文件系统了,这边先说明我的内存分配情况如下:
Flash
0x10000000 ――― 0x10020000     boot
0x10020000 ――― 0x10040000     uboot
0x10040000 ――― 0x10060000     uboot env
0x10060000 ――― 0x10200000     kernel
0x10200000 ――― 0x11000000     ramdisk

Sdram
0x20007fc0 ――― 0x20a00000     kernel
0x20a00000 ―――                ramdisk

Loadb 通过串口下载数据到ram
cp.b    拷贝ram中的数据到flash中。

kernel以及文件系统ramdisk下载完毕之后,我们还需要设置uboot的环境变量,这样uboot才能够在上电启动的时候启动内核等操作。环境变量设置如下:
Set cpfltoram cp.b 10200000 20a00000 18ffff            //拷贝文件系统到ram
Set boot bootm 20007fc0                            //启动kernel
Set bootcmd run cpfltoker\;run cpfltoram\;run boot       //uboot复位的执行指令

Set cpfltoker cp.b 10060000 20007fc0 f4fff             //拷贝内核到ram
Set bootargs root=/dev/ram rw initrd=0x20a00000,4M init=/linuxrc console=ttyS0,11520
0,mem=32m                      //uboot传递给内核的启动参数

设置完毕后,saveenv把环境变量存储起来。


学习心得:zImage 和uImage 都是生成的可执行内核镜像文件  
2者在u-boot中启动的方式分别是 go addr 与 bootm addr 来实现启动过程的
即对于zImage是通过 go 来进行引导   而uImage是通过bootm来进行引导的
zImage 和 uImage 2者的关系 是 uImage 是zImage通过mkimage (u--boot下面tools下的工具)来生成的
结果是后者比前者在头部多了64个byte,这多余的64个byte是用来通知给u-boot用的;将相关信息告知u-boot;

这样做的结果在u-boot引导内核时存在2个地址:loadaddress 和entry address 2者的差值刚好是0x40(64byte)的大小
这样在使用bootm loadaddress 时u-boot会根据相应的loadaddress进行调整,有2中情况;
1)、当loadaddress与mkimage时传送的一致时:
          那么在加载 ldr pc,entry address时,会选择mkinage 时的entry地址;即pc=loadaddress + 4;然后由pc来控制流程跳转倒ram中去执行;
2)、当loadaddress与mkimage时传送的不一致时:
          那么,u-bbot会进行地址比较后,将当前的loadaddress减去64byte后,将真正的内核映像(去掉64byte头部的内核)拷贝倒预先制定的loadaddress,然后直接从这个loadaddress来引导内核运行;

总结,那么上面2中情况实际区别呢?其实就是最终代码执行时,如果地址与mkinage时指定的不符,那么u-boot将进行去头后,拷贝内核代码,直接执行;而如果不处理,则会将 loadaddress+0x40来执行内核;
通过tftp服务来下载 zImage或者uImage;
loadb   在tftp不成功的情况下使用  串口来下载内核 希望不要用这个方法
cp【.b\.w\.l】 完成 内存之间 内存向flash之间进行拷贝
最后可以设置 bootcmd 环境变量可以实现 u-boot自动引导内核启动

至于文件系统的2中方式:ramdisk 以及nfs 推荐开发者使用nfs  方便修改;
当使用ramdisk时,
#gunzip k9.img.gz                     //解压缩
#mount –o loop k9.img /mnt/new_disk    //挂载
#umount /mnt/new_disk                 //卸载
#gzip –c –v9 k9.img > k9.img.gz          //压缩生成最终的文件系统

这四条命令不要忘记,对于你来说多么强大
不要你去再建立根文件系统。

此外还有一篇关于二者区别的好文章:
http://www./forum/printthread.php?Cat=&Board=embedded&main=700811&type=post
uboot启动zImage(go)和uImage(bootm)分析

我找了一块龙珠系列mx9328的板子和一个windriver ice,于是想把上面跑起来linux,不过内核启动遇到一些,调试了1个星期,有点收获,准备记录下来,先把之前不明白的zImage和uImage差异写出来。
做个记号
1.之间差异。
bootm 加载linux镜像是加载uIamge,uIamge是由mkimage制作而来,和zIamge的差异是uIamge是zIamge压缩过的,bootm需要先对uIamge解压,解压地址为内核入口地址。当解压完成时uIamge和zIamge 几乎是相同的,具体差异可以论述。uboot目前只能支持uImage启动,不支持zImage启动
2.bootm解压过程
---------------------------------------------------------------------------
## Booting image at 08808000 ...
Image Name: Linux-2.6.14.7
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 989172 Bytes = 966 kB
Load Address: 08008000
Entry Point: 08008000
Verifying Checksum ... OK
OK uboot bootm命令对uIamge的解压操作
---------------------------------------------------------------------------
Starting kernel ...
传递内核参数将控制权交给arch\arm\boot\compressed]head.S
----------------------------------------------------------------------------
如mx1ADS班子内存的起始地址为0x0800_0000,通过tftp 下载到0x0800_8000 +offset ,offset大于0x80_0000,即tftp 0x0880_8000 然后bootm 0x0880_8000 即
将0x0880_8000处的uIamge解压,解压地址即为mkimage 设置的kernel入口地址0x0800_8000。
2.bootm内核启动分析
通过分析uboot把控制权交给kernel的瞬间,我们可以知道bootm启动linux内核,它究竟做了哪些工作。
具体源码在uboot1.16 lib_arm\Armlinux.c do_bootm_linux(),大家有兴趣可以看下源码,这里不仔细分析了,网上资料超多。
void (*theKernel)(int zero, int arch, uint params) 通过对这个函数指针的操作实现控制权从uboot到linux的交接。
该函数可以等价于set pc=0x0800_8000 ,r0=0 ,r1=0xA0(mx1ads 芯片的id),r2=0x08000100(bootargs参数基址)。
我通过仿真器,设置了一个指令断点在0x0800_8000,可以看到该瞬间uboot做的操作。

>BKM>dr //查看当前时刻通用寄存器值
R00 = 00000000 R01 = 000000A0 R02 = 08000100 R03 = 08008000
R04 = 00000000 R05 = 08000124 R06 = 083DC0A9 R07 = 0841BC9C
R08 = 083DBFDC R09 = 083E0260 R10(SL) = 00000000 R11(FP) = 00000002
R12(IP) = 083DBFC0 R13(SP) = 083DBD44 R14(LR) = 08413984 PC = 08008000
CPSR = 600000D3 SPSR = B00000FF

>BKM>di 0x08008000 // 反汇编该地址区域代码
08008000: mov r0,r0
08008004: mov r0,r0
08008008: mov r0,r0
0800800c: mov r0,r0
08008010: mov r0,r0
08008014: mov r0,r0
08008018: mov r0,r0
0800801c: mov r0,r0
08008020: b 0x8008030
08008024: .long 0x16F2818
08008028: andeq r0,r0,r0
0800802c: streqd r1,[pc],-r4
08008030: mov r7,r1
08008034: mov r8,#0
08008038: mrs r2,cpsr
0800803c: tst r2,#3
08008040: bne 0x800804C
08008044: mov r0,#0x17
08008048: swi 0x123456
0800804c: mrs r2,cpsr
08008050: orr r2,r2,#0xC0
08008054: msr cpsr_c,r2
08008058: andeq r0,r0,r0
0800805c: andeq r0,r0,r0
08008060: add r0,pc,#0xC8
08008064: ldmia r0,{r1,r2,r3,r4,r5,r6,r12,r13}
08008068: sub r0,r0,r1
0800806c: beq 0x800809C

>BKM>dml 0x08000100 0x50 //uboot bootargs
08000100: 00000005 54410001 00000000 00000000 ....TA..........
08000110: 00000000 00000004 54410002 04000000 ........TA......
08000120: 08000000 0000000F 54410009 736E6F63 ........TA..snoc
08000130: 3D656C6F 53797474 2C30584D 32353131 =eloSytt,0XM2511
08000140: 386E3030 3D706920 746F6F62 6F722070 8n00=pi toobor p
08000150: 2F3D746F 2F766564 0073666E 00000000 /=to/ved.sfn....
08000160: 00000000 00000000 00000000 00000000 ................
通过仿真器可以很清晰的看到bootm先后完成了2部分工作。大家可以分析bootm源码
1.set bootargs 传递参数
2.set pc=0x0800_8000 ,r0=0 ,r1=0xA0
3.对比zImage 分析
通过tftp 0x0800_8000 命令直接将zImage下载到0x0800_8000地址
此时对0x08008000 反汇编可以得到和arch\arm\boot\compressed\head.S一致的汇编代码,说明这是kernel的入口
08008000: msr cpsr_c,#0xD3
08008004: bl 0x8008150
08008008: mov r10,r5
0800800c: beq 0x8008148
08008010: bl 0x80081B0
08008014: mov r8,r5
08008018: beq 0x8008148
0800801c: bl 0x80080D8
08008020: ldr r13,[pc,#+4]
08008024: add r14,pc,#0x68
08008028: add pc,r10,#0xC
0800802c: andgt r8,r0,r0,asr r0
08008030: andgt r2,r11,r0
08008034: andgt r2,r11,r0
08008038: andgt r2,r12,r0,lsl #1
0800803c: ldrgth r14,[r13],-r12
08008040: andgt r5,r12,r12,asr #1
08008044: mulgts r12,r12,r0
08008048: andgt r5,r11,r12,asr #0x13
0800804c: ldrgtsh r3,[r11],-r8
08008050: sub r3,pc,#0x28
08008054: ldmia r3!,{r4,r5,r6,r7}
08008058: cmp r4,r5
0800805c: cmpne r5,r6
08008060: ldrne r11,[r4],#+4
08008064: strne r11,[r5],#+4
08008068: bne 0x800805C
0800806c: mov r11,#0
08008070: cmp r6,r7
08008074: strcc r11,[r6],#+4
08008078: bcc 0x8008070
0800807c: ldmia r3,{r4,r5,r6,r13}
08008080: str r9,[r4,#+0]
此时用go命令 go 0x08008000,go命令本质就是改变当前pc值,即pc=go 0x08008000
断点位置为 0x08008000 ,使用go 会在 0x08008000处停下来
>BKM>dr //此时通用寄存器值为
R00 = 00000000 R01 = 083E0264 R02 = 000F0000 R03 = 0000000C
R04 = 08008000 R05 = 08808000 R06 = 41129200 R07 = 083E0264
R08 = 08000000 R09 = 18000000 R10(SL) = 00000000 R11(FP) = 00000020
R12(IP) = 08808354 R13(SP) = 088E9464 R14(LR) = 08808298 PC = 08008000
CPSR = 400000D3 SPSR = B00000FF
//uboot bootargs
08000100: 00000000 00000000 00000000 00000000 ................
08000110: 00000000 00000000 00000000 00000000 ................
08000120: 00000000 00000000 00000000 00000000 ................
08000130: 00000000 00000000 00000000 00000000 ................
08000140: 00000000 00000000 00000000 00000000 ................
08000150: 00000000 00000000 00000000 00000000 ................
08000160: 00000000 00000000 00000000 00000000 ................
08000170: 00000000 00000000 00000000 00000000 ................
08000180: 00000000 00000000 00000000 00000000 ................
08000190: 00000000 00000000 00000000 00000000 ................
080001A0: 00000000 00000000 00000000 00000000 ................
这个时候发现kernel无法正确启动zImage
4,通过仿真器对go命令加以改造
a.将通用寄存器值改成
R00 = 00000000 R01 = 000000A0 R02 = 08000100 R03 = 0000000C
R04 = 08008000 R05 = 08808000 R06 = 41129200 R07 = 083E0264
R08 = 08000000 R09 = 18000000 R10(SL) = 00000000 R11(FP) = 00000020
R12(IP) = 08808354 R13(SP) = 088E9464 R14(LR) = 08808298 PC = 08008000
CPSR = 400000D3 SPSR = B00000FF
b.通过仿真器修改 0x08000100 地址的值
sml 0x08000100 00000005 54410001 00000000 00000000 00000000 00000004 54410002 04000000 08000000 0000000F 54410009 736E6F63
sml 0x08000130 3D656C6F 53797474 2C30584D 32353131 386E3030 3D706920 746F6F62 6F722070 2F3D746F 2F766564 0073666E 00000000
>BKM>dml 0x08000100 0x50
08000100: 00000005 54410001 00000000 00000000 ....TA..........
08000110: 00000000 00000004 54410002 04000000 ........TA......
08000120: 08000000 0000000F 54410009 736E6F63 ........TA..snoc
08000130: 3D656C6F 53797474 2C30584D 32353131 =eloSytt,0XM2511
08000140: 386E3030 3D706920 746F6F62 6F722070 8n00=pi toobor p
08000150: 2F3D746F 2F766564 0073666E 00000000 /=to/ved.sfn....
08000160: 00000000 00000000 00000000 00000000 ................
然后让程序执行,这样通过uboot也可以让zImage得以执行。

可见go和bootm差异就是 go只是改写pc值,而bootm传递r0,r1,r2还有bootargs

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多