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和自编译工具制作的镜像,除了上述所述“普通文件大小设置”问题外,还获知如下信息:

  1. 128M版本多一个nand page,即第一页page。查看page内容,可知该页内容为根目录头,注意文件名不是“.”或NULL而是“”。该页存不存在关系不大,128M版本镜像删除第一页,自编译镜像不含根目录头仍可正常工作。
  2. 两者oob tags ECC对非普通文件相同,但对普通文件128M版本1-3字节总是0xFF 0xFF 0xFF,而自编译版本总是0x00 0x00 0x00。不知原因。
  3. 通过yaffs自带的ecc算法,可获得oob中含有data ECC(位置40-63)。data ecc每256字节生成3字节,三字节中两版本oob前两个字节都是对调,不知原因。
  4. 128M版本镜像tags ECC与data ECC间总有数据填充,不知具体算法。
  5. 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

上一篇:使用Cordova创建第一个App和Cordova构建环境的配置


下一篇:Emgu-WPF 激光雷达研究-移动物体跟踪2