Home > Articles > Operating Systems, Server > Linux/UNIX/Open Source

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

The Firmware Hub

PC-compatible systems use a NOR flash chip called the Firmware Hub (FWH) to hold the BIOS. The FWH is not directly connected to the processor's address and data bus. Instead, it's interfaced via the Low Pin Count (LPC) bus, which is part of South Bridge chipsets. The connection diagram is shown in Figure 17.5.

Figure 17.5

Figure 17.5 The Firmware Hub on a PC-compatible system.

The MTD subsystem includes drivers to interface the processor with the FWH. FWHs are usually not compliant with the CFI specification. Instead, they conform to the JEDEC (Joint Electron Device Engineering Council) standard. To inform MTD about a yet unsupported JEDEC chip, add an entry to the jedec_table array in drivers/mtd/chips/jedec_probe.c with information such as the chip manufacturer ID and the command-set ID. Here is an example:

static const struct amd_flash_info jedec_table[] = {
  /* ... */
  {
    .mfr_id  = MANUFACTURER_ID, /* E.g.: MANUFACTURER_ST */
    .dev_id  = DEVICE_ID,       /* E.g.: M50FW080 */
    .name    = "MYNAME",        /* E.g.: "M50FW080" */
    .uaddr   = {
      [0] = MTD_UADDR_UNNECESSARY,
    },
    .DevSize  = SIZE_1MiB,    /* E.g.: 1MB */
    .CmdSet   = CMDSET,       /* Command-set to communicate with the
                                 flash chip e.g., P_ID_INTEL_EXT */
    .NumEraseRegions = 1,     /* One region */
    .regions = {
      ERASEINFO (0x10000, 16),/* Sixteen 64K sectors */
    }
  },
  /* ... */
};

When you have your chip details imprinted in the jedec_table as shown here, MTD should recognize your flash, provided you have enabled the right kernel configuration options. The following configuration makes the kernel aware of an FWH that interfaces to the processor via an Intel ICH2 or ICH4 South Bridge chipset:

CONFIG_MTD=y                u2192.gif Enable the MTD subsystem
CONFIG_MTD_GEN_PROBE=y      u2192.gif Common routines for chip probing
CONFIG_MTD_JEDECPROBE=y     u2192.gif JEDEC chip driver
CONFIG_MTD_CFI_INTELEXT=y   u2192.gif The command-set for communicating
                                 with the chip
CONFIG_MTD_ICHXROM=y        u2192.gif The map driver

CONFIG_MTD_JEDECPROBE enables the JEDEC MTD chip driver, and CONFIG_MTD_ICH2ROM adds the MTD map driver that maps the FWH to the processor's address space. In addition, you need to include the appropriate command-set implementation (for example, CONFIG_MTD_CFI_INTELEXT for Intel Extension commands).

After these modules have been loaded, you can talk to the FWH from user-space applications via device nodes exported by MTD. You can, for example, reprogram the BIOS from user space using a simple application, as shown in Listing 17.5. Be warned that incorrectly operating this program can corrupt the BIOS and render your system unbootable!

Listing 17.5 operates on the MTD char device associated with the FWH, which it assumes to be /dev/mtd/0. The program issues three MTD-specific ioctl commands:

  • MEMUNLOCK to unlock the flash sectors prior to programming
  • MEMERASE to erase flash sectors prior to rewriting
  • MEMLOCK to relock the sectors after programming

Listing 17.5. Updating the BIOS

#include <linux/mtd/mtd.h>
#include <stdio.h>
#include <fcntl.h>
#include <asm/ioctl.h>
#include <signal.h>
#include <sys/stat.h>

#define BLOCK_SIZE    4096
#define NUM_SECTORS   16
#define SECTOR_SIZE   64*1024

int
main(int argc, char *argv[])
{
  int fwh_fd, image_fd;
  int usect=0, lsect=0, ret;
  struct erase_info_user fwh_erase_info;
  char buffer[BLOCK_SIZE];
  struct stat statb;
  /* Ignore SIGINTR(^C) and SIGSTOP (^Z), lest
     you end up with a corrupted flash and an
     unbootable system */
  sigignore(SIGINT);
  sigignore(SIGTSTP);

  /* Open MTD char device */
  fwh_fd = open("/dev/mtd/0", O_RDWR);
  if (fwh_fd < 0) exit(1);

  /* Open BIOS image */
  image_fd = open("bios.img", O_RDONLY);
  if (image_fd < 0) exit(2);

  /* Sanity check */
  fstat(image_fd, &statb);
  if (statb.st_size != SECTOR_SIZE*NUM_SECTORS) {
    printf("BIOS image looks bad, exiting.\n");
    exit(3);
  }

  /* Unlock and erase all sectors */
  while (usect < NUM_SECTORS) {
    printf("Unlocking & Erasing Sector[%d]\r", usect+1);

    fwh_erase_info.start = usect*SECTOR_SIZE;
    fwh_erase_info.length = SECTOR_SIZE;

    ret = ioctl(fwh_fd, MEMUNLOCK, &fwh_erase_info);
    if (ret != 0) goto bios_done;

    ret = ioctl(fwh_fd, MEMERASE, &fwh_erase_info);
    if (ret != 0) goto bios_done;
    usect++;
  }

   /* Read blocks from the BIOS image and dump it to the
      Firmware Hub */
  while ((ret = read(image_fd, buffer, BLOCK_SIZE)) != 0) {
    if (ret < 0) goto bios_done;
    ret = write(fwh_fd, buffer, ret);
    if (ret <= 0) goto bios_done;
  }
  /* Verify by reading blocks from the BIOS flash and comparing
     with the image file */

  /* ... */

 bios_done:

  /* Lock back the unlocked sectors */
  while (lsect < usect) {
    printf("Relocking Sector[%d]\r", lsect+1);

    fwh_erase_info.start = lsect*SECTOR_SIZE;
    fwh_erase_info.length = SECTOR_SIZE;

    ret = ioctl(fwh_fd, MEMLOCK, &fwh_erase_info);
    if (ret != 0) printf("Relock failed on sector %d!\n", lsect);
    lsect++;
  }

  close(image_fd);
  close(fwh_fd);
}
  • + Share This
  • 🔖 Save To Your Account