📄 rfd_ftl.c
字号:
/* * rfd_ftl.c -- resident flash disk (flash translation layer) * * Copyright (C) 2005 Sean Young <sean@mess.org> * * This type of flash translation layer (FTL) is used by the Embedded BIOS * by General Software. It is known as the Resident Flash Disk (RFD), see: * * http://www.gensw.com/pages/prod/bios/rfd.htm * * based on ftl.c */#include <linux/hdreg.h>#include <linux/init.h>#include <linux/mtd/blktrans.h>#include <linux/mtd/mtd.h>#include <linux/vmalloc.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <asm/types.h>#define const_cpu_to_le16 __constant_cpu_to_le16static int block_size = 0;module_param(block_size, int, 0);MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");#define PREFIX "rfd_ftl: "/* This major has been assigned by device@lanana.org */#ifndef RFD_FTL_MAJOR#define RFD_FTL_MAJOR 256#endif/* Maximum number of partitions in an FTL region */#define PART_BITS 4/* An erase unit should start with this value */#define RFD_MAGIC 0x9193/* the second value is 0xffff or 0xffc8; function unknown *//* the third value is always 0xffff, ignored *//* next is an array of mapping for each corresponding sector */#define HEADER_MAP_OFFSET 3#define SECTOR_DELETED 0x0000#define SECTOR_ZERO 0xfffe#define SECTOR_FREE 0xffff#define SECTOR_SIZE 512#define SECTORS_PER_TRACK 63struct block { enum { BLOCK_OK, BLOCK_ERASING, BLOCK_ERASED, BLOCK_UNUSED, BLOCK_FAILED } state; int free_sectors; int used_sectors; int erases; u_long offset;};struct partition { struct mtd_blktrans_dev mbd; u_int block_size; /* size of erase unit */ u_int total_blocks; /* number of erase units */ u_int header_sectors_per_block; /* header sectors in erase unit */ u_int data_sectors_per_block; /* data sectors in erase unit */ u_int sector_count; /* sectors in translated disk */ u_int header_size; /* bytes in header sector */ int reserved_block; /* block next up for reclaim */ int current_block; /* block to write to */ u16 *header_cache; /* cached header */ int is_reclaiming; int cylinders; int errors; u_long *sector_map; struct block *blocks;};static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);static int build_block_map(struct partition *part, int block_no){ struct block *block = &part->blocks[block_no]; int i; block->offset = part->block_size * block_no; if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) { block->state = BLOCK_UNUSED; return -ENOENT; } block->state = BLOCK_OK; for (i=0; i<part->data_sectors_per_block; i++) { u16 entry; entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]); if (entry == SECTOR_DELETED) continue; if (entry == SECTOR_FREE) { block->free_sectors++; continue; } if (entry == SECTOR_ZERO) entry = 0; if (entry >= part->sector_count) { printk(KERN_WARNING PREFIX "'%s': unit #%d: entry %d corrupt, " "sector %d out of range\n", part->mbd.mtd->name, block_no, i, entry); continue; } if (part->sector_map[entry] != -1) { printk(KERN_WARNING PREFIX "'%s': more than one entry for sector %d\n", part->mbd.mtd->name, entry); part->errors = 1; continue; } part->sector_map[entry] = block->offset + (i + part->header_sectors_per_block) * SECTOR_SIZE; block->used_sectors++; } if (block->free_sectors == part->data_sectors_per_block) part->reserved_block = block_no; return 0;}static int scan_header(struct partition *part){ int sectors_per_block; int i, rc = -ENOMEM; int blocks_found; size_t retlen; sectors_per_block = part->block_size / SECTOR_SIZE; part->total_blocks = part->mbd.mtd->size / part->block_size; if (part->total_blocks < 2) return -ENOENT; /* each erase block has three bytes header, followed by the map */ part->header_sectors_per_block = ((HEADER_MAP_OFFSET + sectors_per_block) * sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE; part->data_sectors_per_block = sectors_per_block - part->header_sectors_per_block; part->header_size = (HEADER_MAP_OFFSET + part->data_sectors_per_block) * sizeof(u16); part->cylinders = (part->data_sectors_per_block * (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK; part->sector_count = part->cylinders * SECTORS_PER_TRACK; part->current_block = -1; part->reserved_block = -1; part->is_reclaiming = 0; part->header_cache = kmalloc(part->header_size, GFP_KERNEL); if (!part->header_cache) goto err; part->blocks = kcalloc(part->total_blocks, sizeof(struct block), GFP_KERNEL); if (!part->blocks) goto err; part->sector_map = vmalloc(part->sector_count * sizeof(u_long)); if (!part->sector_map) { printk(KERN_ERR PREFIX "'%s': unable to allocate memory for " "sector map", part->mbd.mtd->name); goto err; } for (i=0; i<part->sector_count; i++) part->sector_map[i] = -1; for (i=0, blocks_found=0; i<part->total_blocks; i++) { rc = part->mbd.mtd->read(part->mbd.mtd, i * part->block_size, part->header_size, &retlen, (u_char*)part->header_cache); if (!rc && retlen != part->header_size) rc = -EIO; if (rc) goto err; if (!build_block_map(part, i)) blocks_found++; } if (blocks_found == 0) { printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n", part->mbd.mtd->name); rc = -ENOENT; goto err; } if (part->reserved_block == -1) { printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n", part->mbd.mtd->name); part->errors = 1; } return 0;err: vfree(part->sector_map); kfree(part->header_cache); kfree(part->blocks); return rc;}static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf){ struct partition *part = (struct partition*)dev; u_long addr; size_t retlen; int rc; if (sector >= part->sector_count) return -EIO; addr = part->sector_map[sector]; if (addr != -1) { rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen, (u_char*)buf); if (!rc && retlen != SECTOR_SIZE) rc = -EIO; if (rc) { printk(KERN_WARNING PREFIX "error reading '%s' at " "0x%lx\n", part->mbd.mtd->name, addr); return rc; } } else memset(buf, 0, SECTOR_SIZE); return 0;}static void erase_callback(struct erase_info *erase){ struct partition *part; u16 magic; int i, rc; size_t retlen; part = (struct partition*)erase->priv; i = erase->addr / part->block_size; if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) { printk(KERN_ERR PREFIX "erase callback for unknown offset %x " "on '%s'\n", erase->addr, part->mbd.mtd->name); return; } if (erase->state != MTD_ERASE_DONE) { printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', " "state %d\n", erase->addr, part->mbd.mtd->name, erase->state); part->blocks[i].state = BLOCK_FAILED; part->blocks[i].free_sectors = 0; part->blocks[i].used_sectors = 0; kfree(erase); return; } magic = const_cpu_to_le16(RFD_MAGIC); part->blocks[i].state = BLOCK_ERASED; part->blocks[i].free_sectors = part->data_sectors_per_block; part->blocks[i].used_sectors = 0; part->blocks[i].erases++; rc = part->mbd.mtd->write(part->mbd.mtd, part->blocks[i].offset, sizeof(magic), &retlen, (u_char*)&magic); if (!rc && retlen != sizeof(magic)) rc = -EIO; if (rc) { printk(KERN_ERR PREFIX "'%s': unable to write RFD " "header at 0x%lx\n", part->mbd.mtd->name, part->blocks[i].offset); part->blocks[i].state = BLOCK_FAILED; } else part->blocks[i].state = BLOCK_OK; kfree(erase);}static int erase_block(struct partition *part, int block){ struct erase_info *erase; int rc = -ENOMEM; erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL); if (!erase) goto err; erase->mtd = part->mbd.mtd; erase->callback = erase_callback; erase->addr = part->blocks[block].offset; erase->len = part->block_size; erase->priv = (u_long)part; part->blocks[block].state = BLOCK_ERASING; part->blocks[block].free_sectors = 0; rc = part->mbd.mtd->erase(part->mbd.mtd, erase); if (rc) { printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' " "failed\n", erase->addr, erase->len, part->mbd.mtd->name); kfree(erase); }err: return rc;}static int move_block_contents(struct partition *part, int block_no, u_long *old_sector){ void *sector_data; u16 *map; size_t retlen; int i, rc = -ENOMEM; part->is_reclaiming = 1; sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL); if (!sector_data) goto err3; map = kmalloc(part->header_size, GFP_KERNEL); if (!map) goto err2; rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block_no].offset, part->header_size, &retlen, (u_char*)map); if (!rc && retlen != part->header_size) rc = -EIO; if (rc) { printk(KERN_ERR PREFIX "error reading '%s' at " "0x%lx\n", part->mbd.mtd->name, part->blocks[block_no].offset); goto err; } for (i=0; i<part->data_sectors_per_block; i++) { u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]); u_long addr; if (entry == SECTOR_FREE || entry == SECTOR_DELETED) continue; if (entry == SECTOR_ZERO) entry = 0; /* already warned about and ignored in build_block_map() */ if (entry >= part->sector_count) continue; addr = part->blocks[block_no].offset + (i + part->header_sectors_per_block) * SECTOR_SIZE; if (*old_sector == addr) { *old_sector = -1; if (!part->blocks[block_no].used_sectors--) { rc = erase_block(part, block_no); break; } continue; } rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen, sector_data); if (!rc && retlen != SECTOR_SIZE) rc = -EIO; if (rc) { printk(KERN_ERR PREFIX "'%s': Unable to " "read sector for relocation\n", part->mbd.mtd->name); goto err; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -