diff options
Diffstat (limited to 'usr.sbin/nandsim/nandsim.c')
-rw-r--r-- | usr.sbin/nandsim/nandsim.c | 1397 |
1 files changed, 1397 insertions, 0 deletions
diff --git a/usr.sbin/nandsim/nandsim.c b/usr.sbin/nandsim/nandsim.c new file mode 100644 index 0000000..082085f --- /dev/null +++ b/usr.sbin/nandsim/nandsim.c @@ -0,0 +1,1397 @@ +/*- + * Copyright (C) 2009-2012 Semihalf + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Control application for the NAND simulator. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <dev/nand/nandsim.h> +#include <dev/nand/nand_dev.h> + +#include <ctype.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdlib.h> +#include <limits.h> +#include <sysexits.h> + +#include "nandsim_cfgparse.h" + +#define SIMDEVICE "/dev/nandsim.ioctl" + +#define error(fmt, args...) do { \ + printf("ERROR: " fmt "\n", ##args); } while (0) + +#define warn(fmt, args...) do { \ + printf("WARNING: " fmt "\n", ##args); } while (0) + +#define DEBUG +#undef DEBUG + +#ifdef DEBUG +#define debug(fmt, args...) do { \ + printf("NANDSIM_CONF:" fmt "\n", ##args); } while (0) +#else +#define debug(fmt, args...) do {} while(0) +#endif + +#define NANDSIM_RAM_LOG_SIZE 16384 + +#define MSG_NOTRUNNING "Controller#%d is not running.Please start" \ + " it first." +#define MSG_RUNNING "Controller#%d is already running!" +#define MSG_CTRLCHIPNEEDED "You have to specify ctrl_no:cs_no pair!" +#define MSG_STATUSACQCTRLCHIP "Could not acquire status for ctrl#%d chip#%d" +#define MSG_STATUSACQCTRL "Could not acquire status for ctrl#%d" +#define MSG_NOCHIP "There is no such chip configured (chip#%d "\ + "at ctrl#%d)!" + +#define MSG_NOCTRL "Controller#%d is not configured!" +#define MSG_NOTCONFIGDCTRLCHIP "Chip connected to ctrl#%d at cs#%d " \ + "is not configured." + +typedef int (commandfunc_t)(int , char **); + +static struct nandsim_command *getcommand(char *); +static int parse_devstring(char *, int *, int *); +static void printchip(struct sim_chip *, uint8_t); +static void printctrl(struct sim_ctrl *); +static int opendev(int *); +static commandfunc_t cmdstatus; +static commandfunc_t cmdconf; +static commandfunc_t cmdstart; +static commandfunc_t cmdstop; +static commandfunc_t cmdmod; +static commandfunc_t cmderror; +static commandfunc_t cmdbb; +static commandfunc_t cmdfreeze; +static commandfunc_t cmdlog; +static commandfunc_t cmdstats; +static commandfunc_t cmddump; +static commandfunc_t cmdrestore; +static commandfunc_t cmddestroy; +static commandfunc_t cmdhelp; +static int checkusage(int, int, char **); +static int is_chip_created(int, int, int *); +static int is_ctrl_created(int, int *); +static int is_ctrl_running(int, int *); +static int assert_chip_connected(int , int); +static int printstats(int, int, uint32_t, int); + +struct nandsim_command { + const char *cmd_name; /* Command name */ + commandfunc_t *commandfunc; /* Ptr to command function */ + uint8_t req_argc; /* Mandatory arguments count */ + const char *usagestring; /* Usage string */ +}; + +static struct nandsim_command commands[] = { + {"status", cmdstatus, 1, + "status <ctl_no|--all|-a> [-v]\n" }, + {"conf", cmdconf, 1, + "conf <filename>\n" }, + {"start", cmdstart, 1, + "start <ctrl_no>\n" }, + {"mod", cmdmod, 2, + "mod [-l <loglevel>] | <ctl_no:cs_no> [-p <prog_time>]\n" + "\t[-e <erase_time>] [-r <read_time>]\n" + "\t[-E <error_ratio>] | [-h]\n" }, + {"stop", cmdstop, 1, + "stop <ctrl_no>\n" }, + {"error", cmderror, 5, + "error <ctrl_no:cs_no> <page_num> <column> <length> <pattern>\n" }, + {"bb", cmdbb, 2, + "bb <ctl_no:cs_no> [blk_num1,blk_num2,..] [-U] [-L]\n" }, + {"freeze", cmdfreeze, 1, + "freeze [ctrl_no]\n" }, + {"log", cmdlog, 1, + "log <ctrl_no|--all|-a>\n" }, + {"stats", cmdstats, 2, + "stats <ctrl_no:cs_no> <pagenumber>\n" }, + {"dump", cmddump, 2, + "dump <ctrl_no:cs_no> <filename>\n" }, + {"restore", cmdrestore, 2, + "restore <ctrl_no:chip_no> <filename>\n" }, + {"destroy", cmddestroy, 1, + "destroy <ctrl_no[:cs_no]|--all|-a>\n" }, + {"help", cmdhelp, 0, + "help [-v]" }, + {NULL, NULL, 0, NULL}, +}; + + +/* Parse command name, and start appropriate function */ +static struct nandsim_command* +getcommand(char *arg) +{ + struct nandsim_command *opts; + + for (opts = commands; (opts != NULL) && + (opts->cmd_name != NULL); opts++) { + if (strcmp(opts->cmd_name, arg) == 0) + return (opts); + } + return (NULL); +} + +/* + * Parse given string in format <ctrl_no>:<cs_no>, if possible -- set + * ctrl and/or cs, and return 0 (success) or 1 (in case of error). + * + * ctrl == 0xff && chip == 0xff : '--all' flag specified + * ctrl != 0xff && chip != 0xff : both ctrl & chip were specified + * ctrl != 0xff && chip == 0xff : only ctrl was specified + */ +static int +parse_devstring(char *str, int *ctrl, int *cs) +{ + char *tmpstr; + unsigned int num = 0; + + /* Ignore white spaces at the beginning */ + while (isspace(*str) && (*str != '\0')) + str++; + + *ctrl = 0xff; + *cs = 0xff; + if (strcmp(str, "--all") == 0 || + strcmp(str, "-a") == 0) { + /* If --all or -a is specified, ctl==chip==0xff */ + debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); + return (0); + } + /* Separate token and try to convert it to int */ + tmpstr = (char *)strtok(str, ":"); + if ((tmpstr != NULL) && (*tmpstr != '\0')) { + if (convert_arguint(tmpstr, &num) != 0) + return (1); + + if (num > MAX_SIM_DEV - 1) { + error("Invalid ctrl_no supplied: %s. Valid ctrl_no " + "value must lie between 0 and 3!", tmpstr); + return (1); + } + + *ctrl = num; + tmpstr = (char *)strtok(NULL, ":"); + + if ((tmpstr != NULL) && (*tmpstr != '\0')) { + if (convert_arguint(tmpstr, &num) != 0) + return (1); + + /* Check if chip_no is valid */ + if (num > MAX_CTRL_CS - 1) { + error("Invalid chip_no supplied: %s. Valid " + "chip_no value must lie between 0 and 3!", + tmpstr); + return (1); + } + *cs = num; + } + } else + /* Empty devstring supplied */ + return (1); + + debug("CTRL=%d CHIP=%d\n", *ctrl, *cs); + return (0); +} + +static int +opendev(int *fd) +{ + + *fd = open(SIMDEVICE, O_RDWR); + if (*fd == -1) { + error("Could not open simulator device file (%s)!", + SIMDEVICE); + return (EX_OSFILE); + } + return (EX_OK); +} + +static int +opencdev(int *cdevd, int ctrl, int chip) +{ + char fname[255]; + + sprintf(fname, "/dev/nandsim%d.%d", ctrl, chip); + *cdevd = open(fname, O_RDWR); + if (*cdevd == -1) + return (EX_NOINPUT); + + return (EX_OK); +} + +/* + * Check if given arguments count match requirements. If no, or + * --help (-h) flag is specified -- return 1 (print usage) + */ +static int +checkusage(int gargc, int argsreqd, char **gargv) +{ + + if (gargc < argsreqd + 2 || (gargc >= (argsreqd + 2) && + (strcmp(gargv[1], "--help") == 0 || + strcmp(gargv[1], "-h") == 0))) + return (1); + + return (0); +} + +static int +cmdstatus(int gargc, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, idx, idx2, start, stop; + uint8_t verbose = 0; + struct sim_ctrl ctrlconf; + struct sim_chip chipconf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) { + return (EX_USAGE); + } else if (ctl == 0xff) { + /* Every controller */ + start = 0; + stop = MAX_SIM_DEV-1; + } else { + /* Specified controller only */ + start = ctl; + stop = ctl; + } + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (idx = 0; idx < gargc; idx ++) + if (strcmp(gargv[idx], "-v") == 0 || + strcmp(gargv[idx], "--verbose") == 0) + verbose = 1; + + for (idx = start; idx <= stop; idx++) { + ctrlconf.num = idx; + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrlconf); + if (err) { + err = EX_SOFTWARE; + error(MSG_STATUSACQCTRL, idx); + continue; + } + + printctrl(&ctrlconf); + + for (idx2 = 0; idx2 < MAX_CTRL_CS; idx2++) { + chipconf.num = idx2; + chipconf.ctrl_num = idx; + + err = ioctl(fd, NANDSIM_STATUS_CHIP, &chipconf); + if (err) { + err = EX_SOFTWARE; + error(MSG_STATUSACQCTRL, idx); + continue; + } + + printchip(&chipconf, verbose); + } + } + close(fd); + return (err); +} + +static int +cmdconf(int gargc __unused, char **gargv) +{ + int err; + + err = parse_config(gargv[2], SIMDEVICE); + if (err) + return (EX_DATAERR); + + return (EX_OK); +} + +static int +cmdstart(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, running, state; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + err = is_ctrl_created(ctl, &state); + if (err) { + return (EX_SOFTWARE); + } else if (state == 0) { + error(MSG_NOCTRL, ctl); + return (EX_SOFTWARE); + } + + err = is_ctrl_running(ctl, &running); + if (err) + return (EX_SOFTWARE); + + if (running) { + warn(MSG_RUNNING, ctl); + } else { + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_START_CTRL, &ctl); + close(fd); + if (err) { + error("Cannot start controller#%d", ctl); + err = EX_SOFTWARE; + } + } + return (err); +} + +static int +cmdstop(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, running; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + err = is_ctrl_running(ctl, &running); + if (err) + return (EX_SOFTWARE); + + if (!running) { + error(MSG_NOTRUNNING, ctl); + } else { + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STOP_CTRL, &ctl); + close(fd); + if (err) { + error("Cannot stop controller#%d", ctl); + err = EX_SOFTWARE; + } + } + + return (err); +} + +static int +cmdmod(int gargc __unused, char **gargv) +{ + int chip, ctl, err = 0, fd = -1, i; + struct sim_mod mods; + + if (gargc >= 4) { + if (strcmp(gargv[2], "--loglevel") == 0 || strcmp(gargv[2], + "-l") == 0) { + /* Set loglevel (ctrl:chip pair independant) */ + mods.field = SIM_MOD_LOG_LEVEL; + + if (convert_arguint(gargv[3], &mods.new_value) != 0) + return (EX_SOFTWARE); + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_MODIFY, &mods); + if (err) { + error("simulator parameter %s could not be " + "modified !", gargv[3]); + close(fd); + return (EX_SOFTWARE); + } + + debug("request : loglevel = %d\n", mods.new_value); + close(fd); + return (EX_OK); + } + } + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + else if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + /* Find out which flags were passed */ + for (i = 3; i < gargc; i++) { + + if (convert_arguint(gargv[i + 1], &mods.new_value) != 0) + continue; + + if (strcmp(gargv[i], "--prog-time") == 0 || + strcmp(gargv[i], "-p") == 0) { + + mods.field = SIM_MOD_PROG_TIME; + debug("request : progtime = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--erase-time") == 0 || + strcmp(gargv[i], "-e") == 0) { + + mods.field = SIM_MOD_ERASE_TIME; + debug("request : eraseime = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--read-time") == 0 || + strcmp(gargv[i], "-r") == 0) { + + mods.field = SIM_MOD_READ_TIME; + debug("request : read_time = %d\n", mods.new_value); + + } else if (strcmp(gargv[i], "--error-ratio") == 0 || + strcmp(gargv[i], "-E") == 0) { + + mods.field = SIM_MOD_ERROR_RATIO; + debug("request : error_ratio = %d\n", mods.new_value); + + } else { + /* Flag not recognized, or nothing specified. */ + error("Unrecognized flag:%s\n", gargv[i]); + if (fd >= 0) + close(fd); + return (EX_USAGE); + } + + mods.chip_num = chip; + mods.ctrl_num = ctl; + + /* Call appropriate ioctl */ + err = ioctl(fd, NANDSIM_MODIFY, &mods); + if (err) { + error("simulator parameter %s could not be modified! ", + gargv[i]); + continue; + } + i++; + } + close(fd); + return (EX_OK); +} + +static int +cmderror(int gargc __unused, char **gargv) +{ + uint32_t page, column, len, pattern; + int chip = 0, ctl = 0, err = 0, fd; + struct sim_error sim_err; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (convert_arguint(gargv[3], &page) || + convert_arguint(gargv[4], &column) || + convert_arguint(gargv[5], &len) || + convert_arguint(gargv[6], &pattern)) + return (EX_SOFTWARE); + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + sim_err.page_num = page; + sim_err.column = column; + sim_err.len = len; + sim_err.pattern = pattern; + sim_err.ctrl_num = ctl; + sim_err.chip_num = chip; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_INJECT_ERROR, &sim_err); + + close(fd); + if (err) { + error("Could not inject error !"); + return (EX_SOFTWARE); + } + return (EX_OK); +} + +static int +cmdbb(int gargc, char **gargv) +{ + struct sim_block_state bs; + struct chip_param_io cparams; + uint32_t blkidx; + int c, cdevd, chip = 0, ctl = 0, err = 0, fd, idx; + uint8_t flagL = 0, flagU = 0; + int *badblocks = NULL; + + /* Check for --list/-L or --unmark/-U flags */ + for (idx = 3; idx < gargc; idx++) { + if (strcmp(gargv[idx], "--list") == 0 || + strcmp(gargv[idx], "-L") == 0) + flagL = idx; + if (strcmp(gargv[idx], "--unmark") == 0 || + strcmp(gargv[idx], "-U") == 0) + flagU = idx; + } + + if (flagL == 2 || flagU == 2 || flagU == 3) + return (EX_USAGE); + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) { + return (EX_USAGE); + } + if (chip == 0xff || ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&cdevd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) + return (EX_SOFTWARE); + + close(cdevd); + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + if (flagL != 3) { + /* + * Flag -L was specified either after blocklist or was not + * specified at all. + */ + c = parse_intarray(gargv[3], &badblocks); + + for (idx = 0; idx < c; idx++) { + bs.block_num = badblocks[idx]; + /* Do not change wearout */ + bs.wearout = -1; + bs.state = (flagU == 0) ? NANDSIM_BAD_BLOCK : + NANDSIM_GOOD_BLOCK; + + err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); + if (err) { + error("Could not set bad block(%d) for " + "controller (%d)!", + badblocks[idx], ctl); + err = EX_SOFTWARE; + break; + } + } + } + if (flagL != 0) { + /* If flag -L was specified (anywhere) */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + bs.block_num = blkidx; + /* Do not change the wearout */ + bs.wearout = -1; + err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); + if (err) { + error("Could not acquire block state"); + err = EX_SOFTWARE; + continue; + } + printf("Block#%d: wear count: %d %s\n", blkidx, + bs.wearout, + (bs.state == NANDSIM_BAD_BLOCK) ? "BAD":"GOOD"); + } + } + close(fd); + return (err); +} + +static int +cmdfreeze(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, i, start = 0, state, stop = 0; + struct sim_ctrl_chip ctrlchip; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (ctl == 0xff) { + error("You have to specify at least controller number"); + return (EX_USAGE); + } + + if (ctl != 0xff && chip == 0xff) { + start = 0; + stop = MAX_CTRL_CS - 1; + } else { + start = chip; + stop = chip; + } + + ctrlchip.ctrl_num = ctl; + + err = is_ctrl_running(ctl, &state); + if (err) + return (EX_SOFTWARE); + if (state == 0) { + error(MSG_NOTRUNNING, ctl); + return (EX_SOFTWARE); + } + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (i = start; i <= stop; i++) { + err = is_chip_created(ctl, i, &state); + if (err) + return (EX_SOFTWARE); + else if (state == 0) { + continue; + } + + ctrlchip.chip_num = i; + err = ioctl(fd, NANDSIM_FREEZE, &ctrlchip); + if (err) { + error("Could not freeze ctrl#%d chip#%d", ctl, i); + close(fd); + return (EX_SOFTWARE); + } + } + close(fd); + return (EX_OK); +} + +static int +cmdlog(int gargc __unused, char **gargv) +{ + struct sim_log log; + int chip = 0, ctl = 0, err = 0, fd, idx, start = 0, stop = 0; + char *logbuf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + logbuf = (char *)malloc(sizeof(char) * NANDSIM_RAM_LOG_SIZE); + if (logbuf == NULL) { + error("Not enough memory to create log buffer"); + return (EX_SOFTWARE); + } + + memset(logbuf, 0, NANDSIM_RAM_LOG_SIZE); + log.log = logbuf; + log.len = NANDSIM_RAM_LOG_SIZE; + + if (ctl == 0xff) { + start = 0; + stop = MAX_SIM_DEV-1; + } else { + start = ctl; + stop = ctl; + } + + if (opendev(&fd) != EX_OK) { + free(logbuf); + return (EX_OSFILE); + } + + /* Print logs for selected controller(s) */ + for (idx = start; idx <= stop; idx++) { + log.ctrl_num = idx; + + err = ioctl(fd, NANDSIM_PRINT_LOG, &log); + if (err) { + error("Could not get log for controller %d!", idx); + continue; + } + + printf("Logs for controller#%d:\n%s\n", idx, logbuf); + } + + free(logbuf); + close(fd); + return (EX_OK); +} + +static int +cmdstats(int gargc __unused, char **gargv) +{ + int cdevd, chip = 0, ctl = 0, err = 0; + uint32_t pageno = 0; + + err = parse_devstring(gargv[2], &ctl, &chip); + + if (err) + return (EX_USAGE); + + if (chip == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (convert_arguint(gargv[3], &pageno) != 0) + return (EX_USAGE); + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&cdevd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = printstats(ctl, chip, pageno, cdevd); + if (err) { + close(cdevd); + return (EX_SOFTWARE); + } + close(cdevd); + return (EX_OK); +} + +static int +cmddump(int gargc __unused, char **gargv) +{ + struct sim_dump dump; + struct sim_block_state bs; + struct chip_param_io cparams; + int chip = 0, ctl = 0, err = EX_OK, fd, dumpfd; + uint32_t blkidx, bwritten = 0, totalwritten = 0; + void *buf; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + + if (chip == 0xff || ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + if (opencdev(&fd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) { + error("Cannot get parameters for chip %d:%d", ctl, chip); + close(fd); + return (EX_SOFTWARE); + } + close(fd); + + dump.ctrl_num = ctl; + dump.chip_num = chip; + + dump.len = cparams.pages_per_block * (cparams.page_size + + cparams.oob_size); + + buf = malloc(dump.len); + if (buf == NULL) { + error("Could not allocate memory!"); + return (EX_SOFTWARE); + } + dump.data = buf; + + errno = 0; + dumpfd = open(gargv[3], O_WRONLY | O_CREAT, 0666); + if (dumpfd == -1) { + error("Cannot create dump file."); + free(buf); + return (EX_SOFTWARE); + } + + if (opendev(&fd)) { + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + bs.ctrl_num = ctl; + bs.chip_num = chip; + + /* First uint32_t in file shall contain block count */ + if (write(dumpfd, &cparams, sizeof(cparams)) < (int)sizeof(cparams)) { + error("Error writing to dumpfile!"); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + /* + * First loop acquires blocks states and writes them to + * the dump file. + */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + bs.block_num = blkidx; + err = ioctl(fd, NANDSIM_GET_BLOCK_STATE, &bs); + if (err) { + error("Could not get bad block(%d) for " + "controller (%d)!", blkidx, ctl); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + + bwritten = write(dumpfd, &bs, sizeof(bs)); + if (bwritten != sizeof(bs)) { + error("Error writing to dumpfile"); + close(fd); + close(dumpfd); + free(buf); + return (EX_SOFTWARE); + } + } + + /* Second loop dumps the data */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + debug("Block#%d...", blkidx); + dump.block_num = blkidx; + + err = ioctl(fd, NANDSIM_DUMP, &dump); + if (err) { + error("Could not dump ctrl#%d chip#%d " + "block#%d", ctl, chip, blkidx); + err = EX_SOFTWARE; + break; + } + + bwritten = write(dumpfd, dump.data, dump.len); + if (bwritten != dump.len) { + error("Error writing to dumpfile"); + err = EX_SOFTWARE; + break; + } + debug("OK!\n"); + totalwritten += bwritten; + } + printf("%d out of %d B written.\n", totalwritten, dump.len * blkidx); + + close(fd); + close(dumpfd); + free(buf); + return (err); +} + +static int +cmdrestore(int gargc __unused, char **gargv) +{ + struct sim_dump dump; + struct sim_block_state bs; + struct stat filestat; + int chip = 0, ctl = 0, err = 0, fd, dumpfd = -1; + uint32_t blkidx, blksz, fsize = 0, expfilesz; + void *buf; + struct chip_param_io cparams, dumpcparams; + + err = parse_devstring(gargv[2], &ctl, &chip); + if (err) + return (EX_USAGE); + else if (ctl == 0xff) { + error(MSG_CTRLCHIPNEEDED); + return (EX_USAGE); + } + + if (!assert_chip_connected(ctl, chip)) + return (EX_SOFTWARE); + + /* Get chip geometry */ + if (opencdev(&fd, ctl, chip) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NAND_IO_GET_CHIP_PARAM, &cparams); + if (err) { + error("Cannot get parameters for chip %d:%d", ctl, chip); + close(fd); + return (err); + } + close(fd); + + /* Obtain dump file size */ + errno = 0; + if (stat(gargv[3], &filestat) != 0) { + error("Could not acquire file size! : %s", + strerror(errno)); + return (EX_IOERR); + } + + fsize = filestat.st_size; + blksz = cparams.pages_per_block * (cparams.page_size + + cparams.oob_size); + + /* Expected dump file size for chip */ + expfilesz = cparams.blocks * (blksz + sizeof(bs)) + sizeof(cparams); + + if (fsize != expfilesz) { + error("File size does not match chip geometry (file size: %d" + ", dump size: %d)", fsize, expfilesz); + return (EX_SOFTWARE); + } + + dumpfd = open(gargv[3], O_RDONLY); + if (dumpfd == -1) { + error("Could not open dump file!"); + return (EX_IOERR); + } + + /* Read chip params saved in dumpfile */ + read(dumpfd, &dumpcparams, sizeof(dumpcparams)); + + /* XXX */ + if (bcmp(&dumpcparams, &cparams, sizeof(cparams)) != 0) { + error("Supplied dump is created for a chip with different " + "chip configuration!"); + close(dumpfd); + return (EX_SOFTWARE); + } + + if (opendev(&fd) != EX_OK) { + close(dumpfd); + return (EX_OSFILE); + } + + buf = malloc(blksz); + if (buf == NULL) { + error("Could not allocate memory for block buffer"); + close(dumpfd); + close(fd); + return (EX_SOFTWARE); + } + + dump.ctrl_num = ctl; + dump.chip_num = chip; + dump.data = buf; + /* Restore block states and wearouts */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + dump.block_num = blkidx; + if (read(dumpfd, &bs, sizeof(bs)) != sizeof(bs)) { + error("Error reading dumpfile"); + close(dumpfd); + close(fd); + free(buf); + return (EX_SOFTWARE); + } + bs.ctrl_num = ctl; + bs.chip_num = chip; + debug("BLKIDX=%d BLOCKS=%d CTRL=%d CHIP=%d STATE=%d\n" + "WEAROUT=%d BS.CTRL_NUM=%d BS.CHIP_NUM=%d\n", + blkidx, cparams.blocks, dump.ctrl_num, dump.chip_num, + bs.state, bs.wearout, bs.ctrl_num, bs.chip_num); + + err = ioctl(fd, NANDSIM_SET_BLOCK_STATE, &bs); + if (err) { + error("Could not set bad block(%d) for " + "controller: %d, chip: %d!", blkidx, ctl, chip); + close(dumpfd); + close(fd); + free(buf); + return (EX_SOFTWARE); + } + } + /* Restore data */ + for (blkidx = 0; blkidx < cparams.blocks; blkidx++) { + errno = 0; + dump.len = read(dumpfd, buf, blksz); + if (errno) { + error("Failed to read block#%d from dumpfile.", blkidx); + err = EX_SOFTWARE; + break; + } + dump.block_num = blkidx; + err = ioctl(fd, NANDSIM_RESTORE, &dump); + if (err) { + error("Could not restore block#%d of ctrl#%d chip#%d" + ": %s", blkidx, ctl, chip, strerror(errno)); + err = EX_SOFTWARE; + break; + } + } + + free(buf); + close(dumpfd); + close(fd); + return (err); + +} + +static int +cmddestroy(int gargc __unused, char **gargv) +{ + int chip = 0, ctl = 0, err = 0, fd, idx, idx2, state; + int chipstart, chipstop, ctrlstart, ctrlstop; + struct sim_chip_destroy chip_destroy; + + err = parse_devstring(gargv[2], &ctl, &chip); + + if (err) + return (EX_USAGE); + + if (ctl == 0xff) { + /* Every chip at every controller */ + ctrlstart = chipstart = 0; + ctrlstop = MAX_SIM_DEV - 1; + chipstop = MAX_CTRL_CS - 1; + } else { + ctrlstart = ctrlstop = ctl; + if (chip == 0xff) { + /* Every chip at selected controller */ + chipstart = 0; + chipstop = MAX_CTRL_CS - 1; + } else + /* Selected chip at selected controller */ + chipstart = chipstop = chip; + } + debug("CTRLSTART=%d CTRLSTOP=%d CHIPSTART=%d CHIPSTOP=%d\n", + ctrlstart, ctrlstop, chipstart, chipstop); + for (idx = ctrlstart; idx <= ctrlstop; idx++) { + err = is_ctrl_created(idx, &state); + if (err) { + error("Could not acquire ctrl#%d state. Cannot " + "destroy controller.", idx); + return (EX_SOFTWARE); + } + if (state == 0) { + continue; + } + err = is_ctrl_running(idx, &state); + if (err) { + error(MSG_STATUSACQCTRL, idx); + return (EX_SOFTWARE); + } + if (state != 0) { + error(MSG_RUNNING, ctl); + return (EX_SOFTWARE); + } + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + for (idx2 = chipstart; idx2 <= chipstop; idx2++) { + err = is_chip_created(idx, idx2, &state); + if (err) { + error(MSG_STATUSACQCTRLCHIP, idx2, idx); + continue; + } + if (state == 0) + /* There is no such chip running */ + continue; + chip_destroy.ctrl_num = idx; + chip_destroy.chip_num = idx2; + ioctl(fd, NANDSIM_DESTROY_CHIP, + &chip_destroy); + } + /* If chip isn't explicitly specified -- destroy ctrl */ + if (chip == 0xff) { + err = ioctl(fd, NANDSIM_DESTROY_CTRL, &idx); + if (err) { + error("Could not destroy ctrl#%d", idx); + continue; + } + } + close(fd); + } + return (err); +} + +int +main(int argc, char **argv) +{ + struct nandsim_command *cmdopts; + int retcode = 0; + + if (argc < 2) { + cmdhelp(argc, argv); + retcode = EX_USAGE; + } else { + cmdopts = getcommand(argv[1]); + if (cmdopts != NULL && cmdopts->commandfunc != NULL) { + if (checkusage(argc, cmdopts->req_argc, argv) == 1) { + /* Print command specific usage */ + printf("nandsim %s", cmdopts->usagestring); + return (EX_USAGE); + } + retcode = cmdopts->commandfunc(argc, argv); + + if (retcode == EX_USAGE) { + /* Print command-specific usage */ + printf("nandsim %s", cmdopts->usagestring); + } else if (retcode == EX_OSFILE) { + error("Could not open device file"); + } + + } else { + error("Unknown command!"); + retcode = EX_USAGE; + } + } + return (retcode); +} + +static int +cmdhelp(int gargc __unused, char **gargv __unused) +{ + struct nandsim_command *opts; + + printf("usage: nandsim <command> [command params] [params]\n\n"); + + for (opts = commands; (opts != NULL) && + (opts->cmd_name != NULL); opts++) + printf("nandsim %s", opts->usagestring); + + printf("\n"); + return (EX_OK); +} + +static void +printchip(struct sim_chip *chip, uint8_t verbose) +{ + + if (chip->created == 0) + return; + if (verbose > 0) { + printf("\n[Chip info]\n"); + printf("num= %d\nctrl_num=%d\ndevice_id=%02x" + "\tmanufacturer_id=%02x\ndevice_model=%s\nmanufacturer=" + "%s\ncol_addr_cycles=%d\nrow_addr_cycles=%d" + "\npage_size=%d\noob_size=%d\npages_per_block=%d\n" + "blocks_per_lun=%d\nluns=%d\n\nprog_time=%d\n" + "erase_time=%d\nread_time=%d\n" + "error_ratio=%d\nwear_level=%d\nwrite_protect=%c\n" + "chip_width=%db\n", chip->num, chip->ctrl_num, + chip->device_id, chip->manufact_id,chip->device_model, + chip->manufacturer, chip->col_addr_cycles, + chip->row_addr_cycles, chip->page_size, + chip->oob_size, chip->pgs_per_blk, chip->blks_per_lun, + chip->luns,chip->prog_time, chip->erase_time, + chip->read_time, chip->error_ratio, chip->wear_level, + (chip->is_wp == 0) ? 'N':'Y', chip->width); + } else { + printf("[Chip info]\n"); + printf("\tnum=%d\n\tdevice_model=%s\n\tmanufacturer=%s\n" + "\tpage_size=%d\n\twrite_protect=%s\n", + chip->num, chip->device_model, chip->manufacturer, + chip->page_size, (chip->is_wp == 0) ? "NO":"YES"); + } +} + +static void +printctrl(struct sim_ctrl *ctrl) +{ + int i; + + if (ctrl->created == 0) { + printf(MSG_NOCTRL "\n", ctrl->num); + return; + } + printf("\n[Controller info]\n"); + printf("\trunning: %s\n", ctrl->running ? "yes" : "no"); + printf("\tnum cs: %d\n", ctrl->num_cs); + printf("\tecc: %d\n", ctrl->ecc); + printf("\tlog_filename: %s\n", ctrl->filename); + printf("\tecc_layout:"); + for (i = 0; i < MAX_ECC_BYTES; i++) { + if (ctrl->ecc_layout[i] == 0xffff) + break; + else + printf("%c%d", i%16 ? ' ' : '\n', + ctrl->ecc_layout[i]); + } + printf("\n"); +} + +static int +is_ctrl_running(int ctrl_no, int *running) +{ + struct sim_ctrl ctrl; + int err, fd; + + ctrl.num = ctrl_no; + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); + if (err) { + error(MSG_STATUSACQCTRL, ctrl_no); + close(fd); + return (err); + } + *running = ctrl.running; + close(fd); + return (0); +} + +static int +is_ctrl_created(int ctrl_no, int *created) +{ + struct sim_ctrl ctrl; + int err, fd; + + ctrl.num = ctrl_no; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CTRL, &ctrl); + if (err) { + error("Could not acquire conf for ctrl#%d", ctrl_no); + close(fd); + return (err); + } + *created = ctrl.created; + close(fd); + return (0); +} + +static int +is_chip_created(int ctrl_no, int chip_no, int *created) +{ + struct sim_chip chip; + int err, fd; + + chip.ctrl_num = ctrl_no; + chip.num = chip_no; + + if (opendev(&fd) != EX_OK) + return (EX_OSFILE); + + err = ioctl(fd, NANDSIM_STATUS_CHIP, &chip); + if (err) { + error("Could not acquire conf for chip#%d", chip_no); + close(fd); + return (err); + } + *created = chip.created; + close(fd); + return (0); +} + +static int +assert_chip_connected(int ctrl_no, int chip_no) +{ + int created, running; + + if (is_ctrl_created(ctrl_no, &created)) + return (0); + + if (!created) { + error(MSG_NOCTRL, ctrl_no); + return (0); + } + + if (is_chip_created(ctrl_no, chip_no, &created)) + return (0); + + if (!created) { + error(MSG_NOTCONFIGDCTRLCHIP, ctrl_no, chip_no); + return (0); + } + + if (is_ctrl_running(ctrl_no, &running)) + return (0); + + if (!running) { + error(MSG_NOTRUNNING, ctrl_no); + return (0); + } + + return (1); +} + +static int +printstats(int ctrlno, int chipno, uint32_t pageno, int cdevd) +{ + struct page_stat_io pstats; + struct block_stat_io bstats; + struct chip_param_io cparams; + uint32_t blkidx; + int err; + + /* Gather information about chip */ + err = ioctl(cdevd, NAND_IO_GET_CHIP_PARAM, &cparams); + + if (err) { + error("Could not acquire chip info for chip attached to cs#" + "%d, ctrl#%d", chipno, ctrlno); + return (EX_SOFTWARE); + } + + blkidx = (pageno / cparams.pages_per_block); + bstats.block_num = blkidx; + + err = ioctl(cdevd, NAND_IO_BLOCK_STAT, &bstats); + if (err) { + error("Could not acquire block#%d statistics!", blkidx); + return (ENXIO); + } + + printf("Block #%d erased: %d\n", blkidx, bstats.block_erased); + pstats.page_num = pageno; + + err = ioctl(cdevd, NAND_IO_PAGE_STAT, &pstats); + if (err) { + error("Could not acquire page statistics!"); + return (ENXIO); + } + + debug("BLOCKIDX = %d PAGENO (REL. TO BLK) = %d\n", blkidx, + pstats.page_num); + + printf("Page#%d : reads:%d writes:%d \n\traw reads:%d raw writes:%d " + "\n\tecc_succeeded:%d ecc_corrected:%d ecc_failed:%d\n", + pstats.page_num, pstats.page_read, pstats.page_written, + pstats.page_raw_read, pstats.page_raw_written, + pstats.ecc_succeded, pstats.ecc_corrected, pstats.ecc_failed); + return (0); +} |