|
原文:
http://www.enseirb.fr/~kadionik/embedded/uclinux/mtd/howto_mtd.html
(中文没有版权,英文参考原文版权)
=======================================
HOW TO USE MTD/JFFS2 UNDER µClinux
(translated by Liang Alei)
Patrice KADIONIK, Professor Assistant at the ENSEIRB School of Electrical Engineering, Telecommunication, and Computer Science
[email protected]
http://www.enseirb.fr/~kadionik
1. MOTIVATIONS
本文将详细描述:怎样在µClinux 中step-by-step地构建一个MTD/JFFS2。
I'm currently teaching embedded system programming and propose to my students practical exercices on. In every
我目前在大学讲授“嵌入式系统编程”,并指导学生“µClinux on Motorola M5407C3 ColFire boards”的实验。一般在每个嵌入式系统中都存在有FLASH memory。通常可以用来存放配置参数(in a raw binary format)。而现在在embedded Linux中,我们可以在FLASH中放置一个文件系统,并在embedded Linux OS启动之后将其mount。这里采用的技术是MTD (Memory Technology Device),MTD将隐藏有关“物理(physical) FLASH”的编程细节(如:读/写/擦除扇区),并在MTD之上放置JFFS(2) 文件系统(Journalling Flash File System)----一种被Linux和µClinux支持的文件系统。JFFS2很robust,即在意外掉电(power failure)之后,再启动(reboot)之时不需要fsck检查。
JFFS JFFS2
MTD
FLASH, RAM
另外一种使用FLASH memory的情况(或用途)是:将Linux kernel放在FLASH中,由bootloader在启动时将Linux kernel解压缩至RAM中,然后转移至RAM引导kernel。(在此我们不将深入讨论该问题)
强烈推荐:在阅读本文这个HOWTO之前,读--再读--多读以下文档:
· The Memory Technology Device (MTD) Subsystem for Linux site. The Linux MTD, JFFS HOWTO.
· JFFS2: The Journalling Flash File System, version 2. What is JFFS2?
· A paper from David Woodhouse on JFFS2
· The David Woodhouse's presentation on JFFS2. He explains how works the Garbage Collector with JFFS2
· The www.embeddedlinuxworks.com site. JFFS- A practical guide. The list of FLASH memories supporting the MTD technology.
· Using Flash Memory with µClinux by Greg Ungerer.
· Find your Root File System with MTD by Phil Wilshire.
· CFI presentation from AMD site
I will also thank here Massimo Calo who helped me through his threads posted to the µClinux news server and his mails on MTD/JFFS.
2. MTD/JFFS/JFFS2 OVERVIEW
下面的内容摘录自我之前阅读的一篇文档(from the www.embeddedlinuxworks.com site, an article from Vipin Malik)。
" 2000年, Axis Communications AB (www.axis.com),发布了第一版的 JFFS文件系统,也是开源的(Open Sourced)。这是一个完全为嵌入式Linux系统设计的文件系统, JFFS直接设计在FLASH设备之上,能判断获知可擦除扇区的边界,以及FLASH的大小。
MTD可以看作是原始的FLASH芯片的“翻译层(translation layer)”,也正是由于MTD的存在,使得JFFS在Linux中能快速发展。基于MTD这个硬件抽象层(HAL),JFFS几乎可以被mount在任何可随机访问的设备上(如: RAM, FLASH(各种厂家的))-----只要MTD支持这些设备。
如果遇到新设备且驱动程序不存在,则找一个现存的驱动程序(做模板),然后修改其中的有关“read/write/erase”之函数,之后就可以将JFFS mount到“新设备”上了(关键是你根本不需要知道JFFS是怎么工作的)。换句话说,将JFFS文件系统与设备相关的MTD (Memory Technology Device)(包含了很多“raw FLASH chip”之Linux驱动程序)相结合,你就得到了一个完整的解决方案----即由MTD向JFFS文件系统层提供一个抽象的设备层。
在这种方式下,JFFS不关心任何特定的存储(memory)技术。任何支持随机访问的设备(甚至象NAND FLASH这样的伪随机设备)都可以与MTD接口,从而在之上实现JFFS。在设计方面,JFFS(以及JFFS2)在文件系统层中保证一个“meta-data”(或文件系统的“格式”可靠性--- "formatting" reliability),这意味只要你的 write()系统调用返回了,则可以保证数据一定是“记录”下来的 。换句话说,如果在write()命令的执行期间突然掉电了,而此时数据还没有完全写入FLASH芯片,则芯片内记录的数据要么是older数据、要么是 newer数据,或者是两者的混合,但关键是你的文件不会因此而“坏(corrupted)”了。
最初的JFFS是设计成 "append only"类型的文件系统,即“好”数据重来不会被“覆盖”( overwritten)(如:对一个已打开的文件先做rewind()操作,然后 fwrite(),没用!),新的数据总是被添加在上次“文件系统”(而不是文件)写操作的位置之后。与块数据同时被写入的“meta”数据将保证块数据在“逻辑”上已被写入文件的正确位置。当重启动(或mount)之时,整个文件系统将被扫描,零散的数据块被重新排列,以使得在读取文件之时,最新被“标记(stamped)”数据块----即那些在逻辑上覆盖了“older数据块”----被读出。而“older数据”则被标记为“回收(garbage collection)-----在适当的时候将被删除。这种“ append-only结构”的优点是“natural wear leveling on the FLASH”(译注:减少FLASH的擦除次数)。有关JFFS的“掉电可靠性(Power Down Reliability)”,我已经做一些扩展并提交了fixes(已包含在最新版本的 CVS之中),我观察的效果是500多次掉电实验大约10次失败。另外,系统中还存在一些bug,使得JFFS会随机地丢掉一些文件(甚至是静态文件)!我将 NOT推荐在产品中使用该文件系统(至少是当前版本)。
问:解决方案?答: JFFS2。
JFFS2是JFFS技术的第二版,它基于JFFS的设计思想,但是由Redhat (www.redhat.com)实现的。它采用了一种不同的方法实现“可靠性”,所有的 "erase sector"被独立管理,且可以“乱序(out-of-order)”寻址,因而当创建新文件或覆盖老文件之时,可以统一地申请一块“已擦除扇区(erase sector)”。为了保证“掉电可靠性”,在被确认已被成功写入FLASH之前(通过CRC和版本标签),文件的任何部分不会被真正覆盖。之后,老的数据块被标记为“回收”,则待到其所有的邻居(同一个扇区内的)也有相似的标记之时,该扇区被擦除。
好消息,JFFS2还支持压缩。文件数据在被写入时,通过zlib(可调整mod's)压缩;数据在被读出之时在线(on the fly)解压缩;所以事实上你无法感知你的数据是否被压缩了。因而现在你可以采用ASCII文件格式(而不是binary文件)来做日志(log)或配置(config)文件,当然二进制文件也会被压缩的。如果你的文件很“松散(sparse)”(如:其中有很多空格),呵呵,不用担心空间会浪费了。但缺点是,如果你将已经压缩过的数据写入时,系统仍将花费大量时间试图再压缩它。而此时你又无法动态关闭压缩功能。目前有些计划正在试图实现相关功能(即使是基于目录级别,对单个目录实现压缩的开关选项或属性)。
3. STEP 1: ENABLING MTD/JFFS2 UNDER µClinux
我的M5407C3 Motorola板子上,有一个16 bit FLASH内存(AMD Am29PL160C),内存映射空间是$7FE00000~$7FFFFFFF。
首先,你要认真、仔细地阅读有关你所使用的FLASH芯片的“数据手册”,查看它的扇区地址表:
我的FLASH芯片有11个扇区(大小不等)。在第一个256KB空间(SA0~SA3)中包含“dBUG Motorola monitor”代码,其余空间供用户使用(SA4~SA10,每扇区256 KB)。当然你也可以擦除“dBUG monitor”而使用整个地址空间(dBUG monitor代码可以通过BDM线缆重新安装)。我选择的方案是定义两个“MTD分区(partition)”:
· dBUG分区:$7FE00000~$7FE3FFFF (256 Kbyte size)。(我不想将monitor去除。。。)
· user分区:$7FE40000~$7FFFFFFF (1792 Kbyte size)。
如果你擦除“dBUG monitor”,则拥有一个大的user分区:
· user分区:$7FE00000~$7FFFFFFF (2 Mbyte size).
注意:(参考µClinux文档)如果你使用的是JFFS(2),每个MTD分区至少包含6个连续扇区(原因:garbage collection)。
首先,配置µClinux之时,打开“MTD/JFFS2”:
% cd uClinux-dist
% make xconfig
看一下我的有关MTD的选项:
% grep MTD linux-2.4.x/.config
# Memory Technology Devices (MTD)
CONFIG_MTD=y
CONFIG_MTD_DEBUG=y
CONFIG_MTD_DEBUG_VERBOSE=3
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_GEN_PROBE=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_PHYSMAP=y
CONFIG_MTD_PHYSMAP_START=0x7fe00000
CONFIG_MTD_PHYSMAP_LEN=0x200000
CONFIG_MTD_PHYSMAP_BUSWIDTH=2
其中,你已经打开CONFIG_MTD_PHYSMAP(参考uClinux-dist/linux-2.4.x/drivers/mtd/maps/physmap.c),这意味着基于一个MTD分区,你可以访问整个FLASH空间了。
为了定义多个MTD分区,我又创建了一个特殊的physmap.c文件(m5407c3.c,uClinux-dist/linux-2.4.x/drivers/mtd/maps/目录)。
之后,打开JFFS2(µClinux内核配置):
· File systems menu: JFFS2 support, JFFS2 debugging verbosity 2.
选择MTD/JFFS2工具(userland area):
· Flash Tools menu: mtd-utils with erase, mkfs.jff2.
· BusyBox menu: BusyBox with dd, mount, mount: loop devices, umount.
创建自己的MTD之physmap文件
如果你需要多个MTD分区,你可以修改uClinux-dist/linux-2.4.x/drivers/mtd/maps/physmap.c 文件(参考:Phil Wildshire's document 之解释)。
我选择在µClinux distribution中创建自己的文件(在uClinux-dist/linux-2.4.x/drivers/mtd/maps/目录下又很多参考例子,我以m5272c3.c为例,这是一个专用于M5272C3 Motorola 开发板的)。
我的m5407c3.c 文件
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/config.h>
#define WINDOW_ADDR 0x7fe00000
#define WINDOW_SIZE 0x200000
#define BUSWIDTH 2
static struct mtd_info *mymtd;
__u8 m5407c3_read8(struct map_info *map, unsigned long ofs)
{
return __raw_readb(map->map_priv_1 + ofs);
}
__u16 m5407c3_read16(struct map_info *map, unsigned long ofs)
{
return __raw_readw(map->map_priv_1 + ofs);
}
__u32 m5407c3_read32(struct map_info *map, unsigned long ofs)
{
return __raw_readl(map->map_priv_1 + ofs);
}
void m5407c3_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy_fromio(to, map->map_priv_1 + from, len);
}
void m5407c3_write8(struct map_info *map, __u8 d, unsigned long adr)
{
__raw_writeb(d, map->map_priv_1 + adr);
mb();
}
void m5407c3_write16(struct map_info *map, __u16 d, unsigned long adr)
{
__raw_writew(d, map->map_priv_1 + adr);
mb();
}
void m5407c3_write32(struct map_info *map, __u32 d, unsigned long adr)
{
__raw_writel(d, map->map_priv_1 + adr);
mb();
}
void m5407c3_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
memcpy_toio(map->map_priv_1 + to, from, len);
}
struct map_info m5407c3_map = {
name: "MCF5407C3 flash device",
size: WINDOW_SIZE,
buswidth: BUSWIDTH,
read8: m5407c3_read8,
read16: m5407c3_read16,
read32: m5407c3_read32,
copy_from: m5407c3_copy_from,
write8: m5407c3_write8,
write16: m5407c3_write16,
write32: m5407c3_write32,
copy_to: m5407c3_copy_to
};
/*
* MTD 'PARTITIONING' STUFF
*/
static struct mtd_partition m5407c3_partitions[] = {
{
name: "dBUG (256K)",
size: 0x40000,
offset: 0x0
},
{
name: "user (1792K)",
size: 0x1c0000,
offset: 0x40000
}
};
int __init init_m5407c3(void)
{
printk(KERN_NOTICE "m5407c3 flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
m5407c3_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!m5407c3_map.map_priv_1) {
printk("Failed to ioremap\n");
return -EIO;
}
mymtd = do_map_probe("cfi_probe", &m5407c3_map);
if (mymtd) {
mymtd->module = THIS_MODULE;
mymtd->erasesize = 0x40000;
return add_mtd_partitions(mymtd, m5407c3_partitions,
sizeof(m5407c3_partitions) /
sizeof(struct mtd_partition));
}
iounmap((void *)m5407c3_map.map_priv_1);
return -ENXIO;
}
static void __exit cleanup_m5407c3(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
if (m5407c3_map.map_priv_1) {
iounmap((void *)m5407c3_map.map_priv_1);
m5407c3_map.map_priv_1 = 0;
}
}
module_init(init_m5407c3);
module_exit(cleanup_m5407c3);
最重要的是在mtd_partition m5407c3_partitions[]结构中,我定义了两个MTD分区。(其它内容我都是通过“替换”来修改的,即基于vi执行sed命令 :-))。
问:怎样将我的文件集成到&micro;Clinux distribution之中?我的做法是:
1. 在uClinux-dist/linux-2.4.x/drivers/mtd/maps/Config.in 文件中,添加下面行:
if [ "$CONFIG_M5407C3" ]; then dep_tristate ' CFI Flash device mapped on Motorola M5407C3' CONFIG_MTD_M5407C3 $CONFIG_MTD_CFI fi
2. 在uClinux-dist/linux-2.4.x/drivers/mtd/maps/Makefile 文件中添加:
obj-$(CONFIG_MTD_M5407C3) += m5407c3.o
于是,再次执行make xconfig,你将看到:
% cd uClinux-dist
% make xconfig
现在,我没有采用CONFIG_MTD_PHYSMAP默认选项:
# Memory Technology Devices (MTD)
CONFIG_MTD=y
CONFIG_MTD_DEBUG=y
CONFIG_MTD_DEBUG_VERBOSE=3
CONFIG_MTD_PARTITIONS=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_CFI=y
CONFIG_MTD_JEDECPROBE=y
CONFIG_MTD_GEN_PROBE=y
CONFIG_MTD_CFI_AMDSTD=y
CONFIG_MTD_M5407C3=y
3. STEP 2: MODIFYING THE &micro;Clinux KERNEL
3.1. Adding files from the Linux kernel distribution(添加文件)
你可以从某个地方(如:here )下载“Linux kernel version 2.4.19”,然后将linux-2.4.19/fs/jffs2/之中的pushpull.c, zlib.c, zlib.h文件拷贝到uClinux-dist/linux-2.4.x/fs/jffs2/目录下。
3.2. Modifying files from the &micro;Clinux kernel distribution(修改文件)
将这些文件中的BLKMEM_MAJOR值从“31”修改为“30”:
· uClinux-dist/linux-2.4.x/drivers/block/blkmem.c (line 41).
· uClinux-dist/linux-2.4.x/include/linux/major.h (line 66).
目的是避免造成MTD and BLKMEM之间“major”号的冲突。
3.3. Modifying files from the &micro;Clinux M5407C3 BSP port
在文件uClinux-dist/vendors/Motorola/M5407C3/Makefile 之中添加以下内容:
DEVICES = \
tty,c,5,0 console,c,5,1 cua0,c,5,64 cua1,c,5,65 \
mtd0,c,90,0 mtd1,c,90,2 mtd2,c,90,4 mtd3,c,90,6 \
mtd4,c,90,8 mtd5,c,90,10 mtd6,c,90,12 mtd7,c,90,14 \
mtdblock0,b,31,0 mtdblock1,b,31,1 mtdblock2,b,31,2 mtdblock3,b,31,3 \
mtdblock4,b,31,4 mtdblock5,b,31,5 mtdblock6,b,31,6 mtdblock7,b,31,7 \
mem,c,1,1 kmem,c,1,2 null,c,1,3 \
. . .
目的是在目标设备的uClinux目录结构之/dev目录下创建MTD and JFFS2所需要的设备文件。
4. STEP3: COMPILING(编译)
开始编译整个&micro;Clinux distribution:
% cd uClinux-dist
% make dep
% make
4. STEP4: TESTING(测试)
最后,通过网络下载&micro;Clinux的“image”文件至M5407C3板,并启动&micro;Clinux内核:
Hard Reset
DRAM Size: 32M
Copyright 1995-2001 Motorola, Inc. All Rights Reserved.
ColdFire MCF5407 EVS Firmware v2e.1a.1b (Build 18 on Apr 20 2001 11:57:55)
Enter 'help' for help.
dBUG> dn (下载)
Eth Mac Addr is 00:00:00:00:00:01
Downloading Image 'image.bin' from 192.168.4.1
Read 1891552 bytes (3695 blocks)
dBUG> go 20000 (启动)
启动显示:
Linux version 2.4.19-uc1 ([email protected]) (gcc version 2.95.3
uClinux/COLDFIRE(m5407)
COLDFIRE port done by Greg Ungerer, [email protected]
Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne
. . .
Starting kswapd
kmem_create: Forcing size word alignment - file lock cache
JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.
ColdFire internal UART serial driver version 1.00
ttyS0 at 0x100001c0 (irq = 73) is a builtin ColdFire UART
ttyS1 at 0x10000200 (irq = 74) is a builtin ColdFire UART
kmem_create: Forcing size word alignment - blkdev_requests
ne.c:v1.10 9/23/94 Donald Becker ([email protected])
Last modified Nov 1, 2000 by Paul Gortmaker
NE*000 ethercard probe at 0x40000300: 00 00 00 00 00 01
eth0: NE2000 found at 0x40000300, using IRQ 27.
SLIP: version 0.8.4-NET3.019-NEWTTY (dynamic channels, max=256).
CSLIP: code copyright 1989 Regents of the University of California.
Blkmem copyright 1998,1999 D. Jeff Dionne
Blkmem copyright 1998 Kenneth Albanowski
Blkmem 1 disk images:
0: 107FA4-2013A3 [VIRTUAL 107FA4-2013A3] (RO)
RAMDISK driver initialized: 16 RAM disks of 4096K size 1024 blocksize
PPP generic driver version 2.4.2
m5407c3 flash device: 200000 at 7fe00000
Amd/Fujitsu Extended Query Table v1.0 at 0x0040
number of CFI chips: 1
Creating 2 MTD partitions on "MCF5407C3 flash device":
0x00000000-0x00040000 : "dBUG (256K)"
mtd: partition "dBUG (256K)" doesn't end on an erase block -- force read-only
mtd: Giving out device 0 to dBUG (256K)
0x00040000-0x00200000 : "user (1792K)"
mtd: Giving out device 1 to user (1792K)
init_mtdchar: allocated major number 90.
init_mtdblock: allocated major number 31.
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP
. . .
Command: dhcpcd -p -a eth0 &
[14]
Command: cat /etc/motd
. . .
For further information check:
http://www.uclinux.org/
Execution Finished, Exiting
Sash command shell (version 1.1.1)
看到了?JFFS2和MTD都打开了,FLASH内存也被“CFI探测”发现了,2个 MTD分区也被创建了(/dev/mtd0和/dev/mtd1)。
查看一下MTD的分区列表:
/> cd /proc
/proc> cat mtd
dev: size erasesize name
mtd0: 00040000 00038000 "dBUG (256K)"
mtd1: 001c0000 00040000 "user (1792K)"
尝试着在MTD的user分区(/dev/mtd1)上创建一个JFFS2 image:
/proc> cd /tmp
/var/tmp> mkdir fs
/var/tmp> mkdir jffs2
/var/tmp> mkdir jffs2/bin
/var/tmp> cd jffs2
/var/tmp/jffs2> vi file1
/var/tmp/jffs2> cat file1
coucou
/var/tmp/jffs2> cd ..
/var/tmp> mkfs.jffs2 -d jffs2 -o jffs2.img
/var/tmp> erase /dev/mtd1
/var/tmp> cp jffs2.img /dev/mtd1
MTD_open
MTD_write
MTD_close
“Mount”JFFS2分区:
/var/tmp> mount -t jffs2 /dev/mtdblock1 /mnt
mtdblock_open
ok
/var/tmp> cd /mnt
/mnt> ls
bin
file1
toto
/mnt> cd /proc
/proc> cat mounts
rootfs / rootfs rw 0 0
/dev/root / romfs ro 0 0
/proc /proc proc rw 0 0
/dev/ram1 /var ext2 rw 0 0
/dev/mtdblock1 /mnt jffs2 rw 0 0
查看一下所有相关的设备文件(/dev/目录):
/proc> ls -l /dev
. . .
crw------- 1 0 0 90, 0 Jan 01 1970 mtd0
crw------- 1 0 0 90, 2 Jan 01 1970 mtd1
crw------- 1 0 0 90, 6 Jan 01 1970 mtd3
crw------- 1 0 0 90, 8 Jan 01 1970 mtd4
crw------- 1 0 0 90, 10 Jan 01 1970 mtd5
crw------- 1 0 0 90, 12 Jan 01 1970 mtd6
crw------- 1 0 0 90, 14 Jan 01 1970 mtd7
brw------- 1 0 0 31, 0 Jan 01 1970 mtdblock0
brw------- 1 0 0 31, 1 Jan 01 1970 mtdblock1
brw------- 1 0 0 31, 2 Jan 01 1970 mtdblock2
brw------- 1 0 0 31, 3 Jan 01 1970 mtdblock3
brw------- 1 0 0 31, 4 Jan 01 1970 mtdblock4
brw------- 1 0 0 31, 5 Jan 01 1970 mtdblock5
brw------- 1 0 0 31, 6 Jan 01 1970 mtdblock6
brw------- 1 0 0 31, 7 Jan 01 1970 mtdblock7
. . .
最后,你也可以“umount”JFFS2分区:
/proc> umount /mnt
mtdblock_release
ok
就是这样了。
5. CONCLUSION(结论)
本文中,我描述了在&micro;Clinux 中怎样使用MTD/JFFS2 的方法(step by step),并在M5407C3板上通过测试:
相关的测试条件是:
· Linux kernel version 2.4.19.
· &micro;Clinux version 20020927.
· m68k-elf-tools version 20020410.
另外,我使用的PC是Dell laptop (Inspiron 8200, P IV at 1,7 GHz) under RedHat 8.0.
没有人是完美的,如果你发现了“不正确的”内容,请与 me 联系。 |
|