如何在uCLinux上面使用MTD/JFFS2



原文:
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
kadionik@enseirb.fr
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之中),原本大概掉電10次就有一次失敗,在我的修正之後可以成長到超過500次才有一次的失敗。另外,系統中還存在一些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
#include
#include
#include
#include
#include
#include
#include

#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命令 :-))。
問:怎樣將我的文件集成到µ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 µ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 µ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 µ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(編譯)
開始編譯整個µClinux distribution:
% cd uClinux-dist
% make dep
% make

4. STEP4: TESTING(測試)
最後,通過網絡下載µClinux的「image」文件至M5407C3板,並啟動µ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 (root@localhost.localdomain) (gcc version 2.95.3
uClinux/COLDFIRE(m5407)
COLDFIRE port done by Greg Ungerer, gerg@snapgear.com
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 (becker@scyld.com)
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(結論)
本文中,我描述了在µClinux 中怎樣使用MTD/JFFS2 的方法(step by step),並在M5407C3板上通過測試:
相關的測試條件是:
‧ Linux kernel version 2.4.19.
‧ µClinux version 20020927.
‧ m68k-elf-tools version 20020410.
另外,我使用的PC是Dell laptop (Inspiron 8200, P IV at 1,7 GHz) under RedHat 8.0.

arrow
arrow
    全站熱搜

    Bluelove1968 發表在 痞客邦 留言(0) 人氣()