diff options
author | gber <gber@FreeBSD.org> | 2012-05-17 10:11:18 +0000 |
---|---|---|
committer | gber <gber@FreeBSD.org> | 2012-05-17 10:11:18 +0000 |
commit | 6f7c7353004e2ff9709b326a4008ce8ea63d9270 (patch) | |
tree | a325137a898341311de8641f7212e28b7d87950e /usr.sbin | |
parent | 661b9d94414ea6d11d5b7960aef1f172975ce52b (diff) | |
download | FreeBSD-src-6f7c7353004e2ff9709b326a4008ce8ea63d9270.zip FreeBSD-src-6f7c7353004e2ff9709b326a4008ce8ea63d9270.tar.gz |
Import work done under project/nand (@235533) into head.
The NAND Flash environment consists of several distinct components:
- NAND framework (drivers harness for NAND controllers and NAND chips)
- NAND simulator (NANDsim)
- NAND file system (NAND FS)
- Companion tools and utilities
- Documentation (manual pages)
This work is still experimental. Please use with caution.
Obtained from: Semihalf
Supported by: FreeBSD Foundation, Juniper Networks
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/Makefile | 5 | ||||
-rw-r--r-- | usr.sbin/nandsim/Makefile | 8 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim.8 | 230 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim.c | 1397 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim_cfgparse.c | 957 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim_cfgparse.h | 86 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim_rcfile.c | 440 | ||||
-rw-r--r-- | usr.sbin/nandsim/nandsim_rcfile.h | 70 | ||||
-rw-r--r-- | usr.sbin/nandsim/sample.conf | 174 | ||||
-rw-r--r-- | usr.sbin/nandtool/Makefile | 11 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_erase.c | 114 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_info.c | 86 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_read.c | 139 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_readoob.c | 111 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_write.c | 143 | ||||
-rw-r--r-- | usr.sbin/nandtool/nand_writeoob.c | 113 | ||||
-rw-r--r-- | usr.sbin/nandtool/nandtool.8 | 188 | ||||
-rw-r--r-- | usr.sbin/nandtool/nandtool.c | 283 | ||||
-rw-r--r-- | usr.sbin/nandtool/nandtool.h | 57 | ||||
-rw-r--r-- | usr.sbin/nandtool/usage.h | 112 |
20 files changed, 4724 insertions, 0 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 68b82e7..ded1725 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -223,6 +223,11 @@ SUBDIR+= lpr SUBDIR+= manctl .endif +.if ${MK_NAND} != "no" +SUBDIR+= nandsim +SUBDIR+= nandtool +.endif + .if ${MK_NETGRAPH} != "no" SUBDIR+= flowctl SUBDIR+= lmcconfig diff --git a/usr.sbin/nandsim/Makefile b/usr.sbin/nandsim/Makefile new file mode 100644 index 0000000..9269ab5 --- /dev/null +++ b/usr.sbin/nandsim/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= nandsim +SRCS= nandsim.c nandsim_rcfile.c nandsim_cfgparse.c +BINDIR= /usr/sbin +MAN= nandsim.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/nandsim/nandsim.8 b/usr.sbin/nandsim/nandsim.8 new file mode 100644 index 0000000..5e86299 --- /dev/null +++ b/usr.sbin/nandsim/nandsim.8 @@ -0,0 +1,230 @@ +.\" Copyright (c) 2010 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 THE 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. +.\" +.\" $FreeBSD$ +.\" +.Dd August 10, 2010 +.Dt NANDSIM 8 +.Os +.Sh NAME +.Nm nandsim +.Nd NAND simulator control program +.Sh SYNOPSIS +.Nm +.Ic status +.Aq ctrl_no | Fl -all | Fl a +.Op Fl v +.Nm +.Ic conf +.Aq filename +.Nm +.Ic start +.Aq ctrl_no +.Nm +.Ic mod +.Aq ctrl_no:cs_no | Fl l Aq loglevel +.Op Fl p Aq prog_time +.Op Fl e Aq erase_time +.Op Fl r Aq read_time +.Op Fl E Aq error_ratio +.Op Fl h +.Nm +.Ic stop +.Aq ctrl_no +.Nm +.Ic error +.Aq ctrl_no:cs_no +.Aq page_num +.Aq column +.Aq length +.Aq pattern +.Nm +.Ic bb +.Aq ctrl_no:cs_no +.Op blk_num,blk_num2,... +.Op Fl U +.Op Fl L +.Nm +.Ic freeze +.Op ctrl_no +.Nm +.Ic log +.Aq ctrl_no | Fl -all | Fl a +.Nm +.Ic stats +.Aq ctrl_no:cs_no +.Aq page_num +.Nm +.Ic dump +.Aq ctrl_no:cs_no +.Aq filename +.Nm +.Ic restore +.Aq ctrl_no:chip_no +.Aq filename +.Nm +.Ic destroy +.Aq ctrl_no[:cs_no] | Fl -all | Fl a +.Nm +.Ic help +.Op Fl v +.Sh COMMAND DESCRIPTION +Controllers and chips are arranged into a simple hierarchy. +There can be up to 4 controllers configured, each with 4 chip select (CS) lines. +A given chip is connected to one of the chip selects. +.Pp +Controllers are specified as +.Aq ctrl_no ; +chip selects are specified as +.Aq cs_no . +.Bl -tag -width periphlist +.It Ic status +Gets controller(s) status. If +.Fl a +or +.Fl -all +flag is specified - command will print status of every controller +currently available. +Optional flag +.Fl v +causes printing complete information about the controller, and all +chips attached to it. +.It Ic conf +Reads simulator configuration from a specified file (this includes +the simulation "layout" i.e. controllers-chips assignments). +Configuration changes for an already started simulation require a +full stop-start cycle in order to take effect i.e.: +.Pp +.Bl -column +.It nandsim stop ... +.It nandsim destroy ... +.Pp +.It << edit config file >> +.Pp +.It nandsim conf ... +.It nandsim start ... +.El +.It Ic mod +Alters simulator parameters on-the-fly. +If controller number and CS pair is not specified, the general +simulator parameters (not specific to a controller or a chip) will be modified. +Changing chip's parameters requires specifying both controller number and CS +to which the given chip is connected. +Parameters which can be altered: +.Pp +General simulator related: +.Bl -tag -width flag +.It Fl l Aq log_level +change logging level to +.Aq log_level +.El +.Pp +Chip related: +.Bl -tag -width flag +.It Fl p Aq prog_time +change prog time for specified chip to +.Aq prog_time +.It Fl e Aq erase_time +change erase time for specified chip to +.Aq erase_time +.It Fl r Aq read_time +change read time for specified chip to +.Aq read_time +.It Fl E Aq error_ratio +change error ratio for specified chip to +.Aq error_ratio . +Error ratio is a number of errors per million read/write bytes. +.El +.Pp +Additionally, flag +.Fl h +will list parameters which can be altered. +.El +.Bl -tag -width periphlist +.It Ic bb +Marks/unmarks a specified block as bad. +To mark/unmark the bad condition an a block, the following parameters +have to be supplied: controller number, CS number, and at least one +block number. +It is possible to specify multiple blocks, by separating blocks numbers +with a comma. +The following options can be used for the 'bb' command: +.Bl -tag -width flag +.It Fl U +unmark the bad previously marked block as bad. +.It Fl L +list all blocks marked as bad on a given chip. +.El +.It Ic log +Prints activity log of the specified controller to stdout; if +controller number is not specifed, logs for all available +controllers are printed. +.It Ic stats +Print statistics of the selected controller, chip and page. +Statistics includes read count, write count, raw read count, raw +write count, ECC stats (succeeded corrections, failed correction). +.It Ic dump +Dumps a snaphot of a single chip (including data and bad blocks +information, wearout level) into the file. +.It Ic restore +Restores chip state from a dump-file snapshot (produced previously +with the 'dump' command). +.It Ic start +Starts a controller i.e. the simulation. +.It Ic stop +Stops an already started controller; if the controller number is not +supplied, attempts to stop all currently working controllers. +.It Ic destroy +Removes existing active chip/controller and its configuration from +memory and releases the resources. +Specifying flag +.Fl a +or +.Fl -all +causes removal of every chip and controller. +Controller must be stopped in order to be destroyed. +.It Ic error +Directly overwrites a certain number of bytes in the specified page +at a given offset with a supplied pattern (which mimics the +corruption of flash contents). +.It Ic help +Prints synopsis, +.Fl v +gives more verbose output. +.It Ic freeze +Stops simulation of given controller (simulates power-loss). +All commands issues to any chip on this controller are ignored. +.El +.Sh SEE ALSO +.Xr nand 4 , +.Xr nandsim 4 +.Xr nandsim.conf 5 +.Sh HISTORY +The +.Nm +utility first appeared in +.Fx 10.0 . +.Sh AUTHOR +This utility was written by +.An Lukasz Wojcik . 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); +} diff --git a/usr.sbin/nandsim/nandsim_cfgparse.c b/usr.sbin/nandsim/nandsim_cfgparse.c new file mode 100644 index 0000000..40e8d4c --- /dev/null +++ b/usr.sbin/nandsim/nandsim_cfgparse.c @@ -0,0 +1,957 @@ +/*- + * 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. + */ +__FBSDID("$FreeBSD$"); + +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> + +#include <dev/nand/nandsim.h> + +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> + +#include "nandsim_cfgparse.h" + +#define warn(fmt, args...) do { \ + printf("WARNING: " fmt "\n", ##args); } while (0) + +#define error(fmt, args...) do { \ + printf("ERROR: " fmt "\n", ##args); } while (0) + +#define MSG_MANDATORYKEYMISSING "mandatory key \"%s\" value belonging to " \ + "section \"%s\" is missing!\n" + +#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 STRBUFSIZ 2000 + +/* Macros extracts type and type size */ +#define TYPE(x) ((x) & 0xf8) +#define SIZE(x) (((x) & 0x07)) + +/* Erase/Prog/Read time max and min values */ +#define DELAYTIME_MIN 10000 +#define DELAYTIME_MAX 10000000 + +/* Structure holding configuration for controller. */ +static struct sim_ctrl ctrl_conf; +/* Structure holding configuration for chip. */ +static struct sim_chip chip_conf; + +static struct nandsim_key nandsim_ctrl_keys[] = { + {"num_cs", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num_cs, 0}, + {"ctrl_num", 1, VALUE_UINT | SIZE_8, (void *)&ctrl_conf.num, 0}, + + {"ecc_layout", 1, VALUE_UINTARRAY | SIZE_16, + (void *)&ctrl_conf.ecc_layout, MAX_ECC_BYTES}, + + {"filename", 0, VALUE_STRING, + (void *)&ctrl_conf.filename, FILENAME_SIZE}, + + {"ecc", 0, VALUE_BOOL, (void *)&ctrl_conf.ecc, 0}, + {NULL, 0, 0, NULL, 0}, +}; + +static struct nandsim_key nandsim_chip_keys[] = { + {"chip_cs", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.num, 0}, + {"chip_ctrl", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.ctrl_num, + 0}, + {"device_id", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.device_id, + 0}, + {"manufacturer_id", 1, VALUE_UINT | SIZE_8, + (void *)&chip_conf.manufact_id, 0}, + {"model", 0, VALUE_STRING, (void *)&chip_conf.device_model, + DEV_MODEL_STR_SIZE}, + {"manufacturer", 0, VALUE_STRING, (void *)&chip_conf.manufacturer, + MAN_STR_SIZE}, + {"page_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.page_size, + 0}, + {"oob_size", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.oob_size, + 0}, + {"pages_per_block", 1, VALUE_UINT | SIZE_32, + (void *)&chip_conf.pgs_per_blk, 0}, + {"blocks_per_lun", 1, VALUE_UINT | SIZE_32, + (void *)&chip_conf.blks_per_lun, 0}, + {"luns", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.luns, 0}, + {"column_addr_cycle", 1,VALUE_UINT | SIZE_8, + (void *)&chip_conf.col_addr_cycles, 0}, + {"row_addr_cycle", 1, VALUE_UINT | SIZE_8, + (void *)&chip_conf.row_addr_cycles, 0}, + {"program_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.prog_time, 0}, + {"erase_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.erase_time, 0}, + {"read_time", 0, VALUE_UINT | SIZE_32, + (void *)&chip_conf.read_time, 0}, + {"width", 1, VALUE_UINT | SIZE_8, (void *)&chip_conf.width, 0}, + {"wear_out", 1, VALUE_UINT | SIZE_32, (void *)&chip_conf.wear_level, + 0}, + {"bad_block_map", 0, VALUE_UINTARRAY | SIZE_32, + (void *)&chip_conf.bad_block_map, MAX_BAD_BLOCKS}, + {NULL, 0, 0, NULL, 0}, +}; + +struct nandsim_section sections[] = { + {"ctrl", (struct nandsim_key *)&nandsim_ctrl_keys}, + {"chip", (struct nandsim_key *)&nandsim_chip_keys}, + {NULL, NULL}, +}; + +static uint8_t logoutputtoint(char *, int *); +static uint8_t validate_chips(struct sim_chip *, int, struct sim_ctrl *, int); +static uint8_t validate_ctrls(struct sim_ctrl *, int); +static int configure_sim(const char *, struct rcfile *); +static int create_ctrls(struct rcfile *, struct sim_ctrl **, int *); +static int create_chips(struct rcfile *, struct sim_chip **, int *); +static void destroy_ctrls(struct sim_ctrl *); +static void destroy_chips(struct sim_chip *); +static int validate_section_config(struct rcfile *, const char *, int); + +int +convert_argint(char *arg, int *value) +{ + + if (arg == NULL || value == NULL) + return (EINVAL); + + errno = 0; + *value = (int)strtol(arg, NULL, 0); + if (*value == 0 && errno != 0) { + error("Cannot convert to number argument \'%s\'", arg); + return (EINVAL); + } + return (0); +} + +int +convert_arguint(char *arg, unsigned int *value) +{ + + if (arg == NULL || value == NULL) + return (EINVAL); + + errno = 0; + *value = (unsigned int)strtol(arg, NULL, 0); + if (*value == 0 && errno != 0) { + error("Cannot convert to number argument \'%s\'", arg); + return (EINVAL); + } + return (0); +} + +/* Parse given ',' separated list of bytes into buffer. */ +int +parse_intarray(char *array, int **buffer) +{ + char *tmp, *tmpstr, *origstr; + unsigned int currbufp = 0, i; + unsigned int count = 0, from = 0, to = 0; + + /* Remove square braces */ + if (array[0] == '[') + array ++; + if (array[strlen(array)-1] == ']') + array[strlen(array)-1] = ','; + + from = strlen(array); + origstr = (char *)malloc(sizeof(char) * from); + strcpy(origstr, array); + + tmpstr = (char *)strtok(array, ","); + /* First loop checks for how big int array we need to allocate */ + while (tmpstr != NULL) { + errno = 0; + if ((tmp = strchr(tmpstr, '-')) != NULL) { + *tmp = ' '; + if (convert_arguint(tmpstr, &from) || + convert_arguint(tmp, &to)) { + free(origstr); + return (EINVAL); + } + + count += to - from + 1; + } else { + if (convert_arguint(tmpstr, &from)) { + free(origstr); + return (EINVAL); + } + count++; + } + tmpstr = (char *)strtok(NULL, ","); + } + + if (count == 0) + goto out; + + /* Allocate buffer of ints */ + tmpstr = (char *)strtok(origstr, ","); + *buffer = malloc(count * sizeof(int)); + + /* Second loop is just inserting converted values into int array */ + while (tmpstr != NULL) { + errno = 0; + if ((tmp = strchr(tmpstr, '-')) != NULL) { + *tmp = ' '; + from = strtol(tmpstr, NULL, 0); + to = strtol(tmp, NULL, 0); + tmpstr = strtok(NULL, ","); + for (i = from; i <= to; i ++) + (*buffer)[currbufp++] = i; + continue; + } + errno = 0; + from = (int)strtol(tmpstr, NULL, 0); + (*buffer)[currbufp++] = from; + tmpstr = (char *)strtok(NULL, ","); + } +out: + free(origstr); + return (count); +} + +/* Convert logoutput strings literals into appropriate ints. */ +static uint8_t +logoutputtoint(char *logoutput, int *output) +{ + int out; + + if (strcmp(logoutput, "file") == 0) + out = NANDSIM_OUTPUT_FILE; + + else if (strcmp(logoutput, "console") == 0) + out = NANDSIM_OUTPUT_CONSOLE; + + else if (strcmp(logoutput, "ram") == 0) + out = NANDSIM_OUTPUT_RAM; + + else if (strcmp(logoutput, "none") == 0) + out = NANDSIM_OUTPUT_NONE; + else + out = -1; + + *output = out; + + if (out == -1) + return (EINVAL); + else + return (0); +} + +static int +configure_sim(const char *devfname, struct rcfile *f) +{ + struct sim_param sim_conf; + char buf[255]; + int err, tmpv, fd; + + err = rc_getint(f, "sim", 0, "log_level", &tmpv); + + if (tmpv < 0 || tmpv > 255 || err) { + error("Bad log level specified (%d)\n", tmpv); + return (ENOTSUP); + } else + sim_conf.log_level = tmpv; + + rc_getstring(f, "sim", 0, "log_output", 255, (char *)&buf); + + tmpv = -1; + err = logoutputtoint((char *)&buf, &tmpv); + if (err) { + error("Log output specified in config file does not seem to " + "be valid (%s)!", (char *)&buf); + return (ENOTSUP); + } + + sim_conf.log_output = tmpv; + + fd = open(devfname, O_RDWR); + if (fd == -1) { + error("could not open simulator device file (%s)!", + devfname); + return (EX_OSFILE); + } + + err = ioctl(fd, NANDSIM_SIM_PARAM, &sim_conf); + if (err) { + error("simulator parameters could not be modified: %s", + strerror(errno)); + close(fd); + return (ENXIO); + } + + close(fd); + return (EX_OK); +} + +static int +create_ctrls(struct rcfile *f, struct sim_ctrl **ctrls, int *cnt) +{ + int count, i; + struct sim_ctrl *ctrlsptr; + + count = rc_getsectionscount(f, "ctrl"); + if (count > MAX_SIM_DEV) { + error("Too many CTRL sections specified(%d)", count); + return (ENOTSUP); + } else if (count == 0) { + error("No ctrl sections specified"); + return (ENOENT); + } + + ctrlsptr = (struct sim_ctrl *)malloc(sizeof(struct sim_ctrl) * count); + if (ctrlsptr == NULL) { + error("Could not allocate memory for ctrl configuration"); + return (ENOMEM); + } + + for (i = 0; i < count; i++) { + bzero((void *)&ctrl_conf, sizeof(ctrl_conf)); + + /* + * ECC layout have to end up with 0xffff, so + * we're filling buffer with 0xff. If ecc_layout is + * defined in config file, values will be overriden. + */ + memset((void *)&ctrl_conf.ecc_layout, 0xff, + sizeof(ctrl_conf.ecc_layout)); + + if (validate_section_config(f, "ctrl", i) != 0) { + free(ctrlsptr); + return (EINVAL); + } + + if (parse_section(f, "ctrl", i) != 0) { + free(ctrlsptr); + return (EINVAL); + } + + memcpy(&ctrlsptr[i], &ctrl_conf, sizeof(ctrl_conf)); + /* Try to create ctrl with config parsed */ + debug("NUM=%d\nNUM_CS=%d\nECC=%d\nFILENAME=%s\nECC_LAYOUT[0]" + "=%d\nECC_LAYOUT[1]=%d\n\n", + ctrlsptr[i].num, ctrlsptr[i].num_cs, ctrlsptr[i].ecc, + ctrlsptr[i].filename, ctrlsptr[i].ecc_layout[0], + ctrlsptr[i].ecc_layout[1]); + } + *cnt = count; + *ctrls = ctrlsptr; + return (0); +} + +static void +destroy_ctrls(struct sim_ctrl *ctrls) +{ + + free(ctrls); +} + +static int +create_chips(struct rcfile *f, struct sim_chip **chips, int *cnt) +{ + struct sim_chip *chipsptr; + int count, i; + + count = rc_getsectionscount(f, "chip"); + if (count > (MAX_CTRL_CS * MAX_SIM_DEV)) { + error("Too many chip sections specified(%d)", count); + return (ENOTSUP); + } else if (count == 0) { + error("No chip sections specified"); + return (ENOENT); + } + + chipsptr = (struct sim_chip *)malloc(sizeof(struct sim_chip) * count); + if (chipsptr == NULL) { + error("Could not allocate memory for chip configuration"); + return (ENOMEM); + } + + for (i = 0; i < count; i++) { + bzero((void *)&chip_conf, sizeof(chip_conf)); + + /* + * Bad block map have to end up with 0xffff, so + * we're filling array with 0xff. If bad block map is + * defined in config file, values will be overriden. + */ + memset((void *)&chip_conf.bad_block_map, 0xff, + sizeof(chip_conf.bad_block_map)); + + if (validate_section_config(f, "chip", i) != 0) { + free(chipsptr); + return (EINVAL); + } + + if (parse_section(f, "chip", i) != 0) { + free(chipsptr); + return (EINVAL); + } + + memcpy(&chipsptr[i], &chip_conf, sizeof(chip_conf)); + + /* Try to create chip with config parsed */ + debug("CHIP:\nNUM=%d\nCTRL_NUM=%d\nDEVID=%d\nMANID=%d\n" + "PAGE_SZ=%d\nOOBSZ=%d\nREAD_T=%d\nDEVMODEL=%s\n" + "MAN=%s\nCOLADDRCYCLES=%d\nROWADDRCYCLES=%d\nCHWIDTH=%d\n" + "PGS/BLK=%d\nBLK/LUN=%d\nLUNS=%d\nERR_RATIO=%d\n" + "WEARLEVEL=%d\nISWP=%d\n\n\n\n", + chipsptr[i].num, chipsptr[i].ctrl_num, + chipsptr[i].device_id, chipsptr[i].manufact_id, + chipsptr[i].page_size, chipsptr[i].oob_size, + chipsptr[i].read_time, chipsptr[i].device_model, + chipsptr[i].manufacturer, chipsptr[i].col_addr_cycles, + chipsptr[i].row_addr_cycles, chipsptr[i].width, + chipsptr[i].pgs_per_blk, chipsptr[i].blks_per_lun, + chipsptr[i].luns, chipsptr[i].error_ratio, + chipsptr[i].wear_level, chipsptr[i].is_wp); + } + *cnt = count; + *chips = chipsptr; + return (0); +} + +static void +destroy_chips(struct sim_chip *chips) +{ + + free(chips); +} + +int +parse_config(char *cfgfname, const char *devfname) +{ + int err = 0, fd; + unsigned int chipsectionscnt, ctrlsectionscnt, i; + struct rcfile *f; + struct sim_chip *chips; + struct sim_ctrl *ctrls; + + err = rc_open(cfgfname, "r", &f); + if (err) { + error("could not open configuration file (%s)", cfgfname); + return (EX_NOINPUT); + } + + /* First, try to configure simulator itself. */ + if (configure_sim(devfname, f) != EX_OK) { + rc_close(f); + return (EINVAL); + } + + debug("SIM CONFIGURED!\n"); + /* Then create controllers' configs */ + if (create_ctrls(f, &ctrls, &ctrlsectionscnt) != 0) { + rc_close(f); + return (ENXIO); + } + debug("CTRLS CONFIG READ!\n"); + + /* Then create chips' configs */ + if (create_chips(f, &chips, &chipsectionscnt) != 0) { + destroy_ctrls(ctrls); + rc_close(f); + return (ENXIO); + } + debug("CHIPS CONFIG READ!\n"); + + if (validate_ctrls(ctrls, ctrlsectionscnt) != 0) { + destroy_ctrls(ctrls); + destroy_chips(chips); + rc_close(f); + return (EX_SOFTWARE); + } + if (validate_chips(chips, chipsectionscnt, ctrls, + ctrlsectionscnt) != 0) { + destroy_ctrls(ctrls); + destroy_chips(chips); + rc_close(f); + return (EX_SOFTWARE); + } + + /* Open device */ + fd = open(devfname, O_RDWR); + if (fd == -1) { + error("could not open simulator device file (%s)!", + devfname); + rc_close(f); + destroy_chips(chips); + destroy_ctrls(ctrls); + return (EX_OSFILE); + } + + debug("SIM CONFIG STARTED!\n"); + + /* At this stage, both ctrls' and chips' configs should be valid */ + for (i = 0; i < ctrlsectionscnt; i++) { + err = ioctl(fd, NANDSIM_CREATE_CTRL, &ctrls[i]); + if (err) { + if (err == EEXIST) + error("Controller#%d already created\n", + ctrls[i].num); + else if (err == EINVAL) + error("Incorrect controler number (%d)\n", + ctrls[i].num); + else + error("Could not created controller#%d\n", + ctrls[i].num); + /* Errors during controller creation stops parsing */ + close(fd); + rc_close(f); + destroy_ctrls(ctrls); + destroy_chips(chips); + return (ENXIO); + } + debug("CTRL#%d CONFIG STARTED!\n", i); + } + + for (i = 0; i < chipsectionscnt; i++) { + err = ioctl(fd, NANDSIM_CREATE_CHIP, &chips[i]); + if (err) { + if (err == EEXIST) + error("Chip#%d for controller#%d already " + "created\n", chips[i].num, + chips[i].ctrl_num); + else if (err == EINVAL) + error("Incorrect chip number (%d:%d)\n", + chips[i].num, chips[i].ctrl_num); + else + error("Could not create chip (%d:%d)\n", + chips[i].num, chips[i].ctrl_num); + error("Could not start chip#%d\n", i); + destroy_chips(chips); + destroy_ctrls(ctrls); + close(fd); + rc_close(f); + return (ENXIO); + } + } + debug("CHIPS CONFIG STARTED!\n"); + + close(fd); + rc_close(f); + destroy_chips(chips); + destroy_ctrls(ctrls); + return (0); +} + +/* + * Function tries to get appropriate value for given key, convert it to + * array of ints (of given size), and perform all the neccesary checks and + * conversions. + */ +static int +get_argument_intarray(const char *sect_name, int sectno, + struct nandsim_key *key, struct rcfile *f) +{ + char strbuf[STRBUFSIZ]; + int *intbuf; + int getres; + uint32_t cnt, i = 0; + + getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, + (char *)&strbuf); + + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (EINVAL); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + cnt = parse_intarray((char *)&strbuf, &intbuf); + cnt = (cnt <= key->maxlength) ? cnt : key->maxlength; + + for (i = 0; i < cnt; i++) { + if (SIZE(key->valuetype) == SIZE_8) + *((uint8_t *)(key->field) + i) = + (uint8_t)intbuf[i]; + else if (SIZE(key->valuetype) == SIZE_16) + *((uint16_t *)(key->field) + i) = + (uint16_t)intbuf[i]; + else + *((uint32_t *)(key->field) + i) = + (uint32_t)intbuf[i]; + } + free(intbuf); + return (0); +} + +/* + * Function tries to get appropriate value for given key, convert it to + * int of certain length. + */ +static int +get_argument_int(const char *sect_name, int sectno, struct nandsim_key *key, + struct rcfile *f) +{ + int getres; + uint32_t val; + + getres = rc_getint(f, sect_name, sectno, key->keyname, &val); + if (getres != 0) { + + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + + return (EINVAL); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + if (SIZE(key->valuetype) == SIZE_8) + *(uint8_t *)(key->field) = (uint8_t)val; + else if (SIZE(key->valuetype) == SIZE_16) + *(uint16_t *)(key->field) = (uint16_t)val; + else + *(uint32_t *)(key->field) = (uint32_t)val; + return (0); +} + +/* Function tries to get string value for given key */ +static int +get_argument_string(const char *sect_name, int sectno, + struct nandsim_key *key, struct rcfile *f) +{ + char strbuf[STRBUFSIZ]; + int getres; + + getres = rc_getstring(f, sect_name, sectno, key->keyname, STRBUFSIZ, + strbuf); + + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (1); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + strncpy(key->field, (char *)&strbuf, (size_t)(key->maxlength - 1)); + return (0); +} + +/* Function tries to get on/off value for given key */ +static int +get_argument_bool(const char *sect_name, int sectno, struct nandsim_key *key, + struct rcfile *f) +{ + int getres, val; + + getres = rc_getbool(f, sect_name, sectno, key->keyname, &val); + if (getres != 0) { + if (key->mandatory != 0) { + error(MSG_MANDATORYKEYMISSING, key->keyname, + sect_name); + return (1); + } else + /* Non-mandatory key, not present -- skip */ + return (0); + } + *(uint8_t *)key->field = (uint8_t)val; + return (0); +} + +int +parse_section(struct rcfile *f, const char *sect_name, int sectno) +{ + struct nandsim_key *key; + struct nandsim_section *sect = (struct nandsim_section *)§ions; + int getres = 0; + + while (1) { + if (sect == NULL) + return (EINVAL); + + if (strcmp(sect->name, sect_name) == 0) + break; + else + sect++; + } + key = sect->keys; + do { + debug("->Section: %s, Key: %s, type: %d, size: %d", + sect_name, key->keyname, TYPE(key->valuetype), + SIZE(key->valuetype)/2); + + switch (TYPE(key->valuetype)) { + case VALUE_UINT: + /* Single int value */ + getres = get_argument_int(sect_name, sectno, key, f); + + if (getres != 0) + return (getres); + + break; + case VALUE_UINTARRAY: + /* Array of ints */ + getres = get_argument_intarray(sect_name, + sectno, key, f); + + if (getres != 0) + return (getres); + + break; + case VALUE_STRING: + /* Array of chars */ + getres = get_argument_string(sect_name, sectno, key, + f); + + if (getres != 0) + return (getres); + + break; + case VALUE_BOOL: + /* Boolean value (true/false/on/off/yes/no) */ + getres = get_argument_bool(sect_name, sectno, key, + f); + + if (getres != 0) + return (getres); + + break; + } + } while ((++key)->keyname != NULL); + + return (0); +} + +static uint8_t +validate_chips(struct sim_chip *chips, int chipcnt, + struct sim_ctrl *ctrls, int ctrlcnt) +{ + int cchipcnt, i, width, j, id, max; + + cchipcnt = chipcnt; + for (chipcnt -= 1; chipcnt >= 0; chipcnt--) { + if (chips[chipcnt].num >= MAX_CTRL_CS) { + error("chip no. too high (%d)!!\n", + chips[chipcnt].num); + return (EINVAL); + } + + if (chips[chipcnt].ctrl_num >= MAX_SIM_DEV) { + error("controller no. too high (%d)!!\n", + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if (chips[chipcnt].width != 8 && + chips[chipcnt].width != 16) { + error("invalid width:%d for chip#%d", + chips[chipcnt].width, chips[chipcnt].num); + return (EINVAL); + } + + /* Check if page size is > 512 and if its power of 2 */ + if (chips[chipcnt].page_size < 512 || + (chips[chipcnt].page_size & + (chips[chipcnt].page_size - 1)) != 0) { + error("invalid page size:%d for chip#%d at ctrl#%d!!" + "\n", chips[chipcnt].page_size, + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + /* Check if controller no. ctrl_num is configured */ + for (i = 0, id = -1; i < ctrlcnt && id == -1; i++) + if (ctrls[i].num == chips[chipcnt].ctrl_num) + id = i; + + if (i == ctrlcnt && id == -1) { + error("Missing configuration for controller %d" + " (at least one chip is connected to it)", + chips[chipcnt].ctrl_num); + return (EINVAL); + } else { + /* + * Controller is configured -> check oob_size + * validity + */ + i = 0; + max = ctrls[id].ecc_layout[0]; + while (i < MAX_ECC_BYTES && + ctrls[id].ecc_layout[i] != 0xffff) { + + if (ctrls[id].ecc_layout[i] > max) + max = ctrls[id].ecc_layout[i]; + i++; + } + + if (chips[chipcnt].oob_size < (unsigned)i) { + error("OOB size for chip#%d at ctrl#%d is " + "smaller than ecc layout length!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + exit(EINVAL); + } + + if (chips[chipcnt].oob_size < (unsigned)max) { + error("OOB size for chip#%d at ctrl#%d is " + "smaller than maximal ecc position in " + "defined layout!", chips[chipcnt].num, + chips[chipcnt].ctrl_num); + exit(EINVAL); + } + + + } + + if ((chips[chipcnt].erase_time < DELAYTIME_MIN || + chips[chipcnt].erase_time > DELAYTIME_MAX) && + chips[chipcnt].erase_time != 0) { + error("Invalid erase time value for chip#%d at " + "ctrl#%d", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if ((chips[chipcnt].prog_time < DELAYTIME_MIN || + chips[chipcnt].prog_time > DELAYTIME_MAX) && + chips[chipcnt].prog_time != 0) { + error("Invalid prog time value for chip#%d at " + "ctr#%d!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + + if ((chips[chipcnt].read_time < DELAYTIME_MIN || + chips[chipcnt].read_time > DELAYTIME_MAX) && + chips[chipcnt].read_time != 0) { + error("Invalid read time value for chip#%d at " + "ctrl#%d!", + chips[chipcnt].num, + chips[chipcnt].ctrl_num); + return (EINVAL); + } + } + /* Check if chips attached to the same controller, have same width */ + for (i = 0; i < ctrlcnt; i++) { + width = -1; + for (j = 0; j < cchipcnt; j++) { + if (chips[j].ctrl_num == i) { + if (width == -1) { + width = chips[j].width; + } else { + if (width != chips[j].width) { + error("Chips attached to " + "ctrl#%d have different " + "widths!\n", i); + return (EINVAL); + } + } + } + } + } + + return (0); +} + +static uint8_t +validate_ctrls(struct sim_ctrl *ctrl, int ctrlcnt) +{ + for (ctrlcnt -= 1; ctrlcnt >= 0; ctrlcnt--) { + if (ctrl[ctrlcnt].num > MAX_SIM_DEV) { + error("Controller no. too high (%d)!!\n", + ctrl[ctrlcnt].num); + return (EINVAL); + } + if (ctrl[ctrlcnt].num_cs > MAX_CTRL_CS) { + error("Too many CS (%d)!!\n", ctrl[ctrlcnt].num_cs); + return (EINVAL); + } + if (ctrl[ctrlcnt].ecc != 0 && ctrl[ctrlcnt].ecc != 1) { + error("ECC is set to neither 0 nor 1 !\n"); + return (EINVAL); + } + } + + return (0); +} + +static int validate_section_config(struct rcfile *f, const char *sect_name, + int sectno) +{ + struct nandsim_key *key; + struct nandsim_section *sect; + char **keys_tbl; + int i, match; + + for (match = 0, sect = (struct nandsim_section *)§ions; + sect != NULL; sect++) { + if (strcmp(sect->name, sect_name) == 0) { + match = 1; + break; + } + } + + if (match == 0) + return (EINVAL); + + keys_tbl = rc_getkeys(f, sect_name, sectno); + if (keys_tbl == NULL) + return (ENOMEM); + + for (i = 0; keys_tbl[i] != NULL; i++) { + key = sect->keys; + match = 0; + do { + if (strcmp(keys_tbl[i], key->keyname) == 0) { + match = 1; + break; + } + } while ((++key)->keyname != NULL); + + if (match == 0) { + error("Invalid key in config file: %s\n", keys_tbl[i]); + free(keys_tbl); + return (EINVAL); + } + } + + free(keys_tbl); + return (0); +} diff --git a/usr.sbin/nandsim/nandsim_cfgparse.h b/usr.sbin/nandsim/nandsim_cfgparse.h new file mode 100644 index 0000000..b9c642a --- /dev/null +++ b/usr.sbin/nandsim/nandsim_cfgparse.h @@ -0,0 +1,86 @@ +/*- + * 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. + * + * $FreeBSD$ + */ + +#ifndef _NANDSIM_CONFPARSER_H_ +#define _NANDSIM_CONFPARSER_H_ + +#define VALUE_UINT 0x08 +#define VALUE_INT 0x10 +#define VALUE_UINTARRAY 0x18 +#define VALUE_INTARRAY 0x20 +#define VALUE_STRING 0x28 +#define VALUE_CHAR 0x40 +#define VALUE_BOOL 0x48 + +#define SIZE_8 0x01 +#define SIZE_16 0x02 +#define SIZE_32 0x04 + +#include "nandsim_rcfile.h" + +/* + * keyname = name of a key, + * mandatory = is key mandatory in section belonging to, 0=false 1=true + * valuetype = what kind of value is assigned to that key, e.g. + * VALUE_UINT | SIZE_8 -- unsigned uint size 8 bits; + * VALUE_UINTARRAY | SIZE_8 -- array of uints 8-bit long; + * VALUE_BOOL -- 'on', 'off','true','false','yes' or 'no' + * literals; + * VALUE_STRING -- strings + * field = ptr to the field that should hold value for parsed value + * maxlength = contains maximum length of an array (used only with either + * VALUE_STRING or VALUE_(U)INTARRAY value types. + */ +struct nandsim_key { + const char *keyname; + uint8_t mandatory; + uint8_t valuetype; + void *field; + uint32_t maxlength; +}; +struct nandsim_section { + const char *name; + struct nandsim_key *keys; +}; + +struct nandsim_config { + struct sim_param **simparams; + struct sim_chip **simchips; + struct sim_ctrl **simctrls; + int chipcnt; + int ctrlcnt; +}; + +int parse_intarray(char *, int **); +int parse_config(char *, const char *); +int parse_section(struct rcfile *, const char *, int); +int compare_configs(struct nandsim_config *, struct nandsim_config *); +int convert_argint(char *, int *); +int convert_arguint(char *, unsigned int *); + +#endif /* _NANDSIM_CONFPARSER_H_ */ diff --git a/usr.sbin/nandsim/nandsim_rcfile.c b/usr.sbin/nandsim/nandsim_rcfile.c new file mode 100644 index 0000000..0f99e7b --- /dev/null +++ b/usr.sbin/nandsim/nandsim_rcfile.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 THE 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. + * + * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); +#include <sys/types.h> +#include <sys/queue.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <pwd.h> +#include <unistd.h> + +#include "nandsim_rcfile.h" + +SLIST_HEAD(rcfile_head, rcfile); +static struct rcfile_head pf_head = {NULL}; +static struct rcsection *rc_findsect(struct rcfile *rcp, + const char *sectname, int sect_id); +static struct rcsection *rc_addsect(struct rcfile *rcp, + const char *sectname); +static int rc_sect_free(struct rcsection *rsp); +static struct rckey *rc_sect_findkey(struct rcsection *rsp, + const char *keyname); +static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name, + char *value); +static void rc_key_free(struct rckey *p); +static void rc_parse(struct rcfile *rcp); + +static struct rcfile* rc_find(const char *filename); + +/* + * open rcfile and load its content, if already open - return previous handle + */ +int +rc_open(const char *filename, const char *mode,struct rcfile **rcfile) +{ + struct rcfile *rcp; + FILE *f; + rcp = rc_find(filename); + if (rcp) { + *rcfile = rcp; + return (0); + } + f = fopen (filename, mode); + if (f == NULL) + return errno; + rcp = malloc(sizeof(struct rcfile)); + if (rcp == NULL) { + fclose(f); + return ENOMEM; + } + bzero(rcp, sizeof(struct rcfile)); + rcp->rf_name = strdup(filename); + rcp->rf_f = f; + SLIST_INSERT_HEAD(&pf_head, rcp, rf_next); + rc_parse(rcp); + *rcfile = rcp; + return (0); +} + +int +rc_close(struct rcfile *rcp) +{ + struct rcsection *p,*n; + + fclose(rcp->rf_f); + for (p = SLIST_FIRST(&rcp->rf_sect); p; ) { + n = p; + p = SLIST_NEXT(p,rs_next); + rc_sect_free(n); + } + free(rcp->rf_name); + SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next); + free(rcp); + return (0); +} + +static struct rcfile* +rc_find(const char *filename) +{ + struct rcfile *p; + + SLIST_FOREACH(p, &pf_head, rf_next) + if (strcmp (filename, p->rf_name) == 0) + return (p); + return (0); +} + +/* Find section with given name and id */ +static struct rcsection * +rc_findsect(struct rcfile *rcp, const char *sectname, int sect_id) +{ + struct rcsection *p; + + SLIST_FOREACH(p, &rcp->rf_sect, rs_next) + if (strcmp(p->rs_name, sectname) == 0 && p->rs_id == sect_id) + return (p); + return (NULL); +} + +static struct rcsection * +rc_addsect(struct rcfile *rcp, const char *sectname) +{ + struct rcsection *p; + int id = 0; + p = rc_findsect(rcp, sectname, 0); + if (p) { + /* + * If section with that name already exists -- add one more, + * same named, but with different id (higher by one) + */ + while (p != NULL) { + id = p->rs_id + 1; + p = rc_findsect(rcp, sectname, id); + } + } + p = malloc(sizeof(*p)); + if (!p) + return (NULL); + p->rs_name = strdup(sectname); + p->rs_id = id; + SLIST_INIT(&p->rs_keys); + SLIST_INSERT_HEAD(&rcp->rf_sect, p, rs_next); + return (p); +} + +static int +rc_sect_free(struct rcsection *rsp) +{ + struct rckey *p,*n; + + for (p = SLIST_FIRST(&rsp->rs_keys); p; ) { + n = p; + p = SLIST_NEXT(p,rk_next); + rc_key_free(n); + } + free(rsp->rs_name); + free(rsp); + return (0); +} + +static struct rckey * +rc_sect_findkey(struct rcsection *rsp, const char *keyname) +{ + struct rckey *p; + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + if (strcmp(p->rk_name, keyname)==0) + return (p); + return (NULL); +} + +static struct rckey * +rc_sect_addkey(struct rcsection *rsp, const char *name, char *value) +{ + struct rckey *p; + p = rc_sect_findkey(rsp, name); + if (p) { + free(p->rk_value); + } else { + p = malloc(sizeof(*p)); + if (!p) + return (NULL); + SLIST_INSERT_HEAD(&rsp->rs_keys, p, rk_next); + p->rk_name = strdup(name); + } + p->rk_value = value ? strdup(value) : strdup(""); + return (p); +} + +static void +rc_key_free(struct rckey *p) +{ + free(p->rk_value); + free(p->rk_name); + free(p); +} + +enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue}; + +static void +rc_parse(struct rcfile *rcp) +{ + FILE *f = rcp->rf_f; + int state = stNewLine, c; + struct rcsection *rsp = NULL; + struct rckey *rkp = NULL; + char buf[2048]; + char *next = buf, *last = &buf[sizeof(buf)-1]; + + while ((c = getc (f)) != EOF) { + if (c == '\r') + continue; + if (state == stNewLine) { + next = buf; + if (isspace(c)) + continue; /* skip leading junk */ + if (c == '[') { + state = stHeader; + rsp = NULL; + continue; + } + if (c == '#' || c == ';') { + state = stSkipToEOL; + } else { /* something meaningful */ + state = stGetKey; + } + } + if (state == stSkipToEOL || next == last) {/* ignore long lines */ + if (c == '\n') { + state = stNewLine; + next = buf; + } + continue; + } + if (state == stHeader) { + if (c == ']') { + *next = 0; + next = buf; + rsp = rc_addsect(rcp, buf); + state = stSkipToEOL; + } else + *next++ = c; + continue; + } + if (state == stGetKey) { + if (c == ' ' || c == '\t')/* side effect: 'key name='*/ + continue; /* become 'keyname=' */ + if (c == '\n') { /* silently ignore ... */ + state = stNewLine; + continue; + } + if (c != '=') { + *next++ = c; + continue; + } + *next = 0; + if (rsp == NULL) { + fprintf(stderr, "Key '%s' defined before " + "section\n", buf); + state = stSkipToEOL; + continue; + } + rkp = rc_sect_addkey(rsp, buf, NULL); + next = buf; + state = stGetValue; + continue; + } + /* only stGetValue left */ + if (state != stGetValue) { + fprintf(stderr, "Well, I can't parse file " + "'%s'\n",rcp->rf_name); + state = stSkipToEOL; + } + if (c != '\n') { + *next++ = c; + continue; + } + *next = 0; + rkp->rk_value = strdup(buf); + state = stNewLine; + rkp = NULL; + } /* while */ + if (c == EOF && state == stGetValue) { + *next = 0; + rkp->rk_value = strdup(buf); + } +} + +int +rc_getstringptr(struct rcfile *rcp, const char *section, int sect_id, + const char *key, char **dest) +{ + struct rcsection *rsp; + struct rckey *rkp; + + *dest = NULL; + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + *dest = rkp->rk_value; + return (0); +} + +int +rc_getstring(struct rcfile *rcp, const char *section, int sect_id, + const char *key, unsigned int maxlen, char *dest) +{ + char *value; + int error; + + error = rc_getstringptr(rcp, section, sect_id, key, &value); + if (error) + return (error); + if (strlen(value) >= maxlen) { + fprintf(stderr, "line too long for key '%s' in section '%s'," + "max = %d\n",key, section, maxlen); + return (EINVAL); + } + strcpy(dest,value); + return (0); +} + +int +rc_getint(struct rcfile *rcp, const char *section, int sect_id, + const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + errno = 0; + *value = strtol(rkp->rk_value,NULL,0); + if (errno) { + fprintf(stderr, "invalid int value '%s' for key '%s' in " + "section '%s'\n",rkp->rk_value,key,section); + return (errno); + } + return (0); +} + +/* + * 1,yes,true + * 0,no,false + */ +int +rc_getbool(struct rcfile *rcp, const char *section, int sect_id, + const char *key, int *value) +{ + struct rcsection *rsp; + struct rckey *rkp; + char *p; + + rsp = rc_findsect(rcp, section, sect_id); + if (!rsp) + return (ENOENT); + rkp = rc_sect_findkey(rsp,key); + if (!rkp) + return (ENOENT); + p = rkp->rk_value; + while (*p && isspace(*p)) p++; + if (*p == '0' || strcasecmp(p,"no") == 0 || + strcasecmp(p, "false") == 0 || + strcasecmp(p, "off") == 0) { + *value = 0; + return (0); + } + if (*p == '1' || strcasecmp(p,"yes") == 0 || + strcasecmp(p, "true") == 0 || + strcasecmp(p, "on") == 0) { + *value = 1; + return (0); + } + fprintf(stderr, "invalid boolean value '%s' for key '%s' in section " + "'%s' \n",p, key, section); + return (EINVAL); +} + +/* Count how many sections with given name exists in configuration. */ +int rc_getsectionscount(struct rcfile *f, const char *sectname) +{ + struct rcsection *p; + int count = 0; + + p = rc_findsect(f, sectname, 0); + if (p) { + while (p != NULL) { + count = p->rs_id + 1; + p = rc_findsect(f, sectname, count); + } + return (count); + } else + return (0); +} + +char ** +rc_getkeys(struct rcfile *rcp, const char *sectname, int sect_id) +{ + struct rcsection *rsp; + struct rckey *p; + char **names_tbl; + int i = 0, count = 0; + + rsp = rc_findsect(rcp, sectname, sect_id); + if (rsp == NULL) + return (NULL); + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + count++; + + names_tbl = malloc(sizeof(char *) * (count + 1)); + if (names_tbl == NULL) + return (NULL); + + SLIST_FOREACH(p, &rsp->rs_keys, rk_next) + names_tbl[i++] = p->rk_name; + + names_tbl[i] = NULL; + return (names_tbl); +} + diff --git a/usr.sbin/nandsim/nandsim_rcfile.h b/usr.sbin/nandsim/nandsim_rcfile.h new file mode 100644 index 0000000..f5c3ce9 --- /dev/null +++ b/usr.sbin/nandsim/nandsim_rcfile.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 1999, Boris Popov + * 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * 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 THE 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. + * + * $FreeBSD$ + * + * from: FreeBSD: src/lib/libncp/ncpl_rcfile.c,v 1.5 2007/01/09 23:27:39 imp Exp + */ + +#ifndef _SIMRC_H_ +#define _SIMRC_H_ + +#include <sys/queue.h> + +struct rckey { + SLIST_ENTRY(rckey) rk_next; + char *rk_name; /* key name */ + char *rk_value; /* key value */ +}; + +struct rcsection { + SLIST_ENTRY(rcsection) rs_next; + SLIST_HEAD(rckey_head,rckey) rs_keys; /* key list */ + char *rs_name; /* section name */ + int rs_id; /* allow few same named */ +}; + +struct rcfile { + SLIST_ENTRY(rcfile) rf_next; + SLIST_HEAD(rcsec_head, rcsection) rf_sect; /* sections list */ + char *rf_name; /* file name */ + FILE *rf_f; /* file desc */ +}; + +int rc_open(const char *, const char *,struct rcfile **); +int rc_close(struct rcfile *); +int rc_getstringptr(struct rcfile *, const char *, int, const char *, + char **); +int rc_getstring(struct rcfile *, const char *, int, const char *, + unsigned int, char *); +int rc_getint(struct rcfile *, const char *, int, const char *, int *); +int rc_getbool(struct rcfile *, const char *, int, const char *, int *); +int rc_getsectionscount(struct rcfile *, const char *); +char **rc_getkeys(struct rcfile *, const char *, int); + +#endif /* _SIMRC_H_ */ diff --git a/usr.sbin/nandsim/sample.conf b/usr.sbin/nandsim/sample.conf new file mode 100644 index 0000000..bc534e1 --- /dev/null +++ b/usr.sbin/nandsim/sample.conf @@ -0,0 +1,174 @@ +#- +# 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 THE 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. +# +# $FreeBSD$ + +# +# Sample NANDsim configuration file. +# + +############################################################################# +# +# [sim] General (common) simulator configuration section. +# +[sim] +# log_level=0..255 +log_level=11 + +# log_output=[none, console, ram, file] +# +# When log_output=file is specified, each [ctrl] section must have a +# corresponding 'log_filename' field provided, which specifies log file name +# to be used. +log_output=none + +############################################################################# +# +# [ctrl] Controller configuration section. +# +# There can be a number of controllers defined for simulation, each has a +# dedicated [ctrl] section. With a given controller there are associated +# subordinate NAND chips, which are tied to chip select lines. +# +[ctrl] +# The number of this controller. +# ctrl_num=0..3 +ctrl_num=0 + +# The number of chip selects available at this controller. +# num_cs=1..4 +num_cs=1 + +# ECC enable flag. +# ecc=[on|off] +ecc=on + +# ECC layout. This is the list of byte offsets within OOB area, which comprise +# the ECC contents set. +# +# ecc_layout=[byte1, byte2-byte3, ..byten] +ecc_layout=[0-53] + +# Absolute path to the log file for this controller. +#log_filename=/var/log/nandsim-ctl0.log + + +############################################################################# +# +# [chip] Chip configuration section. +# +# There can be a number of individual NAND chip devices defined for +# simulation, and each has a dedicated [chip] section. +# +# A particular chip needs to be associated with its parent NAND controller by +# specifying the following fields: controller number (chip_ctrl) and the chip +# select line it is connected to (chip_cs). The chip can be connected to only +# a single (and unique) controller:cs pair. +# +[chip] +# The number of parent controller. This has to fit one of the controller +# instance number (ctrl_num from [ctrl] section). +# chip_ctrl=0..3 +chip_ctrl=0 + +# Chip select line. +# chip_cs=0..3 +chip_cs=0 + +# ONFI device identifier. +# device_id=0x00..0xff +device_id=0xd3 + +# ONFI manufacturer identifier. +# manufacturer_id=0x00..0xff +manufacturer_id=0xec + +# Textual description of the chip. +# model="model_name" +model="k9xxg08uxM:1GiB 3,3V 8-bit" + +# Textual name of the chip manufacturer. +# manufacturer="manufacturer name" +manufacturer="SAMSUNG" + +# page_size=[must be power of 2 and >= 512] (in bytes) +page_size=2048 +# oob_size=[>0] +oob_size=64 +# pages_per_block=n*32 +pages_per_block=64 +# blocks_per_lun=[>0] +blocks_per_lun=4096 +# luns=1..N +luns=1 +# column_addr_cycle=[1,2] +column_addr_cycle=2 +# row_addr_cycle=[1,2,3] +row_addr_cycle=3 + +# program_time= (in us) +program_time=0 +# erase_time= (in us) +erase_time=0 +# read_time= (in us) +read_time=0 +# ccs_time= (in us) +#ccs_time=200 + +# Simulate write-protect on the chip. +# write_protect=[yes|no] +#write_protect=no + +# Blocks wear-out threshold. Each block has a counter of program-erase cycles; +# when this counter reaches 'wear_out' value a given block is treated as a bad +# block (access will report error). +# +# Setting wear_out to 0 means that blocks will never wear out. +# +# wear_out=0..100000 +wear_out=50000 + +# Errors per million read/write bytes. This simulates an accidental read/write +# block error, which can happen in real devices with certain probability. Note +# this isn't a bad block condition i.e. the block at which the read/write +# operation is simulated to fail here remains usable, only the operation has +# not succeeded (this is where ECC comes into play and is supposed to correct +# such problems). +# +# error_ratio=0..1000000 +#error_ratio=50 + +# Chip data bus width. All chips connected to the same controller must have +# the same bus width. +# +# width=[8|16] +width=8 + +# Bad block map. NANDsim emulates bad block behavior upon accessing a block +# with number from the specified list. +# +# bad_block_map=[bad_block1, bad_block2-bad_block3, ..bad_blockn] +bad_block_map=[100-200] + diff --git a/usr.sbin/nandtool/Makefile b/usr.sbin/nandtool/Makefile new file mode 100644 index 0000000..ae9de2d --- /dev/null +++ b/usr.sbin/nandtool/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +PROG= nandtool +SRCS= nandtool.c nand_read.c nand_write.c nand_erase.c nand_info.c +SRCS+= nand_readoob.c nand_writeoob.c +BINDIR= /usr/sbin +DPADD= ${LIBGEOM} +LDADD= -lgeom +MAN= nandtool.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/nandtool/nand_erase.c b/usr.sbin/nandtool/nand_erase.c new file mode 100644 index 0000000..50bfaa6 --- /dev/null +++ b/usr.sbin/nandtool/nand_erase.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/disk.h> +#include <libgeom.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_erase(struct cmd_param *params) +{ + struct chip_param_io chip_params; + char *dev; + int fd = -1, ret = 0; + off_t pos, count; + off_t start, nblocks, i; + int block_size, mult; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply valid 'dev' parameter.\n"); + return (1); + } + + if (param_has_value(params, "count")) + count = param_get_intx(params, "count"); + else + count = 1; + + if ((fd = g_open(dev, 1)) < 0) { + perrorf("Cannot open %s", dev); + return (1); + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_intx(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_intx(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_intx(params, "pos"); + mult = 1; + } else { + /* Erase whole chip */ + if (ioctl(fd, DIOCGMEDIASIZE, &count) == -1) { + ret = 1; + goto out; + } + + pos = 0; + mult = 1; + } + + if (pos % block_size) { + fprintf(stderr, "Position must be block-size aligned!\n"); + ret = 1; + goto out; + } + + count *= mult; + start = pos / block_size; + nblocks = count / block_size; + + for (i = 0; i < nblocks; i++) { + if (g_delete(fd, (start + i) * block_size, block_size) == -1) { + perrorf("Cannot erase block %d - probably a bad block", + start + i); + ret = 1; + } + } + +out: + g_close(fd); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_info.c b/usr.sbin/nandtool/nand_info.c new file mode 100644 index 0000000..38fe010 --- /dev/null +++ b/usr.sbin/nandtool/nand_info.c @@ -0,0 +1,86 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <libgeom.h> +#include <sys/disk.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_info(struct cmd_param *params) +{ + struct chip_param_io chip_params; + int fd = -1, ret = 0; + int block_size; + off_t chip_size, media_size; + const char *dev; + + if ((dev = param_get_string(params, "dev")) == NULL) { + fprintf(stderr, "Please supply 'dev' parameter, eg. " + "'dev=/dev/gnand0'\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + if (ioctl(fd, DIOCGMEDIASIZE, &media_size) == -1) { + perrorf("Cannot ioctl(DIOCGMEDIASIZE)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + chip_size = block_size * chip_params.blocks; + + printf("Device:\t\t\t%s\n", dev); + printf("Page size:\t\t%d bytes\n", chip_params.page_size); + printf("Block size:\t\t%d bytes (%d KB)\n", block_size, + block_size / 1024); + printf("OOB size per page:\t%d bytes\n", chip_params.oob_size); + printf("Chip size:\t\t%jd MB\n", (uintmax_t)(chip_size / 1024 / 1024)); + printf("Slice size:\t\t%jd MB\n", + (uintmax_t)(media_size / 1024 / 1024)); + +out: + g_close(fd); + + return (ret); +} diff --git a/usr.sbin/nandtool/nand_read.c b/usr.sbin/nandtool/nand_read.c new file mode 100644 index 0000000..5267b7d --- /dev/null +++ b/usr.sbin/nandtool/nand_read.c @@ -0,0 +1,139 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgeom.h> +#include <sys/disk.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_read(struct cmd_param *params) +{ + struct chip_param_io chip_params; + int fd = -1, out_fd = -1, done = 0, ret = 0; + char *dev, *out; + int pos, count, mult, block_size; + uint8_t *buf = NULL; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "You must specify 'dev' parameter\n"); + return (1); + } + + if ((out = param_get_string(params, "out"))) { + out_fd = open(out, O_WRONLY|O_CREAT); + if (out_fd == -1) { + perrorf("Cannot open %s for writing", out); + return (1); + } + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_int(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_int(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_int(params, "pos"); + mult = 1; + if (pos % chip_params.page_size) { + fprintf(stderr, "Position must be page-size aligned!\n"); + ret = 1; + goto out; + } + } else { + fprintf(stderr, "You must specify one of: 'block', 'page'," + "'pos' arguments\n"); + ret = 1; + goto out; + } + + if (!(param_has_value(params, "count"))) + count = mult; + else + count = param_get_int(params, "count") * mult; + + if (!(buf = malloc(chip_params.page_size))) { + perrorf("Cannot allocate buffer [size %x]", + chip_params.page_size); + ret = 1; + goto out; + } + + lseek(fd, pos, SEEK_SET); + + while (done < count) { + if ((ret = read(fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + perrorf("read error (read %d bytes)", ret); + goto out; + } + + if (out_fd != -1) { + done += ret; + if ((ret = write(out_fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + perrorf("write error (written %d bytes)", ret); + ret = 1; + goto out; + } + } else { + hexdumpoffset(buf, chip_params.page_size, done); + done += ret; + } + } + +out: + g_close(fd); + if (out_fd != -1) + close(out_fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_readoob.c b/usr.sbin/nandtool/nand_readoob.c new file mode 100644 index 0000000..37fd14b --- /dev/null +++ b/usr.sbin/nandtool/nand_readoob.c @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgeom.h> +#include <sys/types.h> +#include <sys/disk.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_read_oob(struct cmd_param *params) +{ + struct chip_param_io chip_params; + struct nand_oob_rw req; + char *dev, *out; + int fd = -1, fd_out = -1, ret = 0; + int page; + uint8_t *buf = NULL; + + if ((page = param_get_int(params, "page")) < 0) { + fprintf(stderr, "You must supply valid 'page' argument.\n"); + return (1); + } + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "You must supply 'dev' argument.\n"); + return (1); + } + + if ((out = param_get_string(params, "out"))) { + if ((fd_out = open(out, O_WRONLY | O_CREAT)) == -1) { + perrorf("Cannot open %s", out); + ret = 1; + goto out; + } + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + buf = malloc(chip_params.oob_size); + if (buf == NULL) { + perrorf("Cannot allocate %d bytes\n", chip_params.oob_size); + ret = 1; + goto out; + } + + req.page = page; + req.len = chip_params.oob_size; + req.data = buf; + + if (ioctl(fd, NAND_IO_OOB_READ, &req) == -1) { + perrorf("Cannot read OOB from %s", dev); + ret = 1; + goto out; + } + + if (fd_out != -1) + write(fd_out, buf, chip_params.oob_size); + else + hexdump(buf, chip_params.oob_size); + +out: + close(fd_out); + + if (fd != -1) + g_close(fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_write.c b/usr.sbin/nandtool/nand_write.c new file mode 100644 index 0000000..157c6aa --- /dev/null +++ b/usr.sbin/nandtool/nand_write.c @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgeom.h> +#include <sys/disk.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_write(struct cmd_param *params) +{ + struct chip_param_io chip_params; + char *dev, *file; + int in_fd = -1, ret = 0, done = 0; + int fd, block_size, mult, pos, count; + uint8_t *buf = NULL; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply 'dev' argument.\n"); + return (1); + } + + if (!(file = param_get_string(params, "in"))) { + fprintf(stderr, "Please supply 'in' argument.\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if ((in_fd = open(file, O_RDONLY)) == -1) { + perrorf("Cannot open file %s", file); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + block_size = chip_params.page_size * chip_params.pages_per_block; + + if (param_has_value(params, "page")) { + pos = chip_params.page_size * param_get_int(params, "page"); + mult = chip_params.page_size; + } else if (param_has_value(params, "block")) { + pos = block_size * param_get_int(params, "block"); + mult = block_size; + } else if (param_has_value(params, "pos")) { + pos = param_get_int(params, "pos"); + mult = 1; + if (pos % chip_params.page_size) { + fprintf(stderr, "Position must be page-size " + "aligned!\n"); + ret = 1; + goto out; + } + } else { + fprintf(stderr, "You must specify one of: 'block', 'page'," + "'pos' arguments\n"); + ret = 1; + goto out; + } + + if (!(param_has_value(params, "count"))) + count = mult; + else + count = param_get_int(params, "count") * mult; + + if (!(buf = malloc(chip_params.page_size))) { + perrorf("Cannot allocate buffer [size %x]", + chip_params.page_size); + ret = 1; + goto out; + } + + lseek(fd, pos, SEEK_SET); + + while (done < count) { + if ((ret = read(in_fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + if (ret > 0) { + /* End of file ahead, truncate here */ + break; + } else { + perrorf("Cannot read from %s", file); + ret = 1; + goto out; + } + } + + if ((ret = write(fd, buf, chip_params.page_size)) != + (int32_t)chip_params.page_size) { + ret = 1; + goto out; + } + + done += ret; + } + +out: + g_close(fd); + if (in_fd != -1) + close(in_fd); + if (buf) + free(buf); + + return (ret); +} + diff --git a/usr.sbin/nandtool/nand_writeoob.c b/usr.sbin/nandtool/nand_writeoob.c new file mode 100644 index 0000000..53cb32a --- /dev/null +++ b/usr.sbin/nandtool/nand_writeoob.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <libgeom.h> +#include <sys/disk.h> +#include <dev/nand/nand_dev.h> +#include "nandtool.h" + +int nand_write_oob(struct cmd_param *params) +{ + struct chip_param_io chip_params; + struct nand_oob_rw req; + char *dev, *in; + int fd = -1, fd_in = -1, ret = 0; + uint8_t *buf = NULL; + int page; + + if (!(dev = param_get_string(params, "dev"))) { + fprintf(stderr, "Please supply valid 'dev' parameter.\n"); + return (1); + } + + if (!(in = param_get_string(params, "in"))) { + fprintf(stderr, "Please supply valid 'in' parameter.\n"); + return (1); + } + + if ((page = param_get_int(params, "page")) < 0) { + fprintf(stderr, "Please supply valid 'page' parameter.\n"); + return (1); + } + + if ((fd = g_open(dev, 1)) == -1) { + perrorf("Cannot open %s", dev); + return (1); + } + + if ((fd_in = open(in, O_RDONLY)) == -1) { + perrorf("Cannot open %s", in); + ret = 1; + goto out; + } + + if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1) { + perrorf("Cannot ioctl(NAND_IO_GET_CHIP_PARAM)"); + ret = 1; + goto out; + } + + buf = malloc(chip_params.oob_size); + if (buf == NULL) { + perrorf("Cannot allocate %d bytes\n", chip_params.oob_size); + ret = 1; + goto out; + } + + if (read(fd_in, buf, chip_params.oob_size) == -1) { + perrorf("Cannot read from %s", in); + ret = 1; + goto out; + } + + req.page = page; + req.len = chip_params.oob_size; + req.data = buf; + + if (ioctl(fd, NAND_IO_OOB_PROG, &req) == -1) { + perrorf("Cannot write OOB to %s", dev); + ret = 1; + goto out; + } + +out: + g_close(fd); + if (fd_in != -1) + close(fd_in); + if (buf) + free(buf); + + return (ret); +} + + diff --git a/usr.sbin/nandtool/nandtool.8 b/usr.sbin/nandtool/nandtool.8 new file mode 100644 index 0000000..2cc4588 --- /dev/null +++ b/usr.sbin/nandtool/nandtool.8 @@ -0,0 +1,188 @@ +.\" Copyright (c) 2010 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 THE 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 10, 2012 +.Dt NANDTOOL 8 +.Os +.Sh NAME +.Nm nandtool +.Nd NAND devices swiss army knife +.Sh SYNOPSIS +.Nm +.Ar command +.Op Ar operands ... +.Sh DESCRIPTION +The +.Nm +utility can be used to perform various operations on +.Xr gnand 4 +devices (read, write, erase, +read and write OOB area and to get info about NAND flash chip). +.Pp +The following commands are available: +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.It Cm read Ns +Read pages from NAND device. +.It Cm write Ns +Write pages to NAND device. +.It Cm erase Ns +Erase blocks. +Requires offset aligned to block granularity. +.It Cm info Ns +Get information about NAND chip (page size, block size, OOB area size, chip size +and media size) +.It Cm readoob Ns +Read OOB area from specified page. +.It Cm writeoob Ns +Write OOB area bound to specified page. +.It Cm help Ns +Get usage info. +.El +.Sh COMMAND read +.Pp +The following operands are available for +.Nm +.Cm read +command: +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.It Cm dev Ns = Ns Ar <path> +Path to a +.Xr gnand 4 +device node, required for all operations. +.It Cm out Ns = Ns Ar <file> +Output file path. If not specified, page contents +will be dumped to stdout in format similar to +.Xr hexdump 1 +.It Cm page Ns = Ns Ar <n> +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar <n> +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar <n> +Offset on device, expressed in bytes (however, must be aligned +to page granularity). +.It Cm count Ns = Ns Ar <n> +Count of objects (pages, blocks, bytes). +.El +.Sh COMMAND readoob +.Bl -tag -width ".Cm of Ns = Ns Ar file" +.Pp +The following operands are available for +.Nm +.Cm readoob +command: +.Pp +.It Cm dev Ns = Ns Ar <path> +Path to NAND device node. +.It Cm page Ns = Ns Ar <n> +Offset on device, expressed as page number. +.It Cm out Ns = Ns Ar <file> +Output file path, optional. +.El +.Sh COMMAND write +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm write +command: +.It Cm dev Ns = Ns Ar <path> +Path to NAND device node. +.It Cm page Ns = Ns Ar <n> +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar <n> +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar <n> +Offset on device, expressed in bytes (however, must be aligned +to page granularity). +.It Cm in Ns = Ns Ar <file> +Input file path. +.El +.Sh COMMAND writeoob +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm writeoob +command: +.It Cm dev Ns = Ns Ar <path> +Path to NAND device node. +.It Cm page Ns = Ns Ar <n> +Offset on device, expressed as page number. +.It Cm in Ns = Ns Ar <file> +Input file path. +.El +.Sh COMMAND erase +.Bl -tag -width ".Cm of Ns = Ns Ar file" +The following operands are available for +.Nm +.Cm erase +command: +.It Cm dev Ns = Ns Ar <path> +Path to NAND device node. +.It Cm page Ns = Ns Ar <n> +Offset on device, expressed as page number. +.It Cm block Ns = Ns Ar <n> +Offset on device, expressed as block number. +.It Cm pos Ns = Ns Ar <n> +Offset on device, epressed in bytes (however, must be aligned +to block granularity). +.It Cm count Ns = Ns Ar <n> +Count of objects (pages, blocks, bytes). +.El +.Pp +WARNING: The only required parameter for the \fBerase\fP command is +.Ar dev . +When no other arguments are provided the whole device is erased! +.Sh COMMAND info +.Bl -tag -width ".Cm of Ns = Ns Ar file" +There is only one operand available for +.Nm +.Cm info +command: +.It Cm dev Ns = Ns Ar <path> +Path to NAND device node. +.El +.Sh COMMAND help +.Bl -tag -width ".Cm of Ns = Ns Ar file" +There is only one operand available for +.Nm +.Cm help +command: +.Pp +.It Cm topic Ns = Ns Ar <name> +Help topic. +.El +.Sh EXIT STATUS +.Ex -std +If the supplied argument +.Ar dev +points to a device node other than gnand<num> or gnand.raw<num> both +.Nm +.Cm readoob +and +.Nm +.Cm writeoob +return error. +.Sh SEE ALSO +.Xr gnand 4 diff --git a/usr.sbin/nandtool/nandtool.c b/usr.sbin/nandtool/nandtool.c new file mode 100644 index 0000000..bd7075b --- /dev/null +++ b/usr.sbin/nandtool/nandtool.c @@ -0,0 +1,283 @@ +/*- + * Copyright (c) 2010-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 THE 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> +#include <sysexits.h> +#include <libgeom.h> +#include "nandtool.h" +#include "usage.h" + +int usage(struct cmd_param *); + +static const struct { + const char *name; + const char *usage; + int (*handler)(struct cmd_param *); +} commands[] = { + { "help", nand_help_usage, usage }, + { "read", nand_read_usage, nand_read }, + { "write", nand_write_usage, nand_write }, + { "erase", nand_erase_usage, nand_erase }, + { "readoob", nand_read_oob_usage, nand_read_oob }, + { "writeoob", nand_write_oob_usage, nand_write_oob }, + { "info", nand_info_usage, nand_info }, + { NULL, NULL, NULL }, +}; + +static char * +_param_get_stringx(struct cmd_param *params, const char *name, int doexit) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++) { + if (!strcmp(params[i].name, name)) + return params[i].value; + } + + if (doexit) { + perrorf("Missing parameter %s", name); + exit(1); + } + return (NULL); +} + +char * +param_get_string(struct cmd_param *params, const char *name) +{ + + return (_param_get_stringx(params, name, 0)); +} + +static int +_param_get_intx(struct cmd_param *params, const char *name, int doexit) +{ + int ret; + char *str = _param_get_stringx(params, name, doexit); + + if (!str) + return (-1); + + errno = 0; + ret = (int)strtol(str, (char **)NULL, 10); + if (errno) { + if (doexit) { + perrorf("Invalid value for parameter %s", name); + exit(1); + } + return (-1); + } + + return (ret); +} + +int +param_get_intx(struct cmd_param *params, const char *name) +{ + + return (_param_get_intx(params, name, 1)); +} + +int +param_get_int(struct cmd_param *params, const char *name) +{ + + return (_param_get_intx(params, name, 0)); +} + +int +param_get_boolean(struct cmd_param *params, const char *name) +{ + char *str = param_get_string(params, name); + + if (!str) + return (0); + + if (!strcmp(str, "true") || !strcmp(str, "yes")) + return (1); + + return (0); +} + +int +param_has_value(struct cmd_param *params, const char *name) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++) { + if (!strcmp(params[i].name, name)) + return (1); + } + + return (0); +} + +int +param_get_count(struct cmd_param *params) +{ + int i; + + for (i = 0; params[i].name[0] != '\0'; i++); + + return (i); +} + +void +hexdumpoffset(uint8_t *buf, int length, int off) +{ + int i, j; + for (i = 0; i < length; i += 16) { + printf("%08x: ", off + i); + + for (j = 0; j < 16; j++) + printf("%02x ", buf[i+j]); + + printf("| "); + + for (j = 0; j < 16; j++) { + printf("%c", isalnum(buf[i+j]) + ? buf[i+j] + : '.'); + } + + printf("\n"); + } +} + +void +hexdump(uint8_t *buf, int length) +{ + + hexdumpoffset(buf, length, 0); +} + +void * +xmalloc(size_t len) +{ + void *ret = malloc(len); + + if (!ret) { + fprintf(stderr, "Cannot allocate buffer of %zd bytes. " + "Exiting.\n", len); + exit(EX_OSERR); + } + + return (ret); +} + +void +perrorf(const char *format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, ": %s\n", strerror(errno)); +} + +int +usage(struct cmd_param *params) +{ + int i; + + if (!params || !param_get_count(params)) { + fprintf(stderr, "Usage: nandtool <command> [arguments...]\n"); + fprintf(stderr, "Arguments are in form 'name=value'.\n\n"); + fprintf(stderr, "Available commands:\n"); + + for (i = 0; commands[i].name != NULL; i++) + fprintf(stderr, "\t%s\n", commands[i].name); + + fprintf(stderr, "\n"); + fprintf(stderr, "For information about particular command, " + "type:\n"); + fprintf(stderr, "'nandtool help topic=<command>'\n"); + } else if (param_has_value(params, "topic")) { + for (i = 0; commands[i].name != NULL; i++) { + if (!strcmp(param_get_string(params, "topic"), + commands[i].name)) { + fprintf(stderr, commands[i].usage, "nandtool"); + return (0); + } + } + + fprintf(stderr, "No such command\n"); + return (EX_SOFTWARE); + } else { + fprintf(stderr, "Wrong arguments given. Try: 'nandtool help'\n"); + } + + return (EX_USAGE); +} + +int +main(int argc, const char *argv[]) +{ + struct cmd_param *params; + int i, ret, idx; + + if (argc < 2) { + usage(NULL); + return (0); + } + + params = malloc(sizeof(struct cmd_param) * (argc - 1)); + + for (i = 2, idx = 0; i < argc; i++, idx++) { + if (sscanf(argv[i], "%63[^=]=%63s", params[idx].name, + params[idx].value) < 2) { + fprintf(stderr, "Syntax error in argument %d. " + "Argument should be in form 'name=value'.\n", i); + free(params); + return (-1); + } + } + + params[idx].name[0] = '\0'; + params[idx].value[0] = '\0'; + + for (i = 0; commands[i].name != NULL; i++) { + if (!strcmp(commands[i].name, argv[1])) { + ret = commands[i].handler(params); + free(params); + return (ret); + } + } + + free(params); + fprintf(stderr, "Unknown command. Try '%s help'\n", argv[0]); + + return (-1); +} + diff --git a/usr.sbin/nandtool/nandtool.h b/usr.sbin/nandtool/nandtool.h new file mode 100644 index 0000000..639f95e --- /dev/null +++ b/usr.sbin/nandtool/nandtool.h @@ -0,0 +1,57 @@ +/*- + * Copyright (c) 2010-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 THE 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. + * + * $FreeBSD$ + */ + +#ifndef __UTILS_H +#define __UTILS_H + +struct cmd_param +{ + char name[64]; + char value[64]; +}; + +char *param_get_string(struct cmd_param *, const char *); +int param_get_int(struct cmd_param *, const char *); +int param_get_intx(struct cmd_param *, const char *); +int param_get_boolean(struct cmd_param *, const char *); +int param_has_value(struct cmd_param *, const char *); +int param_get_count(struct cmd_param *); +void perrorf(const char *, ...); +void hexdumpoffset(uint8_t *, int, int); +void hexdump(uint8_t *, int); +void *xmalloc(size_t); + +/* Command handlers */ +int nand_read(struct cmd_param *); +int nand_write(struct cmd_param *); +int nand_read_oob(struct cmd_param *); +int nand_write_oob(struct cmd_param *); +int nand_erase(struct cmd_param *); +int nand_info(struct cmd_param *); + +#endif /* __UTILS_H */ diff --git a/usr.sbin/nandtool/usage.h b/usr.sbin/nandtool/usage.h new file mode 100644 index 0000000..74e8543 --- /dev/null +++ b/usr.sbin/nandtool/usage.h @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2010-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 THE 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. + * + * $FreeBSD$ + */ + +#ifndef __USAGE_H +#define __USAGE_H + +static const char nand_help_usage[] = + "Usage: %s help topic=<cmd>\n" + "\n" + "Arguments:\n" + "\tcmd\t- [help|read|write|erase|readoob|writeoob|info]\n" + "\n"; + +static const char nand_read_usage[] = + "Usage: %s read dev=<gnand_device> (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, must be page-aligned)\n" + "\tout\t- output file (hexdump to stdout if not supplied)\n" + "\n" + "Note that you can only specify only one of: 'block', 'page', 'pos'\n" + "parameters at once. 'count' parameter is meaningful in terms of used\n" + "unit (page, block or byte).\n"; + +static const char nand_write_usage[] = + "Usage: %s write dev=<gnand_device> in=<file> (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tin\t- path to input file which be writed to gnand\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, must be page-aligned)\n" + "\tcount\t- byte/page/block count\n" + "\n" + ""; + +static const char nand_erase_usage[] = + "Usage: %s erase dev=<gnand_device> (block|page|pos)=n [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tblock\t- starting block or\n" + "\tpage\t- starting page or\n" + "\tpos\t- starting position (in bytes, muse be block-aligned)\n" + "\tcount\t- byte/page/block count\n" + "\n" + "NOTE: position and count for erase operation MUST be block-aligned\n"; + +static const char nand_read_oob_usage[] = + "Usage: %s readoob dev=<gnand_device> page=n [out=file] [count=n]\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n" + "\tpage\t- page (page) number\n" + "\tout\t- outut file (hexdump to stdout if not supplied)\n" + "\tcount\t- page count (default is 1)\n" + "\n" + "If you supply count parameter with value other than 1, data will be\n" + "read from subsequent page's OOB areas\n"; + +static const char nand_write_oob_usage[] = + "Usage: %s writeoob dev=<gnand_device> in=<file> page=n [count=n]\n" + "\n" + "\tdev\t- path to gnand device node\n" + "\tin\t- path to file containing data which will be written\n" + "\tpage\t- page (page) number\n" + "\n" + "If you supply count parameter with value other than 1, data will be\n" + "written to subsequent page's OOB areas\n"; + +static const char nand_info_usage[] = + "Usage: %s info dev=<gnand_device>\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n"; + +static const char nand_stats_usage[] = + "Usage: %s stats dev=<gnand_device> (page|block)=<n>\n" + "\n" + "Arguments:\n" + "\tdev\t- path to gnand device node\n"; + +#endif /* __USAGE_H */ |