📄 nandsim.c
字号:
ns->geom.pgaddrbytes = 4; ns->geom.secaddrbytes = 3; } } else { if (ns->geom.totsz <= (128 << 20)) { ns->geom.pgaddrbytes = 4; ns->geom.secaddrbytes = 2; } else { ns->geom.pgaddrbytes = 5; ns->geom.secaddrbytes = 3; } } /* Fill the partition_info structure */ if (parts_num > ARRAY_SIZE(ns->partitions)) { NS_ERR("too many partitions.\n"); ret = -EINVAL; goto error; } remains = ns->geom.totsz; next_offset = 0; for (i = 0; i < parts_num; ++i) { u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; if (!part_sz || part_sz > remains) { NS_ERR("bad partition size.\n"); ret = -EINVAL; goto error; } ns->partitions[i].name = get_partition_name(i); ns->partitions[i].offset = next_offset; ns->partitions[i].size = part_sz; next_offset += ns->partitions[i].size; remains -= ns->partitions[i].size; } ns->nbparts = parts_num; if (remains) { if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { NS_ERR("too many partitions.\n"); ret = -EINVAL; goto error; } ns->partitions[i].name = get_partition_name(i); ns->partitions[i].offset = next_offset; ns->partitions[i].size = remains; ns->nbparts += 1; } /* Detect how many ID bytes the NAND chip outputs */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) continue; if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) ns->options |= OPT_AUTOINCR; } if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); printk("flash size: %llu MiB\n", (unsigned long long)ns->geom.totsz >> 20); printk("page size: %u bytes\n", ns->geom.pgsz); printk("OOB area size: %u bytes\n", ns->geom.oobsz); printk("sector size: %u KiB\n", ns->geom.secsz >> 10); printk("pages number: %u\n", ns->geom.pgnum); printk("pages per sector: %u\n", ns->geom.pgsec); printk("bus width: %u\n", ns->busw); printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); printk("bits in OOB size: %u\n", ns->geom.oobshift); printk("flash size with OOB: %llu KiB\n", (unsigned long long)ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); if ((ret = alloc_device(ns)) != 0) goto error; /* Allocate / initialize the internal buffer */ ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); if (!ns->buf.byte) { NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ns->geom.pgszoob); ret = -ENOMEM; goto error; } memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); return 0;error: free_device(ns); return ret;}/* * Free the nandsim structure. */static void free_nandsim(struct nandsim *ns){ kfree(ns->buf.byte); free_device(ns); return;}static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd){ char *w; int zero_ok; unsigned int erase_block_no; loff_t offset; if (!badblocks) return 0; w = badblocks; do { zero_ok = (*w == '0' ? 1 : 0); erase_block_no = simple_strtoul(w, &w, 0); if (!zero_ok && !erase_block_no) { NS_ERR("invalid badblocks.\n"); return -EINVAL; } offset = erase_block_no * ns->geom.secsz; if (mtd->block_markbad(mtd, offset)) { NS_ERR("invalid badblocks.\n"); return -EINVAL; } if (*w == ',') w += 1; } while (*w); return 0;}static int parse_weakblocks(void){ char *w; int zero_ok; unsigned int erase_block_no; unsigned int max_erases; struct weak_block *wb; if (!weakblocks) return 0; w = weakblocks; do { zero_ok = (*w == '0' ? 1 : 0); erase_block_no = simple_strtoul(w, &w, 0); if (!zero_ok && !erase_block_no) { NS_ERR("invalid weakblocks.\n"); return -EINVAL; } max_erases = 3; if (*w == ':') { w += 1; max_erases = simple_strtoul(w, &w, 0); } if (*w == ',') w += 1; wb = kzalloc(sizeof(*wb), GFP_KERNEL); if (!wb) { NS_ERR("unable to allocate memory.\n"); return -ENOMEM; } wb->erase_block_no = erase_block_no; wb->max_erases = max_erases; list_add(&wb->list, &weak_blocks); } while (*w); return 0;}static int erase_error(unsigned int erase_block_no){ struct weak_block *wb; list_for_each_entry(wb, &weak_blocks, list) if (wb->erase_block_no == erase_block_no) { if (wb->erases_done >= wb->max_erases) return 1; wb->erases_done += 1; return 0; } return 0;}static int parse_weakpages(void){ char *w; int zero_ok; unsigned int page_no; unsigned int max_writes; struct weak_page *wp; if (!weakpages) return 0; w = weakpages; do { zero_ok = (*w == '0' ? 1 : 0); page_no = simple_strtoul(w, &w, 0); if (!zero_ok && !page_no) { NS_ERR("invalid weakpagess.\n"); return -EINVAL; } max_writes = 3; if (*w == ':') { w += 1; max_writes = simple_strtoul(w, &w, 0); } if (*w == ',') w += 1; wp = kzalloc(sizeof(*wp), GFP_KERNEL); if (!wp) { NS_ERR("unable to allocate memory.\n"); return -ENOMEM; } wp->page_no = page_no; wp->max_writes = max_writes; list_add(&wp->list, &weak_pages); } while (*w); return 0;}static int write_error(unsigned int page_no){ struct weak_page *wp; list_for_each_entry(wp, &weak_pages, list) if (wp->page_no == page_no) { if (wp->writes_done >= wp->max_writes) return 1; wp->writes_done += 1; return 0; } return 0;}static int parse_gravepages(void){ char *g; int zero_ok; unsigned int page_no; unsigned int max_reads; struct grave_page *gp; if (!gravepages) return 0; g = gravepages; do { zero_ok = (*g == '0' ? 1 : 0); page_no = simple_strtoul(g, &g, 0); if (!zero_ok && !page_no) { NS_ERR("invalid gravepagess.\n"); return -EINVAL; } max_reads = 3; if (*g == ':') { g += 1; max_reads = simple_strtoul(g, &g, 0); } if (*g == ',') g += 1; gp = kzalloc(sizeof(*gp), GFP_KERNEL); if (!gp) { NS_ERR("unable to allocate memory.\n"); return -ENOMEM; } gp->page_no = page_no; gp->max_reads = max_reads; list_add(&gp->list, &grave_pages); } while (*g); return 0;}static int read_error(unsigned int page_no){ struct grave_page *gp; list_for_each_entry(gp, &grave_pages, list) if (gp->page_no == page_no) { if (gp->reads_done >= gp->max_reads) return 1; gp->reads_done += 1; return 0; } return 0;}static void free_lists(void){ struct list_head *pos, *n; list_for_each_safe(pos, n, &weak_blocks) { list_del(pos); kfree(list_entry(pos, struct weak_block, list)); } list_for_each_safe(pos, n, &weak_pages) { list_del(pos); kfree(list_entry(pos, struct weak_page, list)); } list_for_each_safe(pos, n, &grave_pages) { list_del(pos); kfree(list_entry(pos, struct grave_page, list)); } kfree(erase_block_wear);}static int setup_wear_reporting(struct mtd_info *mtd){ size_t mem; if (!rptwear) return 0; wear_eb_count = divide(mtd->size, mtd->erasesize); mem = wear_eb_count * sizeof(unsigned long); if (mem / sizeof(unsigned long) != wear_eb_count) { NS_ERR("Too many erase blocks for wear reporting\n"); return -ENOMEM; } erase_block_wear = kzalloc(mem, GFP_KERNEL); if (!erase_block_wear) { NS_ERR("Too many erase blocks for wear reporting\n"); return -ENOMEM; } return 0;}static void update_wear(unsigned int erase_block_no){ unsigned long wmin = -1, wmax = 0, avg; unsigned long deciles[10], decile_max[10], tot = 0; unsigned int i; if (!erase_block_wear) return; total_wear += 1; if (total_wear == 0) NS_ERR("Erase counter total overflow\n"); erase_block_wear[erase_block_no] += 1; if (erase_block_wear[erase_block_no] == 0) NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); rptwear_cnt += 1; if (rptwear_cnt < rptwear) return; rptwear_cnt = 0; /* Calc wear stats */ for (i = 0; i < wear_eb_count; ++i) { unsigned long wear = erase_block_wear[i]; if (wear < wmin) wmin = wear; if (wear > wmax) wmax = wear; tot += wear; } for (i = 0; i < 9; ++i) { deciles[i] = 0; decile_max[i] = (wmax * (i + 1) + 5) / 10; } deciles[9] = 0; decile_max[9] = wmax; for (i = 0; i < wear_eb_count; ++i) { int d; unsigned long wear = erase_block_wear[i]; for (d = 0; d < 10; ++d) if (wear <= decile_max[d]) { deciles[d] += 1; break; } } avg = tot / wear_eb_count; /* Output wear report */ NS_INFO("*** Wear Report ***\n"); NS_INFO("Total numbers of erases: %lu\n", tot); NS_INFO("Number of erase blocks: %u\n", wear_eb_count); NS_INFO("Average number of erases: %lu\n", avg); NS_INFO("Maximum number of erases: %lu\n", wmax); NS_INFO("Minimum number of erases: %lu\n", wmin); for (i = 0; i < 10; ++i) { unsigned long from = (i ? decile_max[i - 1] + 1 : 0); if (from > decile_max[i]) continue; NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", from, decile_max[i], deciles[i]); } NS_INFO("*** End of Wear Report ***\n");}/* * Returns the string representation of 'state' state. */static char *get_state_name(uint32_t state){ switch (NS_STATE(state)) { case STATE_CMD_READ0: return "STATE_CMD_READ0"; case STATE_CMD_READ1: return "STATE_CMD_READ1"; case STATE_CMD_PAGEPROG: return "STATE_CMD_PAGEPROG"; case STATE_CMD_READOOB: return "STATE_CMD_READOOB"; case STATE_CMD_READSTART: return "STATE_CMD_READSTART"; case STATE_CMD_ERASE1: return "STATE_CMD_ERASE1"; case STATE_CMD_STATUS: return "STATE_CMD_STATUS"; case STATE_CMD_STATUS_M: return "STATE_CMD_STATUS_M"; case STATE_CMD_SEQIN: return "STATE_CMD_SEQIN"; case STATE_CMD_READID: return "STATE_CMD_READID"; case STATE_CMD_ERASE2: return "STATE_CMD_ERASE2"; case STATE_CMD_RESET: return "STATE_CMD_RESET"; case STATE_CMD_RNDOUT: return "STATE_CMD_RNDOUT"; case STATE_CMD_RNDOUTSTART: return "STATE_CMD_RNDOUTSTART"; case STATE_ADDR_PAGE: return "STATE_ADDR_PAGE"; case STATE_ADDR_SEC: return "STATE_ADDR_SEC"; case STATE_ADDR_ZERO: return "STATE_ADDR_ZERO"; case STATE_ADDR_COLUMN: return "STATE_ADDR_COLUMN"; case STATE_DATAIN: return "STATE_DATAIN"; case STATE_DATAOUT: return "STATE_DATAOUT"; case STATE_DATAOUT_ID: return "STATE_DATAOUT_ID"; case STATE_DATAOUT_STATUS: return "STATE_DATAOUT_STATUS"; case STATE_DATAOUT_STATUS_M: return "STATE_DATAOUT_STATUS_M"; case STATE_READY: return "STATE_READY"; case STATE_UNKNOWN: return "STATE_UNKNOWN"; } NS_ERR("get_state_name: unknown state, BUG\n"); return NULL;}/* * Check if command is valid. * * RETURNS: 1 if wrong command, 0 if right. */static int check_command(int cmd){ switch (cmd) { case NAND_CMD_READ0: case NAND_CMD_READ1: case NAND_CMD_READSTART: case NAND_CMD_PAGEPROG: case NAND_CMD_READOOB: case NAND_CMD_ERASE1: case NAND_CMD_STATUS: case NAND_CMD_SEQIN: case NAND_CMD_READID: case NAND_CMD_ERASE2: case NAND_CMD_RESET: case NAND_CMD_RNDOUT: case NAND_CMD_RNDOUTSTART: return 0; case NAND_CMD_STATUS_MULTI: default: return 1; }}/* * Returns state after command is accepted by command number. */static uint32_t get_state_by_command(unsigned command){ switch (command) { case NAND_CMD_READ0: return STATE_CMD_READ0; case NAND_CMD_READ1: return STATE_CMD_READ1; case NAND_CMD_PAGEPROG: return STATE_CMD_PAGEPROG; case NAND_CMD_READSTART: return STATE_CMD_READSTART; case NAND_CMD_READOOB: return STATE_CMD_READOOB; case NAND_CMD_ERASE1: return STATE_CMD_ERASE1; case NAND_CMD_STATUS: return STATE_CMD_STATUS; case NAND_CMD_STATUS_MULTI: return STATE_CMD_STATUS_M; case NAND_CMD_SEQIN: return STATE_CMD_SEQIN; case NAND_CMD_READID: return STATE_CMD_READID; case NAND_CMD_ERASE2: return STATE_CMD_ERASE2; case NAND_CMD_RESET: return STATE_CMD_RESET; case NAND_CMD_RNDOUT: return STATE_CMD_RNDOUT; case NAND_CMD_RNDOUTSTART: return STATE_CMD_RNDOUTSTART; } NS_ERR("get_state_by_command: unknown command, BUG\n"); return 0;}/* * Move an address byte to the correspondent internal register. */static inline void accept_addr_byte(struct nandsim *ns, u_char bt){ uint byte = (uint)bt;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -