Accessing flash memory from Linux

Raw NOR and NAND flash memory is handled by the memory technology device sub-system, or MTD, which provides basic interfaces to read, erase, and write blocks of flash memory. In the case of NAND flash, there are functions to handle the OOB area and to identify bad blocks.

For managed flash, you need drivers to handle the particular hardware interface. MMC/SD cards and eMMC use the mmcblk driver; CompactFlash and hard drives use the SCSI Disk driver, sd. USB flash drives use the usb_storage driver together with the sd driver.

Memory technology devices

The memory technology devices (MTD), sub-system was started by David Woodhouse in 1999 and has been extensively developed over the intervening years. In this section, I will concentrate on the way it handles the two main technologies, NOR and NAND flash.

MTD consists of three layers: a core set of functions, a set of drivers for various types of chips, and user-level drivers that present the flash memory as a character device or a block device, as shown in the following diagram:

Memory technology devices

The chip drivers are at the lowest level and interface with flash chips. Only a small number of drivers are needed for NOR flash chips, enough to cover the CFI standard and variations plus a few non-compliant chips which are now mostly obsolete. For NAND flash, you will need a driver for the NAND flash controller you are using; this is usually supplied as part of the board support package. There are drivers for about 40 of them in the current mainline kernel in the directory drivers/mtd/nand.

MTD partitions

In most cases, you will want to partition the flash memory into a number of areas, for example, to provide space for a bootloader, a kernel image, or a root filesystem. In MTD, there are several ways to specify the size and location of partitions, the main ones being:

  • Through the kernel command line using CONFIG_MTD_CMDLINE_PARTS
  • Via the device tree using CONFIG_MTD_OF_PARTS
  • With a platform mapping driver

In the case of the first option, the kernel command line option to use is mtdparts, which is defined as follows in the Linux source code in drivers/mtd/cmdlinepart.c:

mtdparts=<mtddef>[;<mtddef]
<mtddef>  := <mtd-id>:<partdef>[,<partdef>]
<mtd-id>  := unique name for the chip
<partdef> := <size>[@<offset>][<name>][ro][lk]
<size>    := size of partition OR "-" to denote all remaining
             space
<offset>  := offset to the start of the partition; leave blank
             to follow the previous partition without any gap
<name>    := '(' NAME ')'

Perhaps an example will help. Imagine that you have one flash chip of 128 MB that is to be divided into five partitions. A typical command line would be:

mtdparts=:512k(SPL)ro,780k(U-Boot)ro,128k(U-BootEnv),
4m(Kernel),-(Filesystem)

The first element, before the colon, :, is mtd-id, which identifies the flash chip, either by number or by the name assigned by the board support package. If there is only one chip, as here, it can be left empty. If there is more than one chip, the information for each is separated by a semicolon, ;. Then, for each chip, there is a comma-separated list of partitions, each with a size in bytes, kilobytes, k, or megabytes, m, and a name in brackets. The ro suffix makes the partition read-only to MTD and is often used to prevent accidental overwriting of the bootloader. The size of the last partition for the chip may be replaced by a dash, -, indicating that it should take up all the remaining space.

You can see a summary of the configuration at runtime by reading /proc/mtd:

# cat /proc/mtd
dev:    size   erasesize   name
mtd0: 00080000 00020000  "SPL"
mtd1: 000C3000 00020000  "U-Boot"
mtd2: 00020000 00020000  "U-BootEnv"
mtd3: 00400000 00020000  "Kernel"
mtd4: 07A9D000 00020000  "Filesystem"

There is more detailed information for each partition in /sys/class/mtd, including the erase block size and the page size, and it is nicely summarized using mtdinfo:

# mtdinfo /dev/mtd0
mtd0
Name:                           SPL
Type:                           nand
Eraseblock size:                131072 bytes, 128.0 KiB
Amount of eraseblocks:          4 (524288 bytes, 512.0 KiB)
Minimum input/output unit size: 2048 bytes
Sub-page size:                  512 bytes
OOB size:                       64 bytes
Character device major/minor:   90:0
Bad blocks are allowed:         true
Device is writable:             false

The equivalent partition information can be written as part of the device tree like so:

nand@0,0 {
  #address-cells = <1>;
  #size-cells = <1>;
  partition@0 {
    label = "SPL";
    reg = <0 0x80000>;
  };
  partition@80000 {
    label = "U-Boot";
    reg = <0x80000 0xc3000>;
  };
  partition@143000 {
    label = "U-BootEnv";
    reg = <0x143000 0x20000>;
  };
  partition@163000 {
    label = "Kernel";
    reg = <0x163000 0x400000>;
  };
  partition@563000 {
    label = "Filesystem";
    reg = <0x563000 0x7a9d000>;
  };
};

A third alternative is to code the partition information as platform data in an mtd_partition structure, as shown in this example taken from arch/arm/mach-omap2/board-omap3beagle.c (NAND_BLOCK_SIZE is defined elsewhere to be 128K):

static struct mtd_partition omap3beagle_nand_partitions[] = {
  {
    .name           = "X-Loader",
    .offset         = 0,
    .size           = 4 * NAND_BLOCK_SIZE,
    .mask_flags     = MTD_WRITEABLE,    /* force read-only */
  },
  {
    .name           = "U-Boot",
    .offset         = 0x80000;
    .size           = 15 * NAND_BLOCK_SIZE,
    .mask_flags     = MTD_WRITEABLE,    /* force read-only */
  },
  {
    .name           = "U-Boot Env",
    .offset         = 0x260000;
    .size           = 1 * NAND_BLOCK_SIZE,
  },
  {
    .name           = "Kernel",
    .offset         = 0x280000;
    .size           = 32 * NAND_BLOCK_SIZE,
  },
  {
    .name           = "File System",
    .offset         = 0x680000;
    .size           = MTDPART_SIZ_FULL,
  },
};

MTD device drivers

The upper level of the MTD sub-system is a pair of device drivers:

  • A character device, with a major number of 90. There are two device nodes per MTD partition number, N: /dev/mtdN (minor number=N*2) and /dev/mtdNro (minor number=(N*2 + 1)). The latter is just a read-only version of the former.
  • A block device, with a major number of 31 and a minor number of N. The device nodes are in the form /dev/mtdblockN.

The MTD character device, mtd

The character devices are the most important: they allow you to access the underlying flash memory as an array of bytes so that you can read and write (program) the flash. It also implements a number of ioctl functions that allow you to erase blocks and to manage the OOB area on NAND chips. The following list is in include/uapi/mtd/mtd-abi.h:

IOCTL

Description

MEMGETINFO

Gets basic MTD characteristic information

MEMERASE

Erases blocks in the MTD partition

MEMWRITEOOB

Writes out-of-band data for the page

MEMREADOOB

Reads out-of-band data for the page

MEMLOCK

Locks the chip (if supported)

MEMUNLOCK

Unlocks the chip (if supported)

MEMGETREGIONCOUNT

Gets the number of erase regions: non-zero if there are erase blocks of differing sizes in the partition, which is common for NOR flash, rare on NAND

MEMGETREGIONINFO

If MEMGETREGIONCOUNT is non-zero, this can be used to get the offset, size, and block count of each region

MEMGETOOBSEL

Deprecated

MEMGETBADBLOCK

Gets the bad block flag

MEMSETBADBLOCK

Sets the bad block flag

OTPSELECT

Sets OTP (one-time programmable) mode, if the chip supports it

OTPGETREGIONCOUNT

Gets the number of OTP regions

OTPGETREGIONINFO

Gets information about an OTP region

ECCGETLAYOUT

Deprecated

There is a set of utility programs known as mtd-utils for manipulating flash memory that makes use of these ioctl functions. The source is available from http://git.infradead.org/mtd-utils.git and is available as a package in the Yocto Project and Buildroot. The essential tools are shown in the following list. The package also contains utilities for the JFFS2 and UBI/UBIFS filesystems which I will cover later. For each of these tools, the MTD character device is one of the parameters:

  • flash_erase: Erases a range of blocks.
  • flash_lock: Locks a range of blocks.
  • flash_unlock: Unlocks a range of blocks.
  • nanddump: Dumps memory from NAND flash, optionally including the OOB area. Skips bad blocks.
  • nandtest: Tests and diagnostics for NAND flash.
  • nandwrite: Writes (program) from a data file to NAND flash, skipping bad blocks.

    Tip

    You must always erase flash memory before writing new contents to it: flash_erase is the command to do that.

To program NOR flash, you simply copy bytes to the MTD device node using the cp command or similar.

Unfortunately, this doesn't work with NAND memory as the copy will fail at the first bad block. Instead, use nandwrite, which skips over any bad blocks. To read back NAND memory, you should use nanddump which also skips bad blocks.

The MTD block device, mtdblock

The mtdblock driver is little used. Its purpose is to present flash memory as a block device which you can use to format and mount as a filesystem. However, it has severe limitations because it does not handle bad blocks in NAND flash, does not do wear leveling, and does not handle the mismatch in size between filesystem blocks and flash erase blocks. In other words, it does not have a flash translation layer, which is essential for reliable file storage. The only case where the mtdblock device is useful is to mount read-only filesystems such as Squashfs on top of reliable flash memory such as NOR.

Tip

If you want a read-only filesystem on NAND flash, you should use the UBI driver, as described later in this chapter.

Logging kernel oops to MTD

Kernel errors, or oopsies, are normally logged via the klogd and syslogd daemons to a circular memory buffer or a file. Following a reboot, the log will be lost in the case of a ring buffer and, even in the case of a file, it may not have been properly written before the system crashed.

Tip

A more reliable method is to write oops and kernel panics to an MTD partition as a circular log buffer. You enable it with CONFIG_MTD_OOPS and you add console=ttyMTDN to the kernel command line, N being the MTD device number to write the messages to.

Simulating NAND memory

The NAND simulator emulates a NAND chip using system RAM. The main use is for testing code that has to be NAND-aware without access to physical NAND memory. In particular, the ability to simulate bad blocks, bit flips, and other errors allows you to test code paths that are difficult to exercise using real flash memory. For more information, the best place to look is in the code itself, which has a comprehensive description of the ways you can configure the driver. The code is in drivers/mtd/nand/nandsim.c. Enable it with the kernel configuration CONFIG_MTD_NAND_NANDSIM.

The MMC block driver

MMC/SD cards and eMMC chips are accessed using the mmcblk block driver. You need a host controller to match the MMC adapter you are using, which is part of the board support package. The drivers are located in the Linux source code in drivers/mmc/host.

MMC storage is partitioned using a partition table in exactly the same way you would for hard disks, using fdisk or a similar utility.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.107.193