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 |
下载: |
下载 | |