Ref: http://scyangzhu.wordpress.com/page/3/
Ref: http://www.freeors.com/bbs/forum.php?mod=viewthread&tid=11149
Ref: http://blog.chinaunix.net/space.php?uid=13701930&do=blog&cuid=279896
Ref: http://blog.csdn.net/niuniumenghua/article/details/6490832
Ref: http://blog.csdn.net/niuniumenghua/article/details/6490832
以下这种方法最后合并出的文件不是通常uClinux“认为”的标准镜像文件,而且一定要修改bootload代码。请慎用此方法。
(一)、为什么要合并uzImage.bin和cramfs.bin?
一、为了升级时安全、方便
安全。当系统升级程序拿到升级文件时,它必然要判断该文件合法性。uzImage.bin有crc检查,判断起来很容易,cramfs.bin是不带的,有难度。如果两个合为一个,然后使用加在uzImage.bin上的crc机制,那合法性检查上就简单了。
方便。两个合为一个,升级时只须一个文件。
二、节省flash占用空间
分为uzImage.bin和cramfs.bin,则意味着必须给flash人为地分出两个空间,而且这两个空间大小被事先固定。像4M字节
flash,uzImage.bin占用从0x040000开始的1M字节,cramfs.bin占用从0x140000开始的2,752K字节。
既分在两个区就使得分配时不得不存在一个权衡问题,代码不可能写得“恰好”占用。像对于uzImage.bin,这个版本可能是恰好占用,但代码写多了都
知道,功能上的删、加、修改是不确定的,下个版块可能就要因一个意外修改而导致不得不修改uzImage.bin。为解决这个问题,可以使用在
bootload环境变量中写上两区边界方法,但这种方法怎么说也没只是一个文件时方便,而且再“恰好”也肯定是有一定字节“空隙”。
(二)、如何合并
合并采用的方法分为宿主机链接时和uClinux的bootload加载时两个部分。
注:image.0000.img表示最终形成的单个镜像文件。
宿主机链接时
当拿到uzImage.bin.gz(这里多了个gz,这个文件不是最后mkimage后的,而是在mkimage之前gzip之后)和cramfs.bin,链接时执行:
cat $(cramfs.bin) $(uzImage.bin.gz) > $(uzImage.gz)
./mkimage -A arm -O linux -T kernel -C gzip -a <加载地址> -e
<执行地址> -n "uClinux Kernel for xxxx" -d $(uzImage.gz)
$(IMAGEDIR)/image.0000.img
简单来说,就是在mkimage uzImage.bin.gz之前,先用cat命令合并uzImage.bin.gz和cramfs.bin,合并时cramfs.bin必须放在前头。
cramfs.bin为何要放在前头?
cramfs.bin文件的[4h---7h]四字节存储了该cramfs.bin的文件长度。gzip之后的uzImage.bin.gz无法自识别长度。
至此形成的image.0000.img文件结构
image_header_t:mkImage添加的文件头,固定64字节
cramfs.bin:cramfs.bin部分,它的长度可由偏移68--71字节处得到,假设是cramfs_len。
uzImage.bin:uzImage.bin部分。它在image.0000.img开始址址是64+cramfs_len,它的长度则可以由image_head_t中的有效数据长度字段值减去cramfs_len得到。
uClinux的bootload加载时
加载还是要分两次进行,分别加载uzImage.bin和cramfs.bin。
当运行到要加载uzImage.bin时。开始地址:image.0000.img开始地址加上64+cramfs_len;长度:image_head_t中的有效数据长度字段值减去cramfs_len。
当运行到要加载cramfs.bin时。开始地址:image.0000.img开始地址加上64。长度:image.0000.img偏移处68--71字节处得到的值。
(三)、几点补充
一、检查镜像文件合法性上沿用原先给uzImage.bin的crc机制,可以说不增加较验上开销。
二、在flash向ram加载效率上,只是多了几个判断,和原先分两个文件加载方法上可说一样的执行效率。
=================================================
=================================================
一个实例:对bootload改动
bootload准备好flash和sdram可通信后,它接下执行:
1):把uzImage.bin从flash加载到sdram;
2):把cramfs.bin从flash加载到sdram;
3):把矢量表加载到sdram零处(在u-boot代码中,该0x0地址是一个向量表,第一条指令跳转branch到复位代码start_code。Depend on Processor)
4):运行uClinux内核
bootcmd环境变量表示以上四个过程:
bootcmd=cp.l fc040000 01800000 40000; cp.l fc140000 02000000 AC000; cp.l fc3f8000 0 e;bootm 01800000bootm命令是用来引导经过u-boot的工具mkimage打包后的kernel image的,什么叫做经过u-boot的工具mkimage打包后的kernel image,这个就要看mkimage的代码,看看它做了些什么,虽然我很希望大家不要偷懒,认真地去看看,但是我知道还是有很多人懒得去做这件,那么我 就j将分析mkimage代码后得到的总结告诉大家,mkimage做了些什么,怎么用这个工具。
mkimage的用法
- uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。
- mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么
root@Glym:/tftpboot# ./mkimage
Usage: ./mkimage -l image
-l ==> list image header information
./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 指定操作系统类型,可以取以下值:
openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos
-T 指定映象类型,可以取以下值:
standalone、kernel、ramdisk、multi、firmware、script、filesystem
-C 指定映象压缩方式,可以取以下值:
none 不压缩
gzip 用gzip的压缩方式
bzip2 用bzip2的压缩方式
-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载
-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)
-n 指定映象名
-d 指定制作映象的源文件
cp.l是个复制命令,cp==>copy;l==>long,数量以4字节单位,以上的bootcmd类似下以命令
注:以下的flash地址像fc040000,完全大于了4M/8M,那时因为flash被map后它的起始地址就是0xfc000000,所以fc040000对应的其实是flash上的0x40000地址。
flash-2-mem(0xfc040000, 0x01800000, 0x40000 << 2);
flash-2-mem(0xfc140000, 0x02000000, 0xac000 << 2);
flash-2-mem(0xfc3f8000, 0x00000000, 0xe << 2);
bootm(0x1800000)
以下就是从flash加载到sdram函数。uzImage.bin、cramfs.bin、矢量表被分次调用。
int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong addr, dest, count;
int size;
image_header_t* hdr;
if (argc != 4) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
}
/* Check for size specification.
*/
if ((size = cmd_get_data_size(argv[0], 4)) < 0)
return 1;
addr = simple_strtoul(argv[1], NULL, 16);
addr += base_address;
dest = simple_strtoul(argv[2], NULL, 16);
dest += base_address;
count = simple_strtoul(argv[3], NULL, 16);
if (count == 0) {
puts ("Zero length ???\n");
return 1;
}
//
// 由于使用了把uzImage.bin和cramfs.bin合并到一个文件,而在命令行上又保持不变,就须要修正一些变量意义。
//
if (addr == 0xfc040000) {
// fc040000, flash(uzImage.bin)-->ram
// dest = 0x01008000;
// 读出cramfs.bin文件长度
cramfs_len = *(ulong *)(0xfc040000 + sizeof(image_header_t) + 4);
printf("addr == 0xfc040000, this is uzImage.bin, base_address: %lu, cramfs_len: %lu\n", base_address, cramfs_len);
hdr = (image_header_t*)addr;
count = sizeof(image_header_t) / size; // sizeo = 4, sizeof(image_header_t)一般0x40字节,可以被4整除
// 复制头
while (count-- > 0) {
if (size == 4)
*((ulong *)dest) = *((ulong *)addr);
else if (size == 2)
*((ushort *)dest) = *((ushort *)addr);
else
*((u_char *)dest) = *((u_char *)addr);
addr += size;
dest += size;
}
// 从image_header_t中取出数据长度(count = cramfs.bin + uzImage.bin.gz)
count = SWAP_LONG(hdr->ih_size); // ntohl(ih_size)
// 注意,此时的addr已经变成, 函数参数中的addr + sizeof(image_header_t)
addr = addr + cramfs_len;
count = (count - cramfs_len) / size + 1; // cp.l时,size是4, uzImage.bin有效长度会由gzip压缩机制保证(4M flash)或只要至少比有效多就行(8M flash),这里多复制四个字节总不会出错
#ifndef USE_4M_FLASH
// 对于uzImage.bin来说,为减少字节占用一般使用gzip压缩(大概可以达到2:1压缩率),使整个软件能约束在4M字节以内。
// 因为使用gzip压缩,bootload必须对uzImage.bin进行解压,这个过程有时较耗时间,为减少启动时间宁愿采用不压缩而使用8M字节flash
// 这里就是8M情况
// 对于8M, uzImage.bin没经过压缩, 这里就直接复制到解压后的uzImage.bin应该放到的地址, 省掉do_bootm()中的memcpy
dest = SWAP_LONG(hdr->ih_load);
#endif
} else if ((addr == 0xfc140000) && cramfs_len) {
// fc140000, flash(cramfs.bin)-->ram
// 总是假定uzImage.bin已被下载
printf("addr == 0xfc140000, this is cramfs.bin, base_address: %lu, cramfs_len: %lu\n", base_address, cramfs_len);
addr = 0xfc040000 + sizeof(image_header_t); // addr要回到flash开始内核开始处: 0xfc040000
count = cramfs_len / size;
} else if (addr == 0xfc3f8000) {
// 矢量表。这个才60字节,直接是以内存数组表示
printf("addr == 0xfc3f8000, this is vector, count: %lu\n", count);
addr = vectordata;
}
while (count-- > 0) {
if (size == 4)
*((ulong *)dest) = *((ulong *)addr);
else if (size == 2)
*((ushort *)dest) = *((ushort *)addr);
else
*((u_char *)dest) = *((u_char *)addr);
addr += size;
dest += size;
}
return 0;
}
=================================================
No comments:
Post a Comment