yaffs2文件系统镜像分析
概述
yaffs2文件系统镜像通过mkyaffs2img工具制作,由源码可编译出两个镜像工具mkyaffsimage和mkyaffs2image,其中mkyaffsimage是针对yaffs文件系统,而mkyaffs2image是针对yaffs2文件系统(yaffs和yaffs2区别可参考官方文档HowYaffsWorks.pdf)。
mkyaffs2image用法:
mkyaffsimage: image building tool for YAFFS built Apr 29 2008
usage: mkyaffsimage dir image_file [convert]
dir the directory tree to be converted
image_file the output file to hold the image
'convert' produce a big-endian image from a little-endian machine
mkyaffs2image的原理为顺次读取文件系统中文件,形成chunk(包含extended tags抽象的oob数据),然后所有chunk组成最后的镜像文件(包含packed tags,物理存储的oob数据)。
默认编译出来的mkyaffs2image烧写到Nandflash中不能正确识别,与Nandflash应用中oob的布局有关系。
大页Nandflash的每一页page内包含2K字节的Data area(数据存储区域,带内数据),包含64字节的Spare area(备用区域,带外数据,out-of-band,oob)。由于不用应用中oob布局不同,导致直接采用官方源码编译出来的mkyaffs2image实际不能应用。下面是一nandflash器件K9F2G08UOC的配置,以下说明均依次为例说明,硬件平台为am335x。
page: 2K=2048 bytes
oob: 64 bytes
block: 128K = 64page
oob介绍
这里主要说明oob部分;Data area根据不同版本代码稍有不同,但变化不大。
yaffs中oob数据用tags表示,extended tags是抽象RAM数据结构,packedtags是物理存储结构。
在源码yaffs_packedtags2.h中定义了yaffs2中tags结构体:
//yaffs_packedtags2.h
struct yaffs_packed_tags2_tags_only {
unsigned seq_number;
unsigned obj_id;
unsigned chunk_id;
unsigned n_bytes;
};
struct yaffs_packed_tags2 {
struct yaffs_packed_tags2_tags_only t;
struct yaffs_ecc_other ecc;
};
//yaffs_ecc.h
struct yaffs_ecc_other {
unsigned char col_parity;
unsigned line_parity;
unsigned line_parity_prime;
};
在yaffs_packed_tags2中的yaffs_ecc_other仅对tags进行ECC校验,不对数据区ECC校验。
sizeof(yaffs_packed_tags2) = 28。
采用官方源码编译而得的mkyaffs2image,占用oob区域前28字节。实际测试,文件系统不能正常启动(找不到文件系统或不识别)。并且,烧写后的block均标记为bad block。此时u-boot下通过nand scrub命令可使坏块重新利用。
官方源码oob打包代码:
static void shuffle_oob(char *spareData, struct yaffs_packed_tags2 *pt)
{
assert(sizeof(*pt) <= spareSize);
// NAND LAYOUT: For non-trivial OOB orderings, here would be a good place to shuffle.
memcpy(spareData, pt, sizeof(*pt));
}
static int write_chunk(u8 *data, u32 id, u32 chunk_id, u32 n_bytes)
{
struct yaffs_ext_tags t;
struct yaffs_packed_tags2 pt;
char spareData[spareSize];
if (write(outFile,data,chunkSize) != chunkSize)
fatal("write");
memset(&t, 0, sizeof(t));
t.chunk_id = chunk_id;
// t.serial_number = 0;
t.serial_number = 1; // **CHECK**
t.n_bytes = n_bytes;
t.obj_id = id;
t.seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
// added NCB **CHECK**
t.chunk_used = 1;
if (convert_endian)
{
little_to_big_endian(&t);
}
nPages++;
memset(&pt, 0, sizeof(pt));
yaffs_pack_tags2(NULL, &pt,&t,1);
memset(spareData, 0xff, sizeof(spareData));
shuffle_oob(spareData, &pt);
if (write(outFile,spareData,sizeof(spareData)) != sizeof(spareData))
fatal("write");
return 0;
}
yaffs镜像查看方法
生成的文件系统镜像文件可通过命令查看具体内容:
hexdump –C yaffs2image | more
一页信息展示如下:
~$hexdump -C wang_yaffs2 | more
00000000 03 00 00 00 01 00 00 00 ff ff 74 6d 70 00 00 00 |..........tmp...|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000100 00 00 00 00 00 00 00 00 00 00 ff ff ed 41 00 00 |.............A..|
00000110 00 00 00 00 00 00 00 00 29 c4 e8 57 32 68 fa 56 |........)..W2h.V|
00000120 41 bb e4 57 ff ff ff ff ff ff ff ff ff ff ff ff |A..W............|
00000130 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
000001c0 ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00 |................|
000001d0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000800 ff ff 00 10 00 00 01 01 00 00 00 00 00 00 ff ff |................|
00000810 00 00 25 00 00 00 00 00 00 00 ff ff ff ff ff ff |..%.............|
00000820 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
*
00000840 03 00 00 00 01 00 00 00 ff ff 73 62 69 6e 00 00 |..........sbin..|
说明:上图表示的红色部分为yaffs2 tags数据,前面两个字符用作坏块标记(后面具体说明)。该镜像文件有默认源码mkyaffs2image基础上oob部分头两字节空置为0xFF所得。实际测试nandflash烧写该镜像后,文件系统仅启动一部分,之后停止不再显示信息。
后注:程序停止的原因为mkyaffs2image.c(即制作镜像工具)问题,其在打包镜像时,对于普通文件大小设置错误。在文件头中设置文件大小有file_size_low和file_size_high两部分组成(对普通文件有效),而对于普通文件,c中都通过stat.st_size>>32设置,而stat.st_size本身为32位,右移32位实际值仍为原值,导致文件设置大小过大。导致运行第一个文件时即停止不再运行。
而在yaffs镜像烧写到nandflash后可通过如下命令查看flash内存储的数据信息(包含oob)
nand dump 0x780000
一页信息展示如下:
U-Boot# nand dump 780000
Page 00780000 dump:
03 00 00 00 01 00 00 00 ff ff 74 6d 70 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 ff ff ed 41 00 00
e8 03 00 00 e8 03 00 00 85 b0 cf 57 48 1b d0 57
85 b0 cf 57 ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff 00 00 00 00
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
OOB:
ff ff df 97 11 29 97 c3
fd 0e 2d 4b 49 7f 7c 00
10 ae d1 f6 12 6c 65 3d
68 86 1a db 4a 00 10 ae
d1 f6 12 6c 65 3d 68 86
1a db 4a 00 10 ae d1 f6
12 6c 65 3d 68 86 1a db
4a 00 ff ff ff ff ff ff
说明:上述读出的一页数据中oob红色标记数据为yaffs2 tags,前面两个字节是坏块标识,后面字节包含data区ECC。该镜像有mkyaffs2image-128M制作而成,实际测试,可正常使用。
mkyaffs2image修改
由于自己编译的mkyaffs2image工具制作镜像不能正常运行,花费很长很长时间确定问题。网上查阅很多资料,想查看mkyaffs2image-128M源码不得,但比对生成的镜像可推测mkyaffs2image-128M原理。
通过比对mkyaffs2image-128M和自编译工具制作的镜像,除了上述所述“普通文件大小设置”问题外,还获知如下信息:
- 128M版本多一个nand page,即第一页page。查看page内容,可知该页内容为根目录头,注意文件名不是“.”或NULL而是“”。该页存不存在关系不大,128M版本镜像删除第一页,自编译镜像不含根目录头仍可正常工作。
- 两者oob tags ECC对非普通文件相同,但对普通文件128M版本1-3字节总是0xFF 0xFF 0xFF,而自编译版本总是0x00 0x00 0x00。不知原因。
- 通过yaffs自带的ecc算法,可获得oob中含有data ECC(位置40-63)。data ecc每256字节生成3字节,三字节中两版本oob前两个字节都是对调,不知原因。
- 128M版本镜像tags ECC与data ECC间总有数据填充,不知具体算法。
- 128M版本镜像没有pad_image,即没有用0xFF补全最后块。
网上介绍了好多ecclayout的源码修改,其实际并没有在源码中增加data ECC,而只是确定tags( ECC)位置。而原理很简单,就是oob偏移两个字节存储tags ECC,前两个字节为坏块标记(0xFF 0xFF)。
对于大页,data ecc是连续的,oob的布局简言之:
两字节坏块标记(0xFF 0xFF)+ tags (含ECC, 28字节) +data ECC(开始位置有ecclayout确定)。
#define oob_tags_start 2
#define oob_ecc_start 40
static void shuffle_oob(char *spareData, struct yaffs_packed_tags2 *pt)
{
assert(sizeof(*pt) <= spareSize);
// NAND LAYOUT: For non-trivial OOB orderings, here would be a good place to shuffle.
// memcpy(spareData, pt, sizeof(*pt));
memcpy(spareData + oob_tags_start, pt, sizeof(*pt));
printf("tags size is %x\n", sizeof(*pt));
printf("(pt->t).seq_number = %x\n", (pt->t).seq_number);
printf("(pt->t).obj_id = %x.\n", (pt->t).obj_id);
printf("(pt->t).chunk_id = %x.\n", (pt->t).chunk_id);
printf("(pt->t).n_bytes = %x.\n", (pt->t).n_bytes);
printf("(pt->ecc).col_parity = %x.\n", (pt->ecc).col_parity);
printf("(pt->ecc).line_parity = %x.\n", (pt->ecc).line_parity);
printf("(pt->ecc).line_parity_prime = %x.\n", (pt->ecc).line_parity_prime);
}
static void data_ecc(u8 *data, u8 *ecc)
{
int i = 0;
for(i = 0; i * 256 < chunkSize; i++)
{
yaffs_ecc_calc(data + i*256, ecc + i*3);
}
}
static int write_chunk(u8 *data, u32 id, u32 chunk_id, u32 n_bytes)
{
struct yaffs_ext_tags t;
struct yaffs_packed_tags2 pt;
char spareData[spareSize];
if (write(outFile,data,chunkSize) != chunkSize)
fatal("write");
memset(&t, 0, sizeof(t));
t.chunk_id = chunk_id;
// t.serial_number = 0;
t.serial_number = 1; // **CHECK**
t.n_bytes = n_bytes;
t.obj_id = id;
t.seq_number = YAFFS_LOWEST_SEQUENCE_NUMBER;
// added NCB **CHECK**
t.chunk_used = 1;
if (convert_endian)
{
little_to_big_endian(&t);
}
nPages++;
memset(&pt, 0, sizeof(pt));
yaffs_pack_tags2(NULL, &pt,&t,1);
memset(spareData, 0xff, sizeof(spareData));
shuffle_oob(spareData, &pt);
// data_ecc(data, spareData + oob_ecc_start); //software data ecc
#if 1
int i = 0;
unsigned char para = 0;
printf("the oob data: \n");
for(i = 0; i < spareSize; )
{
para = (unsigned char)spareData[i++];
printf("%02x ", para);
if(i % 8 == 0)
printf(" ");
if(i %16 == 0)
printf("\n");
}
#endif
if (write(outFile,spareData,sizeof(spareData)) != sizeof(spareData))
fatal("write");
return 0;
}
static void object_header_little_to_big_endian(struct yaffs_obj_hdr* oh)
{
oh->type = SWAP32(oh->type); // GCC makes enums 32 bits.
oh->parent_obj_id = SWAP32(oh->parent_obj_id); // int
oh->sum_no_longer_used = SWAP16(oh->sum_no_longer_used); // u16 - Not used, but done for completeness.
// name = skip. Char array. Not swapped.
oh->yst_mode = SWAP32(oh->yst_mode);
// Regular POSIX.
oh->yst_uid = SWAP32(oh->yst_uid);
oh->yst_gid = SWAP32(oh->yst_gid);
oh->yst_atime = SWAP32(oh->yst_atime);
oh->yst_mtime = SWAP32(oh->yst_mtime);
oh->yst_ctime = SWAP32(oh->yst_ctime);
oh->file_size_low = SWAP32(oh->file_size_low); // Aiee. An int... signed, at that!
oh->file_size_high = SWAP32(oh->file_size_high); // Aiee. An int... signed, at that!
oh->equiv_id = SWAP32(oh->equiv_id);
// alias - char array.
oh->yst_rdev = SWAP32(oh->yst_rdev);
oh->win_ctime[0] = SWAP32(oh->win_ctime[0]);
oh->win_ctime[1] = SWAP32(oh->win_ctime[1]);
oh->win_atime[0] = SWAP32(oh->win_atime[0]);
oh->win_atime[1] = SWAP32(oh->win_atime[1]);
oh->win_mtime[0] = SWAP32(oh->win_mtime[0]);
oh->win_mtime[1] = SWAP32(oh->win_mtime[1]);
oh->reserved[0] = SWAP32(oh->reserved[0]);
// oh->reserved[1] = SWAP32(oh->reserved[1]);
oh->inband_shadowed_obj_id = SWAP32(oh->inband_shadowed_obj_id);
oh->inband_is_shrink = SWAP32(oh->inband_is_shrink);
oh->shadows_obj = SWAP32(oh->shadows_obj);
oh->is_shrink = SWAP32(oh->is_shrink);
}
u-boot烧写
setenv ipaddr 192.168.2.110;setenv serverip 192.168.2.222;saveenv;
run erase_rootfs;tftp 81000000 wang_yaffs2;nand write.yaffs 81000000 780000 420000
setenv bootargs noinitrd root=/dev/mtdblock7 init=/linuxrc console=ttyO0,115200n8 rootfstype=yaffs2 mem=253M;setenv bootcmd nandecc hw 2\; nand read.i ${kloadaddr} ${nand_src_addr} ${nand_img_siz}\;nandecc hw 0\; nand read.i ${logoaddr} ${splashimage} 0x80000\; bootm ${kloadaddr};saveenv