Chinaunix首页 | 论坛 | 博客
  • 博客访问: 103447
  • 博文数量: 27
  • 博客积分: 2510
  • 博客等级: 少校
  • 技术积分: 246
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-02 22:35
文章分类

全部博文(27)

文章存档

2009年(13)

2008年(14)

我的朋友

分类: LINUX

2008-11-07 17:41:25


MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。我将CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

            **************     **************
            * 根文件系统  *      *  文件系统  *
            **************     **************
               |                   |
              \|/                 \|/
****************                ****************
* 字符设备节点  *                *    块设备节点  *
****************                ****************
           |                       |
          \|/                     \|/
***************            *********************
* MTD字符设备 *             *     MTD块设备     *
***************            *********************
           |                       |
          \|/                     \|/
************************************************
*               MTD原始设备层                  *
************************************************
                      |
                     \|/
************************************************
*               FLASH硬件驱动层                *
************************************************

在linux kernel中,intel系列flash的硬件驱动层函数在cfi_cmdset_0001.c中,而amd,sst,atmel系列flash的硬件驱动层函数则在cfi_cmdset_0002.c中。本文分析的linux kernel为linux 2.6.19。

本文主要就flash的保护问题说说我的看法,在linux 2.6.19的内核中,只有针对intel系列flash的整个sector的保护实现,当要保护的flash地址不是一个完整sector的起始地址时,就不能进行保护。当然具体的实现细节还要参考对应flash的datasheet。

本文作者在cfi_cmdset_0001.c中添加了cfi_intelext_protect函数,从而实现任意flash地址的lock,unlock功能,如下所示:
##################################################################################
drivers/mtd/chips/cfi_cmdset_0001.c:

#ifdef CONFIG_NEW_MTD
/*
 * jerry.du 2008-11-03, add for flash protection
 * and now, in bootloader, we protect all flash in hardware way
 * so, we should protect off the flash before the erase/write operation,
 * after that, protect on all the flash again
 */
static int cfi_intelext_protect(struct mtd_info *mtd, loff_t ofs, size_t len, void *thunk)
{
    DECLARE_WAITQUEUE(wait, current);
    struct map_info *map = mtd->priv;
    struct cfi_private *cfi = map->fldrv_priv;
    struct flchip *chip;
    unsigned long cmd_adr;
    unsigned long block_size;
    uint32_t sector_start = 0;
    uint32_t sector_end = 0;
    uint32_t sector_num = 0;
    int i;
    int chipnum;
    int ret = 0;
    //printk("cfi_intelext_protect ofs 0x%llx len 0x%x\n", ofs, len);
    chipnum    = ofs >> cfi->chipshift;
    chip       = (struct flchip *)&cfi->chips[chipnum];
    block_size = (cfi->cfiq->EraseRegionInfo[chipnum] >> 8) & ~0xff;
    
    cmd_adr = ofs  - (chipnum << cfi->chipshift);
    cmd_adr += chip->start;

    /* get the sector information, the sector start address should be aligned */
    sector_start = cmd_adr / block_size;
    sector_end   = (cmd_adr + len) / block_size;
    cmd_adr      = sector_start * block_size;
    //printk("cfi_intelext_protect cmd_adr 0x%x len %d\n", map->phys + cmd_adr, len);
    /* 
     * maybe the length is not an all sector, but contain the last section of one sertor 
     * and the begin section of the following sector, as figured:
     * |++++++++++++++++++|+++++++++++++++++++++|  ---->two sectors
     *                          $$$$$$$$$$$$$$$$  ---->one write section, cover two sectors
     */
    sector_num   = sector_end - sector_start;
    if ((cmd_adr + len) % block_size)
        sector_num++;
    //printk("cfi_intelext_protect sector_start 0x%x sector_num %d\n", sector_start, sector_num);

retry:
    spin_lock(chip->mutex);

    if (chip->state != FL_READY){
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(&chip->wq, &wait);

        spin_unlock(chip->mutex);

        schedule();
        remove_wait_queue(&chip->wq, &wait);

        goto retry;
    }

    chip->state = FL_READY;
    for (i = 0; i < sector_num; i++) {
        cmd_adr = cmd_adr + i * block_size;
        ret = do_xxlock_oneblock(map, chip, cmd_adr, block_size, thunk);
        if (ret)
            break;
    }

    wake_up(&chip->wq);
    spin_unlock(chip->mutex);
    return ret;
}

static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    return cfi_intelext_protect(mtd, ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
}

static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    return cfi_intelext_protect(mtd, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
}
#else
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    int ret;

#ifdef DEBUG_LOCK_BITS
    printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
      __FUNCTION__, ofs, len);
    cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif

    ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);

#ifdef DEBUG_LOCK_BITS
    printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
      __FUNCTION__, ret);
    cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif

    return ret;
}

static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    int ret;

#ifdef DEBUG_LOCK_BITS
    printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
      __FUNCTION__, ofs, len);
    cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif

    ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
                ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);

#ifdef DEBUG_LOCK_BITS
    printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
      __FUNCTION__, ret);
    cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
ofs, len, 0);
#endif

    return ret;
}
#endif
##################################################################################

同样,在cfi_cmdset_0002.c中也加入对amd系列flash的支持,如下:
##################################################################################
drivers/mtd/chips/cfi_cmdset_0002.c:

/*
 * jerry.du 2008-10-29, add for flash protection
 * and now, in bootloader, we protect all flash in hardware way(DYB)
 * so, we should protect off the flash before the erase/write operation,
 * after that, protect on all the flash again
 */
#ifdef CONFIG_NEW_MTD
#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1)
#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2)

static int __xipram do_amdstd_xxlock_oneblock(struct map_info *map, struct flchip *chip,
      unsigned long adr, int len, void *thunk)
{
    struct cfi_private *cfi = map->fldrv_priv;
    int ret = 0;
    int mode = 0;
    spin_lock(chip->mutex);

    if (thunk == DO_XXLOCK_ONEBLOCK_LOCK)
        mode = FL_LOCKING;
    else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK)
        mode = FL_UNLOCKING;
    else
        BUG();
   
    ret = get_chip(map, chip, adr, mode);
    if (ret) {
        spin_unlock(chip->mutex);
        return ret;
    }

    DEBUG(MTD_DEBUG_LEVEL3, "MTD %s(): LOCK 0x%08lx len 0x%x\n", __func__, adr, len);

    chip->state = mode;

    cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
    cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
    cfi_send_gen_cmd(0xE0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
    cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
    if (thunk == DO_XXLOCK_ONEBLOCK_LOCK)
        map_write(map, CMD(0x00), adr);
    else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK)
        map_write(map, CMD(0x01), adr);

    if (chip_ready(map, adr)) {
        /* exit DYN command set entry, the command's address don't care */
        cfi_send_gen_cmd(0x90, 0, chip->start, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0x00, 0, chip->start, map, cfi, cfi->device_type, NULL);
    }
    else {
        printk("%s failed!\n", "Protected");
        cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL);
        goto out;
    }

    put_chip(map, chip, adr);
    chip->state = FL_READY;
out:
    spin_unlock(chip->mutex);
    return ret;
}

static int cfi_amdstd_protect(struct mtd_info *mtd, loff_t ofs, size_t len, void *thunk)
{
    DECLARE_WAITQUEUE(wait, current);
    struct map_info *map = mtd->priv;
    struct cfi_private *cfi = map->fldrv_priv;
    struct flchip *chip;
    unsigned long cmd_adr;
    unsigned long block_size;
    uint32_t sector_start = 0;
    uint32_t sector_end = 0;
    uint32_t sector_num = 0;
    int i;
    int chipnum;
    int ret = 0;
    //printk("cfi_amdstd_protect ofs 0x%llx len 0x%x\n", ofs, len);
    chipnum    = ofs >> cfi->chipshift;
    chip       = (struct flchip *)&cfi->chips[chipnum];
    block_size = (cfi->cfiq->EraseRegionInfo[chipnum] >> 8) & ~0xff;
    
    cmd_adr = ofs - (chipnum << cfi->chipshift);
    cmd_adr += chip->start;

    /* get the sector information, the sector start address should be aligned */
    sector_start = cmd_adr / block_size;
    sector_end   = (cmd_adr + len) / block_size;
    cmd_adr      = sector_start * block_size;
    //printk("cfi_amdstd_protect cmd_adr 0x%x len %d\n", map->phys + cmd_adr, len);

    /* 
     * maybe the length is not an all sector, but contain the last section of one sertor 
     * and the begin section of the following sector, as figured:
     * |++++++++++++++++++|+++++++++++++++++++++|  ---->two sectors
     *                          $$$$$$$$$$$$$$$$  ---->one write section, cover two sectors
     */
    sector_num   = sector_end - sector_start;
    if ((cmd_adr + len) % block_size)
        sector_num++;
    //printk("cfi_amdstd_protect sector_start 0x%x sector_num %d\n", sector_start, sector_num);

retry:
    spin_lock(chip->mutex);

    if (chip->state != FL_READY){
        set_current_state(TASK_UNINTERRUPTIBLE);
        add_wait_queue(&chip->wq, &wait);

        spin_unlock(chip->mutex);
        
        schedule();
        remove_wait_queue(&chip->wq, &wait);

        goto retry;
    }

    chip->state = FL_READY;
    for (i = 0; i < sector_num; i++) {
        cmd_adr = cmd_adr + i * block_size;
        ret = do_amdstd_xxlock_oneblock(map, chip, cmd_adr, block_size, thunk);
        if (ret)
            break;
    }

    wake_up(&chip->wq);
    spin_unlock(chip->mutex);
    return ret;
}

static int cfi_amdstd_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    return cfi_amdstd_protect(mtd, ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
}

static int cfi_amdstd_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
    return cfi_amdstd_protect(mtd, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
}
#endif

#ifdef CONFIG_NEW_MTD
/* 
  * jerry.du 2008-10-30, add for amd flash protection
  * don't use MTD_STUPID_LOCK, it maybe unlock the flash
  */
static void fixup_use_amdstd_lock(struct mtd_info *mtd, void *param)
{
    mtd->lock   = cfi_amdstd_lock;
    mtd->unlock = cfi_amdstd_unlock;
}
#endif

static struct cfi_fixup fixup_table[] = {
    /* The CFI vendor ids and the JEDEC vendor IDs appear
     * to be common.  It is like the devices id's are as
     * well.  This table is to pick all cases where
     * we know that is the case.
     */
    { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL },
    { CFI_MFR_ATMEL, AT49BV6416, fixup_use_atmel_lock, NULL },
#ifdef CONFIG_ES3628BT_FLF_ZZ
    { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_amdstd_lock, NULL },
#endif
    { 0, 0, NULL, NULL }
};
##################################################################################

最后在mtdpart.c的part_write,part_erase函数中加入lock和unlock的操作。这样,从VFS层下来的系统调用就会通过part_write和part_erase实现对MTD block设备的读写操作。代码如下:
##################################################################################
drivers/mtd/mtdpart.c:
#ifdef CONFIG_NEW_MTD
static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
    struct mtd_part *part = PART(mtd);
    int ret;

    if (!(mtd->flags & MTD_WRITEABLE))
        return -EROFS;
    if (to >= mtd->size)
        len = 0;
    else if (to + len > mtd->size)
        len = mtd->size - to;
    
    if (part->master->unlock)
        part->master->unlock (part->master, to + part->offset, len);
    ret = part->master->write (part->master, to + part->offset,
   len, retlen, buf);
    if (part->master->lock)
        part->master->lock (part->master, to + part->offset, len);

    return ret;
}
#else

#ifdef CONFIG_NEW_MTD
static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
{
    struct mtd_part *part = PART(mtd);
    int ret;
    if (!(mtd->flags & MTD_WRITEABLE))
        return -EROFS;
    if (instr->addr >= mtd->size)
        return -EINVAL;
    instr->addr += part->offset;
    if (part->master->unlock)
        part->master->unlock (part->master, instr->addr, instr->len);
    ret = part->master->erase(part->master, instr);
    if (part->master->lock)
        part->master->lock (part->master, instr->addr, instr->len);
    return ret;
}
#else
##################################################################################

参考资料如下:

文件: Linux MTD源代码分析.rar
大小: 71KB
下载: 下载

文件: StrataFlash.rar
大小: 531KB
下载: 下载

文件: s29gl-p_00_a11_e.rar
大小: 795KB
下载: 下载


阅读(1381) | 评论(0) | 转发(0) |
0

上一篇:u-boot porting guide

下一篇:Linux Kernel Porject

给主人留下些什么吧!~~