52884.fb2
The Memory Technology Devices (MTD) subsystem grew out of the need to support a wide variety of memory-like devices such as Flash memory chips. Many different types of Flash chips are available, along with numerous methods to program them, partly because of the many specialized and high-performance modes that are supported. The MTD layer architecture enables the separation of the low-level device complexities from the higher-layer data organization and storage formats that use memory devices.
In this chapter, we introduce the MTD subsystem and provide some simple examples of its use. First we look at what is required of the kernel to support MTD services. We introduce some simple operations on a development workstation with MTD enabled, as a means to understand the basics of this subsystem. In this chapter, we integrate MTD and the JFFS2 file system.
We next introduce the concept of partitions as they relate to the MTD layer. We examine the details of building partitions from a bootloader and how they are detected by the Linux kernel. The chapter continues with a brief introduction to the MTD utilities. We conclude by putting it all together and booting a target board using an in-Flash JFFS2 file system image.
To use MTD services, your kernel must be configured with MTD enabled. Many configuration options exist for MTD, some of which can be confusing. The best way to understand the myriad choices is simply to begin working with them. To illustrate the mechanics of the MTD subsystem and how it fits in with the system, we begin with some very simple examples that you can perform on your Linux development workstation. Figure 10-1 shows the kernel configuration (invoked per the usual make ARCH=<arch> gconfig) necessary to enable the bare-minimum MTD functionality. Listing 10-1 displays the .config file entries resulting from the selections shown in Figure 10-1.
Listing 10-1. Basic MTD Configuration from .config
CONFIG_MTD=y
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_MTD_MTDRAM=m
CONFIG_MTDRAM_TOTAL_SIZE=8192
CONFIG_MTDRAM_ERASE_SIZE=128
The MTD subsystem is enabled via the first configuration option, which is selected via the first check box shown in Figure 10-1, Memory Technology Device (MTD) Support. The next two entries from the configuration shown in Figure 10-1 enable special device-level access to the MTD devices, such as Flash memory, from user space. The first one (CONFIG_MTD_CHAR) enables character device mode access, essentially a sequential access characterized by byte-at-a-time sequential read and write access. The second (CONFIG_MTD_BLOCK) enables access to the MTD device in block device mode, the access method used for disk drives, in which blocks of multiple bytes of data are read or written at a time. These access modes allow the use of familiar Linux commands to read and write data to the Flash memory, as you shall shortly see.
Figure 10-1. MTD configuration
The CONFIG_MTD_MTDRAM element enables a special test driver that enables us to examine the MTD subsystem even if we don't have any MTD devices (such as Flash memory) available. Coupled with this configuration selection are two parameters associated with the RAM-based test driver: the device size and the erase size. For this example, we have specified 8192KB total size and 128KB erase size. The objective of this test driver is to emulate a Flash device, primarily to facilitate MTD subsystem testing and development. Because Flash memory is architected using fixed-size erase blocks, the test driver also contains the concept of erase blocks. You will see how these parameters are used shortly.
MTD is included in any recent snapshot of the Linux kernel. However, if you need to take advantage of MTD features that have been added since your kernel version was released, you must download and build the MTD drivers and utilities. Because the MTD package contains both kernel components and user space programs, it is useful to keep the MTD package in a separate project directory and connect it to your kernel source tree. The simplest way to integrate the MTD and your kernel source tree(s) is to use the scripts provided by the MTD package.
Download the MTD package from the location given at the end of this chapter. Unpack the archive into a directory of your choice using the tar utility. Enter the directory and run the patchkernel.sh script. This script provides several options. Execute the script with no parameters for a detailed usage. Listing 10-2 shows how to install the kernel components.
Listing 10-2. Patching Your Kernel for MTD
$ ./patchkernel.sh -2 ../sources/linux-2.6.10-mtd
Patching ../sources/linux-2.6.10-mtd/
Include JFFS2 file system: jffs2
Include JFFS3 file system (experimental): no
Method: ln << Will actually create symbolic links
Can we start now ? [y/N]y
$
Invoking the patchkernel.sh script with the -2 parameter indicates that we want support for the JFFS2 file system. We provide the path to the kernel source directory as ../sources/linux-2.6.10-mtd. By default, patchkernel.sh does not copy any files into the kernel source directory. Instead, it creates symbolic links from the kernel source tree pointing into the MTD subdirectory itself. In this way, you can maintain a common source tree for MTD for any number of kernels that you happen to have on your development workstation. This allows the MTD kernel drivers to be built with the kernel build system, including information about your specific kernel configuration.
Now that we have enabled a simple MTD configuration in our kernel, we can examine how this subsystem works on our Linux development workstation. Using the test RAM driver we just configured in the previous section, we can mount a JFFS2 image using an MTD device. Assuming that you created a JFFS2 image as detailed in Chapter 9, "File Systems," you might want to mount it and examine it. The Linux kernel does not support mounting a JFFS2 file system image directly on a loopback device, such as is possible with ext2 and other file system images. So we must use a different method. This can be achieved using the MTD RAM test driver on our development Linux workstation with MTD enabled, as in Figure 10-1. Listing 10-3 illustrates the steps.
Listing 10-3. Mounting JFFS2 on an MTD RAM Device
# modprobe jffs2
# modprobe mtdblock
# modprobe mtdram
# dd if=jffs2.bin of=/dev/mtdblock0
4690+1 records in
4690+1 records out
# mkdir /mnt/flash
# mount -t jffs2 /dev/mtdblock0/mnt/flash
# ls -l /mnt/flash
total 0
drwxr-xr-x 2 root root 0 Sep 17 22:02 bin
drwxr-xr-x 2 root root 0 Sep 17 21:59 dev
drwxr-xr-x 7 root root 0 Sep 17 15:31 etc
drwxr-xr-x 2 root root 0 Sep 17 15:31 home
drwxr-xr-x 2 root root 0 Sep 17 22:02 lib
drwxr-xr-x 2 root root 0 Sep 17 15:31 proc
drws------ 2 root root 0 Sep 17 15:31 root
drwxr-xr-x 2 root root 0 Sep 17 22:02 sbin
drwxrwxrwt 2 root root 0 Sep 17 15:31 tmp
drwxr-xr-x 9 root root 0 Sep 17 15:31 usr
drwxr-xr-x 14 root root 0 Sep 17 15:31 var
#
From Listing 10-3, first we install the loadable modules that the Linux kernel requires to support JFFS2 and the MTD subsystem. We load the JFFS2 module followed by the mTDblock and mtdram modules. After the necessary device drivers are loaded, we use the Linux dd command to copy our JFFS2 file system image into the MTD RAM test driver using the mTDblock device. In essence, we are using system RAM as a backing device to emulate an MTD block device.
After we have copied our JFFS2 file system image into the MTD block device, we can mount it using the mount command, in the manner shown in Listing 10-3. After the MTD pseudo-device has been mounted, we can work with the JFFS2 file system image in any way we choose. The only limitation using this method is that we can't enlarge the image. The size of the image is limited by two factors. First, when we configured the MTD RAM test device, we gave it a maximum size of 8MB.[75] Second, when we created the JFFS2 image, we fixed the size of the image using the mkfs.jffs2 utility. The image size was determined by the contents of the directory we specified when we created it. Refer back to Listing 9-9, in Chapter 9, to recall how our jffs2.bin image was built.
It is important to realize the limitations of using this method to examine the contents of a JFFS2 file system. Consider what we did: We copied the contents of a file (the JFFS2 file system binary image) into a kernel block device (/dev/mtdblock0). Then we mounted the kernel block device (/dev/mtdblock) as a JFFS2 file system. After we did this, we could use all the traditional file system utilities to examine and even modify the file system. Tools such as ls, df, dh, mv, rm, and cp can all be used to examine and modify the file system. However, unlike the loopback device, there is no connection between the file we copied and the mounted JFFS2 file system image. Therefore, if we unmount the file system after making changes, the changes will be lost. If you want to save the changes, you must copy them back into a file. One such method is the following:
# dd if=/dev/mtdblock0 of=./your-modified-fs-image.bin
This command creates a file called your-modified-fs-image.bin that is the same size as the mtdblock0 device which was specified during configuration. In our example, it would be 8MB. Lacking suitable JFFS2 editing facilities, this is a perfectly valid way to examine and modify a JFFS2 file system. More important, it illustrates the basics of the MTD subsystem on our development system without real Flash memory. Now let's look at some hardware that contains Flash physical devices.
To use MTD with the Flash memory on your board, you must have MTD configured correctly. The following list contains the requirements that must be satisfied to configure MTD for your board, Flash, and Flash layout.
• Specify the partitioning on your Flash device
• Specify the type of Flash and location
• Configure the proper Flash driver for your chosen chip
• Configure the kernel with the appropriate driver(s)
Each of these steps is explored in the following sections.
Most Flash devices on a given hardware platform are divided into several sections, called partitions, similar to the partitions found on a typical desktop workstation hard drive. The MTD subsystem provides support for such Flash partitions. The MTD subsystem must be configured for MTD partitioning support. Figure 10-2 illustrates the configuration options for MTD partitioning support.
Figure 10-2. Kernel configuration for MTD partitioning support
Several methods exist for communicating the partition data to the Linux kernel. The following methods are currently supported. You can see the configuration options for each in Figure 10-2 under MTD Partitioning Support.
• Redboot partition table parsing
• Kernel command-line partition table definition
• Board-specific mapping drivers
MTD also allows configurations without partition data. In this case, MTD simply treats the entire Flash memory as a single device.
One of the more common methods of defining and detecting MTD partitions stems from one of the original implementations: Redboot partitions. Redboot is a bootloader found on many embedded boards, especially ARM XScale boards such as the ADI Engineering Coyote Reference Platform.
The MTD subsystem defines a method for storing partition information on the Flash device itself, similar in concept to a partition table on a hard disk. In the case of the Redboot partitions, the developer reserves and specifies a Flash erase block that holds the partition definitions. A mapping driver is selected that calls the partition parsing functions during boot to detect the partitions on the Flash device. Figure 10-2 shows the mapping driver for our example board; it is the final highlighted entry defining CONFIG_MTD_IXP4xx.
As usual, taking a detailed look at an example helps to illustrate these concepts. We start by looking at the information provided by the Redboot bootloader for the Coyote platform. Listing 10-4 captures some of the output of the Redboot bootloader upon power-up.
Listing 10-4. Redboot Messages on Power-Up
Platform: ADI Coyote (XScale)
IDE/Parallel Port CPLD Version: 1.0
Copyright (C) 2000, 2001, 2002, Red Hat, Inc.
RAM: 0x00000000-0x04000000, 0x0001f960-0x03fd1000 available
FLASH: 0x50000000 - 0x51000000, 128 blocks of 0x00020000 bytes each.
...
This tells us that RAM on this board is physically mapped starting at address 0x00000000 and that Flash is mapped at physical address 0x50000000 through 0x51000000. We can also see that the Flash has 128 blocks of 0x00020000 (128KB) each.
Redboot contains a command to create and display partition information on the Flash. Listing 10-5 contains the output of the fis list command, part of the Flash Image System family of commands available in the Redboot bootloader.
Listing 10-5. Redboot Flash Partition List
RedBoot> fis list
Name FLASH addr Mem addr Length Entry point
RedBoot 0x50000000 0x50000000 0x00060000 0x00000000
RedBoot config 0x50FC0000 0x50FC0000 0x00001000 0x00000000
FIS directory 0x50FE0000 0x50FE0000 0x00020000 0x00000000
RedBoot>
From Listing 10-5, we see that the Coyote board has three partitions defined on the Flash. The partition named RedBoot contains the executable Redboot bootloader image. The partition named RedBoot config contains the configuration parameters maintained by the bootloader. The final partition named FIS directory holds information about the partition table itself.
When properly configured, the Linux kernel can detect and parse this partition table and create MTD partitions representing the physical partitions on Flash. Listing 10-6 reproduces a portion of the boot messages that are output from the aforementioned ADI Engineering Coyote board, booting a Linux kernel configured with support for detecting Redboot partitions.
Listing 10-6. Detecting Redboot Partitions on Linux Boot
...
IXP4XX-Flash0: Found 1 x16 devices at 0x0 in 16-bit bank
Intel/Sharp Extended Query Table at 0x0031
Using buffer write method
cfi_cmdset_0001: Erase suspend on write enabled
Searching for RedBoot partition table in IXP4XX-Flash0 at offset 0xfe0000
3 RedBoot partitions found on MTD device IXP4XX-Flash0
Creating 3 MTD partitions on "IXP4XX-Flash0":
0x00000000-0x00060000: "RedBoot"
0x00fc0000-0x00fc1000: "RedBoot config"
0x00fe0000-0x01000000: "FIS directory"
...
The first message in Listing 10-6 is printed when the Flash chip is detected, via the Common Flash Interface (CFI) driver, enabled via CONFIG_MTD_CFI. CFI is an industry-standard method for determining the Flash chip's characteristics, such as manufacturer, device type, total size, and erase block size. See Section 10.5.1, "Suggestions for Additional Reading," at the end of this chapter for a pointer to the CFI specification.
CFI is enabled via the kernel-configuration utility under the Memory Technology Devices (MTD) top-level menu. Select Detect flash chips by Common Flash Interface (CFI) probe under RAM/ROM/Flash chip drivers, as illustrated in Figure 10-3.
Figure 10-3. Kernel configuration for MTD CFI support
As shown in Listing 10-6, the Flash chip is detected via the CFI interface. Because we also enabled CONFIG_MTD_REDBOOT_PARTS (see Figure 10-2), MTD scans for the Redboot partition table on the Flash chip. Notice also that the chip has been enumerated with the device name IXP4XX-Flash0. You can see from Listing 10-6 that the Linux kernel has detected three partitions on the Flash chip, as enumerated previously using the fis list command in Redboot.
When the infrastructure is in place as described here, the Linux kernel automatically detects and creates kernel data structures representing the three Flash partitions. Evidence of these can be found in the /proc file system when the kernel has completed initialization, as shown in Listing 10-7.
Listing 10-7. Kernel MTD Flash Partitions
root@coyote:~# cat /proc/mtd
dev: size erasesize name
mtd0: 00060000 00020000 "RedBoot"
mtd1: 00001000 00020000 "RedBoot config"
mtd2: 00020000 00020000 "FIS directory"
#
We can easily create a new Redboot partition. We use the Redboot FIS commands for this example, but we do not detail the Redboot commands in this book. However, the interested reader can consult the Redboot user documentation listed in Section 10.5.1 at the end of this chapter. Listing 10-8 shows the details of creating a new Redboot partition.
Listing 10-8. Creating a New Redboot Partition
RedBoot> load -r -v -b 0x01008000 coyote-40-zImage
Using default protocol (TFTP)
Raw file loaded 0x01008000-0x0114dccb, assumed entry at 0x01008000
RedBoot> fis create -b 0x01008000 -l 0x145cd0 -f 0x50100000 MyKernel
... Erase from 0x50100000-0x50260000: ...........
... Program from 0x01008000-0x0114dcd0 at 0x50100000: ...........
... Unlock from 0x50fe0000-0x51000000: .
... Erase from 0x50fe0000-0x51000000: .
... Program from 0x03fdf000-0x03fff000 at 0x50fe0000: .
... Lock from 0x50fe0000-0x51000000: .
First, we load the image we will use to create the new partition. We will use our kernel image for the example. We load it to memory address 0x01008000. Then we create the new partition using the Redboot fis create command. We have instructed Redboot to create the new partition in an area of Flash starting at 0x50100000. You can see the action as Redboot first erases this area of Flash and then programs the kernel image. In the final sequence, Redboot unlocks its directory area and updates the FIS Directory with the new partition information. Listing 10-9 shows the output of fis list with the new partition. Compare this with the output in Listing 10-5.
Listing 10-9. New Redboot Partition List
RedBoot> fis list
Name FLASH addr Mem addr Length Entry point
RedBoot 0x50000000 0x50000000 0x00060000 0x00000000
RedBoot config 0x50FC0000 0x50FC0000 0x00001000 0x00000000
FIS directory 0x50FE0000 0x50FE0000 0x00020000 0x00000000
MyKernel 0x50100000 0x50100000 0x00160000 0x01008000
Of course, when we boot the Linux kernel, it discovers the new partition and we can operate on it as we see fit. The astute reader might have realized the other benefit of this new partition: We can now boot the kernel from Flash instead of having to load it via tftp every time. The command is illustrated next. Simply pass the Redboot exec command the Flash starting address of the partition and the length of the image to transfer into RAM.
...RedBoot> exec -b 0x50100000 -l 0x145cd0
Uncompressing Linux........... done, booting the kernel.
...
As detailed in Section 10.3, "MTD Partitions," the raw Flash partition information can be communicated to the kernel using other methods. Indeed, possibly the most straightforward, though perhaps not the simplest method is to manually pass the partition information directly on the kernel command line. Of course, as we have already learned, some bootloaders make that easy (for example U-Boot), whereas others do not have a facility to pass a kernel command line to the kernel upon boot. In these cases, the kernel command line must be configured at compile time and, therefore, is more difficult to change, requiring a recompile of the kernel itself each time the partitions are modified.
To enable command-line partitioning in the MTD subsystem, your kernel must be configured for this support. You can see this configuration option in Figure 10-2 under MTD partitioning support. Select the option for command-line partition table parsing, which defines the CONFIG_MTD_CMDLINE_PARTS option.
Listing 10-10 shows the format for defining a partition on the kernel command line (taken from .../drivers/mtd/cmdlinepart.c).
Listing 10-10. Kernel Command-Line MTD Partition Format
mtdparts=<mtddef>[;<mtddef]
*<mtddef> := <mtd-id>:<partdef>[,<partdef>]
*<partdef> := <size>[@offset][<name>][ro]
*<mtd-id> := unique name used in mapping driver/device (mtd->name)
*<size> := std linux memsize OR "-" to denote all remaining space
*<name> := '(' NAME ')'
Each mtddef parameter passed on the kernel command line defines a separate partition. As shown is Listing 10-10, each mtddef definition contains multiple parts. You can specify a unique ID, partition size, and offset from the start of the Flash. You can also pass the partition a name and, optionally, the read-only attribute. Referring back to our Redboot partition definitions in Listing 10-5, we could statically define these on the kernel command line as follows:
mtdparts=MainFlash:384K(Redboot),4K(config),128K(FIS),-(unused)
With this definition, the kernel would instantiate four MTD partitions, with an MTD ID of MainFlash, containing the sizes and layout matching that found in Listing 10-5.
The final method for defining your board-specific Flash layout is to use a dedicated board-specific mapping driver. The Linux kernel source tree contains many examples of mapping drivers, located in .../drivers/mtd/maps. Any one of these will provide good examples for how to create your own. The implementation details vary by architecture.
The mapping driver is a proper kernel module, complete with module_init() and module_exit() calls, as described in Chapter 8, "Device Driver Basics." A typical mapping driver is small and easy to navigate, often containing fewer than a couple dozen lines of C.
Listing 10-11 reproduces a section of .../drivers/mtd/maps/pq2fads. This mapping driver defines the Flash device on a Freescale PQ2FADS evaluation board that supports the MPC8272 and other processors.
Listing 10-11. PQ2FADs Flash Mapping Driver
...
static struct mtd_partition pq2fads_partitions[] = {
{
#ifdef CONFIG_ADS8272
.name = "HRCW",
.size = 0x40000,
.offset = 0,
.mask_flags= MTD_WRITEABLE, /* force read-only */
}, {
.name = "User FS",
.size = 0x5c0000,
.offset = 0x40000,
#else
.name = "User FS",
.size = 0x600000,
.offset = 0,
#endif
}, {
.name = "uImage",
.size = 0x100000,
.offset = 0x600000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}, {
.name = "bootloader",
.size = 0x40000,
.offset = 0x700000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}, {
.name = "bootloader env",
.size = 0x40000,
.offset = 0x740000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}
};
/* pointer to MPC885ADS board info data */
extern unsigned char __res[];
static int __init init_pq2fads_mtd(void) {
bd_t *bd = (bd_t *)__res;
physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL);
physmap_set_partitions(pq2fads_partitions, sizeof (pq2fads_partitions) / sizeof (pq2fads_partitions[0]));
return 0;
}
static void __exit cleanup_pq2fads_mtd(void) {}
module_init(init_pq2fads_mtd);
module_exit(cleanup_pq2fads_mtd);
...
This simple but complete Linux device driver communicates the PQ2FADS Flash mapping to the MTD subsystem. Recall from Chapter 8 that when a function in a device driver is declared with the module_init() macro, it is automatically invoked during Linux kernel boot at the appropriate time. In this PQ2FADS mapping driver, the module initialization function init_pq2fads_mtd() performs just two simple calls:
• physmap_configure() passes to the MTD subsystem the Flash chip's physical address, size, and bank width, along with any special setup function required to access the Flash.
• physmap_set_partitions() passes the board's unique partition information to the MTD subsystem from the partition table defined in the pq2fads_partitions[] array found at the start of this mapping driver.
Following this simple example, you can derive a mapping driver for your own board.
MTD has support for a wide variety of Flash chips and devices. Chances are very good that your chosen chip has also been supported. The most common Flash chips support the Common Flash Interface (CFI) mentioned earlier. Older Flash chips might have JEDEC support, which is an older Flash compatibility standard. Figure 10-4 shows the kernel configuration from a recent Linux kernel snapshot. This version supports many Flash types.
Figure 10-4. Flash device support
If your Flash chip is not supported, you must provide a device file yourself. Using one of the many examples in .../drivers/mtd/chips as a starting point, customize or create your own Flash device driver. Better yet, unless the chip was just introduced with some newfangled interface, chances are good that someone has already produced a driver.
Along with a mapping driver, your board-specific (platform) setup must provide the underlying definitions for proper MTD Flash system operation. Listing 10-12 reproduces the relevant portions of .../arch/arm/mach-ixp4xx/coyote-setup.c.
Listing 10-12. Coyote-Specific Board Setup
static struct flash_platform_data coyote_flash_data = {
.map_name = "cfi_probe",
.width = 2,
};
static struct resource coyote_flash_resource = {
.start = COYOTE_FLASH_BASE,
.end = COYOTE_FLASH_BASE + COYOTE_FLASH_SIZE - 1,
.flags = IORESOURCE_MEM,
};
static struct platform_device coyote_flash = {
.name = "IXP4XX-Flash",
.id = 0,
.dev = {
.platform_data = &coyote_flash_data,
},
.num_resources = 1,
.resource = &coyote_flash_resource,
};
...
static struct platform_device *coyote_devices[] __initdata = {
&coyote_flash,
&coyote_uart
};
static void __init coyote_init(void) {
...
platform_add_devices(coyote_devices, ARRAY_SIZE(coyote_devices));
}
...
In Listing 10-12, only the relevant portions of the coyote-setup.c platform initialization file are reproduced. Starting from the bottom, the coyote_init() function calls platform_add_devices(), specifying the Coyote-specific devices defined earlier in this file. You'll notice that two devices are defined just above the coyote_init() routine. The one we're interested in for this discussion is coyote_flash. This structure of type struct platform_device contains all the important details needed by the Linux kernel and MTD subsystem.
The .name member of the coyote_flash structure binds our platform-specific Flash resource to a mapping driver with the same name. You can see this in the mapping driver file .../drivers/mtd/maps/ixp4xx.c. The .resource member communicates the base address of the Flash on the board. The .dev member, which contains a .platform_data member, ties our Flash setup to a chip driver. In this case, we have specified that our board will use the CFI probe method, specified in the kernel configuration as CONFIG_MTD_CFI. You can see this configuration selection in Figure 10-4.
Depending on your own architecture and board, you can use a method similar to this to define the Flash support for your own board.
The MTD package contains a number of system utilities useful for setting up and managing your MTD subsystem. The utilities are built separately from the primary MTD subsystem, which should be built from within your Linux kernel source tree. The utilities can be built in a similar manner to any other cross-compiled user space code.
You must use caution when using these utilities because there is no protection from mistakes. A single-digit typo can wipe out the bootloader on your hardware platform, which can definitely ruin your day unless you've backed it up and know how to reprogram it using a JTAG Flash programmer.
In keeping with a common practice throughout this book, we cannot devote sufficient space to cover every MTD utility. We highlight the most common and useful ones, and leave it as an exercise for the reader to explore the rest. A recent MTD snapshot contained more than 20 binary utilities.
The flash_* family of utilities is useful for raw device operations on an MTD partition. These include flashcp, flash_erase, flash_info, flash_lock, flash_unlock, and others. Hopefully their names are descriptive enough to give some idea of their function. After partitions are defined and enumerated as kernel devices, any of these user space utilities can be run on a partition. We repeat the warning we issued earlier: If you execute flash_erase on the partition containing your bootloader, you'll be the proud owner of a silicon paperweight. If you intend to experiment like this, it's a good idea to have a backup of your bootloader image and know how to re-Flash it using a hardware JTAG emulator or other Flash programming tool.
Our new partition created in Listing 10-8 (MyKernel) shows up in the kernel running on the Coyote board, as detailed in Listing 10-13. Here you can see the new partition we created instantiated as the kernel device mTD1.
Listing 10-13. Kernel MTD Partition List
root@coyote:~# cat /proc/mtd
dev: size erasesize name
mtd0: 00060000 00020000 "RedBoot"
mtd1: 00160000 00020000 "MyKernel"
mtd2: 00001000 00020000 "RedBoot config"x
mtd3: 00020000 00020000 "FIS directory"
Using the MTD utilities, we can perform a number of operations on the newly created partition. The following shows the results of a flash_erase command on the partition:
# flash_erase /dev/mtd1
Erase Total 1 Units
Performing Flash Erase of length 131072 at offset 0x0 done
To copy a new kernel image to this partition, use the flashcp command:
root@coyote:~# flashcp /workspace/coyote-40-zImage /dev/mtd1
It gets a bit more interesting working with a root file system partition. We have the option of using the bootloader or the Linux kernel to place the initial image on the Redboot flash partition. First, we use Redboot to create the new partition that will hold our root file system. The following command creates a new partition on the Flash called RootFS starting at physical memory 0x50300000, with a length of 30 blocks. Remember, a block, generically called an erase unit, is 128KB on this Flash chip.
RedBoot> fis create -f 0x50300000 -l 0x600000 -n RootFS
Next, we boot the kernel and copy the root file system image into the new partition we have named RootFS. This is accomplished with the following command from a Linux command prompt on your target board. Note that this assumes you have already placed your file system image in a directory accessible to your board. As mentioned many times throughout this book, NFS is your best choice for development.
root@coyote:~# flashcp /rootfs.ext2/dev/mtd2
The file system can be anywhere from a couple megabytes up to the largest size we have allowed on this partition, so this can take some time. Remember, this operation involves programming (sometimes called flashing) the image into the Flash memory. After copying, we can mount the partition as a file system. Listing 10-14 displays the sequence.
Listing 10-14. Mounting MTD Flash Partition as ext2 File System
root@coyote:~# mount -t ext2/dev/mtdblock2 /mnt/remote ro
root@coyote:~# ls -l /mnt/remote/
total 16
drwxr-xr-x 2 root root 1024 Nov 19 2005 bin
drwxr-xr-x 2 root root 1024 Oct 26 2005 boot
drwxr-xr-x 2 root root 1024 Nov 19 2005 dev
drwxr-xr-x 5 root root 1024 Nov 19 2005 etc
drwxr-xr-x 2 root root 1024 Oct 26 2005 home
drwxr-xr-x 3 root root 1024 Nov 19 2005 lib
drwxr-xr-x 3 root root 1024 Nov 19 2005 mnt
drwxr-xr-x 2 root root 1024 Oct 26 2005 opt
drwxr-xr-x 2 root root 1024 Oct 26 2005 proc
drwxr-xr-x 2 root root 1024 Oct 26 2005 root
drwxr-xr-x 2 root root 1024 Nov 19 2005 sbin
drwxr-xr-x 2 root root 1024 Oct 26 2005 srv
drwxr-xr-x 2 root root 1024 Oct 26 2005 sys
drwxr-xr-x 2 root root 1024 Oct 26 2005 tmp
drwxr-xr-x 6 root root 1024 Oct 26 2005 usr
drwxr-xr-x 2 root root 1024 Nov 19 2005 var
root@coyote:~#
Listing 10-14 has two important subtleties. Notice that we have specified /dev/mtdblock2 on the mount command line. This is the MTD block driver that enables us to access the MTD partition as a block device. Using /dev/mtd2 as a specifier instructs the kernel to use the MTD character driver. Both the mtdchar and mtdblock are pseudodrivers used to provide either character-based or block-oriented access to the underlying Flash partition. Because mount expects a block device, you must use the block-device specifier. Figure 10-1 shows the kernel configuration that enables these access methods. The respective kernel configuration macros are CONFIG_MTD_CHAR and CONFIG_MTD_BLOCK.
The second subtlety is the use of the read-only (ro) command-line switch on the mount command. It is perfectly acceptable to mount an ext2 image from Flash using the MTD block emulation driver for read-only purposes. However, there is no support for writing to an ext2 device using the mtdblock driver. This is because ext2 has no knowledge of Flash erase blocks. For write access to a Flash-based file system, we need to use a file system with Flash knowledge, such as JFFS2.
Creating a JFFS2 root file system is a straightforward process. In addition to compression, JFFS2 supports wear leveling, a feature designed to increase Flash lifetime by fairly distributing the write cycles across the blocks of the device. As pointed out in Chapter 9, Flash memory is subject to a limited number of write cycles. Wear leveling should be considered a mandatory feature in any Flash-based file system you employ. As mentioned elsewhere in this book, you should consider Flash memory as a write-occasional medium. Specifically, you should avoid allowing any processes that require frequent writes to target the Flash file system. Be especially aware of any logging programs, such as syslogd.
We can build a JFFS2 image on our development workstation using the ext2 image we used on our Redboot RootFS partition. The compression benefits will be immediately obvious. The image we used in the previous RootFS example was an ext2 file system image. Here is the listing in long (-l) format:
# ls -l rootfs.ext2
-rw-r--r-- 1 root root 6291456 Nov 19 16:21 rootfs.ext2
Now let's convert this file system image to JFFS2 using the mkfs.jffs2 utility found in the MTD package. Listing 10-15 shows the command and results.
Listing 10-15. Converting RootFS to JFFS2
# mount -o loop rootfs.ext2/mnt/flash/
# mkfs.jffs2 -r /mnt/flash -e 128 -b -o rootfs.jffs2
# ls -l rootfs.jffs2
-rw-r--r-- 1 root root 2401512 Nov 20 10:08 rootfs.jffs2
#
First we mount the ext2 file system image on a loopback device on an arbitrary mount point on our development workstation. Next we invoke the MTD utility mkfs.jffs2 to create the JFFS2 file system image. The -r flag tells mkfs.jffs2 where the root file system image is located. The -e instructs mkfs.jffs2 to build the image while assuming a 128KB block size. The default is 64KB. JFFS2 does not exhibit its most efficient behavior if the Flash device contains a different block size than the block size of the image. Finally, we display a long listing and discover that the resulting JFFS2 root file system image has been reduced in size by more than 60 percent. When you are working with limited Flash memory, this is a substantial reduction in precious Flash resource usage.
Take note of an important command-line flag passed to mkfs.jffs2 in Listing 10-15. The -b flag is the -big-endian flag. This instructs the mkfs.jffs2 utility to create a JFFS2 Flash image suitable for use on a big-endian target. Because we are targeting the ADI Engineering Coyote board, which contains an Intel IXP425 processor running in big-endian mode, this step is crucial for proper operation. If you fail to specify big endian, you will get several screens full of complaints from the kernel as it tries to negotiate the superblock of a JFFS2 file system that is essentially gibberish.[76] Anyone care to guess how I remembered this important detail?
In a similar manner to the previous example, we can copy this image to our Redboot RootFS Flash partition using the flashcp utility. Then we can boot the Linux kernel using a JFFS2 root file system. Listing 10-16 provides the details, running the MTD utilities on our target hardware.
Listing 10-16. Copying JFFS2 to RootFS Partition
root@coyote:~# cat /proc/mtd
dev: size erasesize name
mtd0: 00060000 00020000 "RedBoot"
mtd1: 00160000 00020000 "MyKernel"
mtd2: 00600000 00020000 "RootFS"
mtd3: 00001000 00020000 "RedBoot config"
mtd4: 00020000 00020000 "FIS directory"
root@coyote:~# flash_erase /dev/mtd2
Erase Total 1 Units
Performing Flash Erase of length 131072 at offset 0x0 done
root@coyote:~# flashcp /rootfs.jffs2 /dev/mtd2
root@coyote:~#
It is important to note that you must have the JFFS2 file system enabled in your kernel configuration. Execute make ARCH=<arch> gconfig and select JFFS2 under File Systems, Miscellaneous File Systems. Another useful hint is to use the -v (verbose) flag on the MTD utilities. This provides progress updates and other useful information during the Flash operations.
We have already seen how to boot a kernel with the Redboot exec command. Listing 10-17 details the sequence of commands to load and boot the Linux kernel with our new JFFS2 file system as root.
Listing 10-17. Booting with JFFS2 as Root File System
RedBoot> load -r -v -b 0x01008000 coyote-zImage
Using default protocol (TFTP)
Raw file loaded 0x01008000-0x0114decb, assumed entry at 0x01008000
RedBoot> exec -c "console=ttyS0,115200 rootfstype=jffs2 root=/dev/mtdblock2"
Using base address 0x01008000 and length 0x00145ecc
Uncompressing Linux...... done, booting the kernel.
...
• The Memory Technology Devices (MTD) subsystem provides support for memory devices such as Flash memory in the Linux kernel.
• MTD must be enabled in your Linux kernel configuration. Several figures in this chapter detailed the configuration options.
• As part of the MTD kernel configuration, the proper Flash driver(s) for your Flash chips must be selected. Figure 10-4 presented the collection of chip drivers supported in a recent Linux kernel snapshot.
• Your Flash memory device can be managed as a single large device or can be divided into multiple partitions.
• Several methods are available for communicating the partition information to the Linux kernel. These include Redboot partition information, kernel command-line parameters, and mapping drivers.
• A mapping driver, together with definitions supplied by your architecture-specific board support, defines your Flash configuration to the kernel.
• MTD comes with a number of user space utilities to manage the images on your Flash devices.
• The Journaling Flash File System 2 (JFFS2) is a good companion to the MTD subsystem for small, efficient Flash-based file systems. In this chapter, we built a JFFS2 image and mounted it as root on our target device.
MTD Linux home page
www.linux-mtd.infradead.org/
Redboot user documentation
http://ecos.sourceware.org/ecos/docs-latest/redboot/redboot-guide.html
Common Flash Memory Interface Specification
AMD Corporation
www.amd.com/us-en/assets/content_type/DownloadableAssets/cfi_r20.pdf
The size was fixed in the kernel configuration when we enabled the MTD RAM test device in the Linux kernel configuration.
The kernel can be configured to operate with a wrong-endian MTD file system, at the cost of reduced performance. In some configurations (such as multiprocessor designs), this can be a useful feature.