summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgber <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
committergber <gber@FreeBSD.org>2012-05-17 10:11:18 +0000
commit6f7c7353004e2ff9709b326a4008ce8ea63d9270 (patch)
treea325137a898341311de8641f7212e28b7d87950e
parent661b9d94414ea6d11d5b7960aef1f172975ce52b (diff)
downloadFreeBSD-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
-rw-r--r--etc/mtree/BSD.include.dist4
-rw-r--r--include/Makefile20
-rw-r--r--lib/Makefile5
-rw-r--r--lib/libnandfs/Makefile9
-rw-r--r--lib/libnandfs/libnandfs.h65
-rw-r--r--lib/libnandfs/nandfs.c247
-rw-r--r--lib/libstand/Makefile3
-rw-r--r--lib/libstand/nandfs.c1041
-rw-r--r--lib/libstand/stand.h1
-rw-r--r--sbin/Makefile5
-rw-r--r--sbin/nandfs/Makefile10
-rw-r--r--sbin/nandfs/lssnap.c112
-rw-r--r--sbin/nandfs/mksnap.c80
-rw-r--r--sbin/nandfs/nandfs.875
-rw-r--r--sbin/nandfs/nandfs.c74
-rw-r--r--sbin/nandfs/nandfs.h40
-rw-r--r--sbin/nandfs/rmsnap.c87
-rw-r--r--sbin/newfs_nandfs/Makefile9
-rw-r--r--sbin/newfs_nandfs/newfs_nandfs.877
-rw-r--r--sbin/newfs_nandfs/newfs_nandfs.c1176
-rw-r--r--share/man/man4/Makefile2
-rw-r--r--share/man/man4/nand.4140
-rw-r--r--share/man/man4/nandsim.4184
-rw-r--r--share/man/man5/Makefile4
-rw-r--r--share/man/man5/nandfs.5129
-rw-r--r--share/mk/bsd.own.mk1
-rw-r--r--sys/boot/arm/uboot/Makefile8
-rw-r--r--sys/boot/arm/uboot/conf.c3
-rw-r--r--sys/boot/arm/uboot/version1
-rw-r--r--sys/boot/i386/loader/Makefile3
-rw-r--r--sys/boot/i386/loader/conf.c3
-rw-r--r--sys/conf/files29
-rw-r--r--sys/conf/options1
-rw-r--r--sys/dev/nand/nand.c832
-rw-r--r--sys/dev/nand/nand.h385
-rw-r--r--sys/dev/nand/nand_bbt.c273
-rw-r--r--sys/dev/nand/nand_cdev.c413
-rw-r--r--sys/dev/nand/nand_dev.h90
-rw-r--r--sys/dev/nand/nand_ecc_pos.h56
-rw-r--r--sys/dev/nand/nand_generic.c1320
-rw-r--r--sys/dev/nand/nand_geom.c414
-rw-r--r--sys/dev/nand/nand_id.c60
-rw-r--r--sys/dev/nand/nand_if.m168
-rw-r--r--sys/dev/nand/nandbus.c530
-rw-r--r--sys/dev/nand/nandbus.h49
-rw-r--r--sys/dev/nand/nandbus_if.m100
-rw-r--r--sys/dev/nand/nandsim.c665
-rw-r--r--sys/dev/nand/nandsim.h175
-rw-r--r--sys/dev/nand/nandsim_chip.c901
-rw-r--r--sys/dev/nand/nandsim_chip.h159
-rw-r--r--sys/dev/nand/nandsim_ctrl.c396
-rw-r--r--sys/dev/nand/nandsim_log.c186
-rw-r--r--sys/dev/nand/nandsim_log.h52
-rw-r--r--sys/dev/nand/nandsim_swap.c389
-rw-r--r--sys/dev/nand/nandsim_swap.h64
-rw-r--r--sys/dev/nand/nfc_if.m165
-rw-r--r--sys/dev/nand/nfc_mv.c236
-rw-r--r--sys/fs/nandfs/bmap.c621
-rw-r--r--sys/fs/nandfs/bmap.h40
-rw-r--r--sys/fs/nandfs/nandfs.h310
-rw-r--r--sys/fs/nandfs/nandfs_alloc.c364
-rw-r--r--sys/fs/nandfs/nandfs_bmap.c230
-rw-r--r--sys/fs/nandfs/nandfs_buffer.c83
-rw-r--r--sys/fs/nandfs/nandfs_cleaner.c621
-rw-r--r--sys/fs/nandfs/nandfs_cpfile.c776
-rw-r--r--sys/fs/nandfs/nandfs_dat.c344
-rw-r--r--sys/fs/nandfs/nandfs_dir.c314
-rw-r--r--sys/fs/nandfs/nandfs_fs.h565
-rw-r--r--sys/fs/nandfs/nandfs_ifile.c213
-rw-r--r--sys/fs/nandfs/nandfs_mount.h50
-rw-r--r--sys/fs/nandfs/nandfs_segment.c1329
-rw-r--r--sys/fs/nandfs/nandfs_subr.c1120
-rw-r--r--sys/fs/nandfs/nandfs_subr.h238
-rw-r--r--sys/fs/nandfs/nandfs_sufile.c569
-rw-r--r--sys/fs/nandfs/nandfs_vfsops.c1590
-rw-r--r--sys/fs/nandfs/nandfs_vnops.c2455
-rw-r--r--sys/modules/Makefile10
-rw-r--r--sys/modules/nandfs/Makefile12
-rw-r--r--sys/modules/nandsim/Makefile11
-rw-r--r--tools/build/options/WITHOUT_NAND2
-rw-r--r--tools/build/options/WITH_NAND2
-rw-r--r--usr.sbin/Makefile5
-rw-r--r--usr.sbin/nandsim/Makefile8
-rw-r--r--usr.sbin/nandsim/nandsim.8230
-rw-r--r--usr.sbin/nandsim/nandsim.c1397
-rw-r--r--usr.sbin/nandsim/nandsim_cfgparse.c957
-rw-r--r--usr.sbin/nandsim/nandsim_cfgparse.h86
-rw-r--r--usr.sbin/nandsim/nandsim_rcfile.c440
-rw-r--r--usr.sbin/nandsim/nandsim_rcfile.h70
-rw-r--r--usr.sbin/nandsim/sample.conf174
-rw-r--r--usr.sbin/nandtool/Makefile11
-rw-r--r--usr.sbin/nandtool/nand_erase.c114
-rw-r--r--usr.sbin/nandtool/nand_info.c86
-rw-r--r--usr.sbin/nandtool/nand_read.c139
-rw-r--r--usr.sbin/nandtool/nand_readoob.c111
-rw-r--r--usr.sbin/nandtool/nand_write.c143
-rw-r--r--usr.sbin/nandtool/nand_writeoob.c113
-rw-r--r--usr.sbin/nandtool/nandtool.8188
-rw-r--r--usr.sbin/nandtool/nandtool.c283
-rw-r--r--usr.sbin/nandtool/nandtool.h57
-rw-r--r--usr.sbin/nandtool/usage.h112
101 files changed, 28313 insertions, 6 deletions
diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist
index 2de9e84..c2a0e91 100644
--- a/etc/mtree/BSD.include.dist
+++ b/etc/mtree/BSD.include.dist
@@ -122,6 +122,8 @@
mpilib
..
..
+ nand
+ ..
ofw
..
pbio
@@ -154,6 +156,8 @@
..
msdosfs
..
+ nandfs
+ ..
nfs
..
ntfs
diff --git a/include/Makefile b/include/Makefile
index 2cbc9d2..c67dc75 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -43,7 +43,7 @@ LSUBDIRS= cam/ata cam/scsi \
dev/ic dev/iicbus ${_dev_ieee488} dev/io dev/lmc dev/mfi dev/ofw \
dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus \
dev/speaker dev/usb dev/utopia dev/vkbd dev/wi \
- fs/devfs fs/fdescfs fs/msdosfs fs/nfs fs/ntfs fs/nullfs \
+ fs/devfs fs/fdescfs fs/msdosfs fs/nandfs fs/nfs fs/ntfs fs/nullfs \
${_fs_nwfs} fs/portalfs fs/procfs fs/smbfs fs/udf fs/unionfs \
geom/cache geom/concat geom/eli geom/gate geom/journal geom/label \
geom/mirror geom/mountver geom/multipath geom/nop \
@@ -157,7 +157,7 @@ copies:
done
.endif
.endfor
-.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr} ${LSUBSUBDIRS}
+.for i in ${LDIRS} ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand} ${LSUBSUBDIRS}
cd ${.CURDIR}/../sys; \
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 $i/*.h \
${DESTDIR}${INCLUDEDIR}/$i
@@ -168,6 +168,13 @@ copies:
cd ${.CURDIR}/../sys/dev/bktr; \
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 ioctl_*.h \
${DESTDIR}${INCLUDEDIR}/dev/bktr
+.if ${MK_NAND} != "no"
+ cd ${.CURDIR}/../sys/dev/nand; \
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nandsim.h \
+ ${DESTDIR}${INCLUDEDIR}/dev/nand; \
+ ${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 nand_dev.h \
+ ${DESTDIR}${INCLUDEDIR}/dev/nand
+.endif
cd ${.CURDIR}/../sys/contrib/altq/altq; \
${INSTALL} -C -o ${BINOWN} -g ${BINGRP} -m 444 *.h \
${DESTDIR}${INCLUDEDIR}/altq
@@ -224,7 +231,7 @@ symlinks:
ln -fs ../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \
done
.endfor
-.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr}
+.for i in ${LSUBDIRS:Ndev/acpica:Ndev/bktr:Ndev/nand}
cd ${.CURDIR}/../sys/$i; \
for h in *.h; do \
ln -fs ../../../../sys/$i/$$h ${DESTDIR}${INCLUDEDIR}/$i; \
@@ -240,6 +247,13 @@ symlinks:
ln -fs ../../../../sys/dev/bktr/$$h \
${DESTDIR}${INCLUDEDIR}/dev/bktr; \
done
+.if ${MK_NAND} != "no"
+ cd ${.CURDIR}/../sys/dev/nand; \
+ for h in nandsim.h nand_dev.h; do \
+ ln -fs ../../../../sys/dev/nand/$$h \
+ ${DESTDIR}${INCLUDEDIR}/dev/nand; \
+ done
+.endif
.for i in ${LSUBSUBDIRS}
cd ${.CURDIR}/../sys/$i; \
for h in *.h; do \
diff --git a/lib/Makefile b/lib/Makefile
index 2bff80a..a366f5f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -88,6 +88,7 @@ SUBDIR= ${SUBDIR_ORDERED} \
libmemstat \
${_libmilter} \
${_libmp} \
+ ${_libnandfs} \
${_libncp} \
${_libngatm} \
libopie \
@@ -175,6 +176,10 @@ _libipx= libipx
_libthr= libthr
.endif
+.if ${MK_NAND} != "no"
+_libnandfs= libnandfs
+.endif
+
.if ${MK_NETGRAPH} != "no"
_libnetgraph= libnetgraph
.endif
diff --git a/lib/libnandfs/Makefile b/lib/libnandfs/Makefile
new file mode 100644
index 0000000..d87573e
--- /dev/null
+++ b/lib/libnandfs/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+LIB= nandfs
+SRCS+= nandfs.c
+INCS= libnandfs.h
+
+CFLAGS += -I${.CURDIR}
+
+.include <bsd.lib.mk>
diff --git a/lib/libnandfs/libnandfs.h b/lib/libnandfs/libnandfs.h
new file mode 100644
index 0000000..8338c63
--- /dev/null
+++ b/lib/libnandfs/libnandfs.h
@@ -0,0 +1,65 @@
+/*-
+ * 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 _LIBNANDFS_NANDFS_H
+#define _LIBNANDFS_NANDFS_H
+
+struct nandfs {
+ struct nandfs_fsdata n_fsdata;
+ struct nandfs_super_block n_sb;
+ char n_ioc[MNAMELEN];
+ char n_dev[MNAMELEN];
+ int n_iocfd;
+ int n_devfd;
+ int n_flags;
+ char n_errmsg[120];
+};
+
+int nandfs_iserror(struct nandfs *);
+const char *nandfs_errmsg(struct nandfs *);
+
+void nandfs_init(struct nandfs *, const char *);
+void nandfs_destroy(struct nandfs *);
+
+const char *nandfs_dev(struct nandfs *);
+
+int nandfs_open(struct nandfs *);
+void nandfs_close(struct nandfs *);
+
+int nandfs_get_cpstat(struct nandfs *, struct nandfs_cpstat *);
+
+ssize_t nandfs_get_cp(struct nandfs *, uint64_t,
+ struct nandfs_cpinfo *, size_t);
+
+ssize_t nandfs_get_snap(struct nandfs *, uint64_t,
+ struct nandfs_cpinfo *, size_t);
+
+int nandfs_make_snap(struct nandfs *, uint64_t *);
+int nandfs_delete_snap(struct nandfs *, uint64_t);
+
+#endif /* _LIBNANDFS_NANDFS_H */
diff --git a/lib/libnandfs/nandfs.c b/lib/libnandfs/nandfs.c
new file mode 100644
index 0000000..fd59a92
--- /dev/null
+++ b/lib/libnandfs/nandfs.c
@@ -0,0 +1,247 @@
+/*-
+ * 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/stdint.h>
+#include <sys/ucred.h>
+#include <sys/disk.h>
+#include <sys/mount.h>
+
+#include <fs/nandfs/nandfs_fs.h>
+#include <libnandfs.h>
+
+#define NANDFS_IS_VALID 0x1
+#define NANDFS_IS_OPENED 0x2
+#define NANDFS_IS_OPENED_DEV 0x4
+#define NANDFS_IS_ERROR 0x8
+
+#define DEBUG
+#undef DEBUG
+#ifdef DEBUG
+#define NANDFS_DEBUG(fmt, args...) do { \
+ printf("libnandfs:" fmt "\n", ##args); } while (0)
+#else
+#define NANDFS_DEBUG(fmt, args...)
+#endif
+
+#define NANDFS_ASSERT_VALID(fs) assert((fs)->n_flags & NANDFS_IS_VALID)
+#define NANDFS_ASSERT_VALID_DEV(fs) \
+ assert(((fs)->n_flags & (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV)) == \
+ (NANDFS_IS_VALID | NANDFS_IS_OPENED_DEV))
+
+int
+nandfs_iserror(struct nandfs *fs)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+
+ return (fs->n_flags & NANDFS_IS_ERROR);
+}
+
+const char *
+nandfs_errmsg(struct nandfs *fs)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+
+ assert(nandfs_iserror(fs));
+ assert(fs->n_errmsg);
+ return (fs->n_errmsg);
+}
+
+static void
+nandfs_seterr(struct nandfs *fs, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(fs->n_errmsg, sizeof(fs->n_errmsg), fmt, ap);
+ va_end(ap);
+ fs->n_flags |= NANDFS_IS_ERROR;
+}
+
+const char *
+nandfs_dev(struct nandfs *fs)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+ return (fs->n_dev);
+}
+
+void
+nandfs_init(struct nandfs *fs, const char *dir)
+{
+
+ snprintf(fs->n_ioc, sizeof(fs->n_ioc), "%s/%s", dir, ".");
+ fs->n_iocfd = -1;
+ fs->n_flags = NANDFS_IS_VALID;
+}
+
+void
+nandfs_destroy(struct nandfs *fs)
+{
+
+ assert(fs->n_iocfd == -1);
+ fs->n_flags &=
+ ~(NANDFS_IS_ERROR | NANDFS_IS_VALID);
+ assert(fs->n_flags == 0);
+}
+
+int
+nandfs_open(struct nandfs *fs)
+{
+ struct nandfs_fsinfo fsinfo;
+
+ fs->n_flags |= NANDFS_IS_OPENED;
+
+ fs->n_iocfd = open(fs->n_ioc, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP |
+ S_IWGRP | S_IROTH | S_IWOTH);
+ if (fs->n_iocfd == -1) {
+ nandfs_seterr(fs, "couldn't open %s: %s", fs->n_ioc,
+ strerror(errno));
+ return (-1);
+ }
+
+ if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_FSINFO, &fsinfo) == -1) {
+ nandfs_seterr(fs, "couldn't fetch fsinfo: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ memcpy(&fs->n_fsdata, &fsinfo.fs_fsdata, sizeof(fs->n_fsdata));
+ memcpy(&fs->n_sb, &fsinfo.fs_super, sizeof(fs->n_sb));
+ snprintf(fs->n_dev, sizeof(fs->n_dev), "%s", fsinfo.fs_dev);
+
+ return (0);
+}
+
+void
+nandfs_close(struct nandfs *fs)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+ assert(fs->n_flags & NANDFS_IS_OPENED);
+
+ close(fs->n_iocfd);
+ fs->n_iocfd = -1;
+ fs->n_flags &= ~NANDFS_IS_OPENED;
+}
+
+int
+nandfs_get_cpstat(struct nandfs *fs, struct nandfs_cpstat *cpstat)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+
+ if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPSTAT, cpstat) == -1) {
+ nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPSTAT: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static ssize_t
+nandfs_get_cpinfo(struct nandfs *fs, uint64_t cno, int mode,
+ struct nandfs_cpinfo *cpinfo, size_t nci)
+{
+ struct nandfs_argv args;
+
+ NANDFS_ASSERT_VALID(fs);
+
+ args.nv_base = (u_long)cpinfo;
+ args.nv_nmembs = nci;
+ args.nv_index = cno;
+ args.nv_flags = mode;
+
+ if (ioctl(fs->n_iocfd, NANDFS_IOCTL_GET_CPINFO, &args) == -1) {
+ nandfs_seterr(fs, "ioctl NANDFS_IOCTL_GET_CPINFO: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (args.nv_nmembs);
+}
+
+ssize_t
+nandfs_get_cp(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
+ size_t nci)
+{
+
+ return (nandfs_get_cpinfo(fs, cno, NANDFS_CHECKPOINT, cpinfo, nci));
+}
+
+ssize_t
+nandfs_get_snap(struct nandfs *fs, uint64_t cno, struct nandfs_cpinfo *cpinfo,
+ size_t nci)
+{
+
+ return (nandfs_get_cpinfo(fs, cno, NANDFS_SNAPSHOT, cpinfo, nci));
+}
+
+int
+nandfs_make_snap(struct nandfs *fs, uint64_t *cno)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+
+ if (ioctl(fs->n_iocfd, NANDFS_IOCTL_MAKE_SNAP, cno) == -1) {
+ nandfs_seterr(fs, "ioctl NANDFS_IOCTL_MAKE_SNAP: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+nandfs_delete_snap(struct nandfs *fs, uint64_t cno)
+{
+
+ NANDFS_ASSERT_VALID(fs);
+
+ if (ioctl(fs->n_iocfd, NANDFS_IOCTL_DELETE_SNAP, &cno) == -1) {
+ nandfs_seterr(fs, "ioctl NANDFS_IOCTL_DELETE_SNAP: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/lib/libstand/Makefile b/lib/libstand/Makefile
index 567c886..0ad8fa5 100644
--- a/lib/libstand/Makefile
+++ b/lib/libstand/Makefile
@@ -150,6 +150,9 @@ SRCS+= bootp.c rarp.c bootparam.c
SRCS+= ufs.c nfs.c cd9660.c tftp.c gzipfs.c bzipfs.c
SRCS+= dosfs.c ext2fs.c
SRCS+= splitfs.c
+.if ${MK_NAND} != "no"
+SRCS+= nandfs.c
+.endif
.include <bsd.lib.mk>
diff --git a/lib/libstand/nandfs.c b/lib/libstand/nandfs.c
new file mode 100644
index 0000000..67e2fea
--- /dev/null
+++ b/lib/libstand/nandfs.c
@@ -0,0 +1,1041 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stdint.h>
+#include <ufs/ufs/dinode.h>
+#include <fs/nandfs/nandfs_fs.h>
+#include "stand.h"
+#include "string.h"
+#include "zlib.h"
+
+#define DEBUG
+#undef DEBUG
+#ifdef DEBUG
+#define NANDFS_DEBUG(fmt, args...) do { \
+ printf("NANDFS_DEBUG:" fmt "\n", ##args); } while (0)
+#else
+#define NANDFS_DEBUG(fmt, args...)
+#endif
+
+struct nandfs_mdt {
+ uint32_t entries_per_block;
+ uint32_t entries_per_group;
+ uint32_t blocks_per_group;
+ uint32_t groups_per_desc_block; /* desc is super group */
+ uint32_t blocks_per_desc_block; /* desc is super group */
+};
+
+struct bmap_buf {
+ LIST_ENTRY(bmap_buf) list;
+ nandfs_daddr_t blknr;
+ uint64_t *map;
+};
+
+struct nandfs_node {
+ struct nandfs_inode *inode;
+ LIST_HEAD(, bmap_buf) bmap_bufs;
+};
+struct nandfs {
+ int nf_blocksize;
+ int nf_sectorsize;
+ int nf_cpno;
+
+ struct open_file *nf_file;
+ struct nandfs_node *nf_opened_node;
+ u_int nf_offset;
+ uint8_t *nf_buf;
+ int64_t nf_buf_blknr;
+
+ struct nandfs_fsdata *nf_fsdata;
+ struct nandfs_super_block *nf_sb;
+ struct nandfs_segment_summary nf_segsum;
+ struct nandfs_checkpoint nf_checkpoint;
+ struct nandfs_super_root nf_sroot;
+ struct nandfs_node nf_ifile;
+ struct nandfs_node nf_datfile;
+ struct nandfs_node nf_cpfile;
+ struct nandfs_mdt nf_datfile_mdt;
+ struct nandfs_mdt nf_ifile_mdt;
+
+ int nf_nindir[NIADDR];
+};
+
+static int nandfs_open(const char *, struct open_file *);
+static int nandfs_close(struct open_file *);
+static int nandfs_read(struct open_file *, void *, size_t, size_t *);
+static off_t nandfs_seek(struct open_file *, off_t, int);
+static int nandfs_stat(struct open_file *, struct stat *);
+static int nandfs_readdir(struct open_file *, struct dirent *);
+
+static int nandfs_buf_read(struct nandfs *, char **, size_t *);
+static struct nandfs_node *nandfs_lookup_inode(struct nandfs *, nandfs_daddr_t);
+static struct nandfs_node *nandfs_lookup_path(struct nandfs *, const char *);
+static int nandfs_read_inode(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, u_int, void *, int);
+static int nandfs_read_blk(struct nandfs *, nandfs_daddr_t, void *, int);
+static int nandfs_bmap_lookup(struct nandfs *, struct nandfs_node *,
+ nandfs_lbn_t, nandfs_daddr_t *, int);
+static int nandfs_get_checkpoint(struct nandfs *, uint64_t,
+ struct nandfs_checkpoint *);
+static nandfs_daddr_t nandfs_vtop(struct nandfs *, nandfs_daddr_t);
+static void nandfs_calc_mdt_consts(int, struct nandfs_mdt *, int);
+static void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t,
+ nandfs_daddr_t *, uint32_t *);
+static int ioread(struct open_file *, off_t, void *, u_int);
+static int nandfs_probe_sectorsize(struct open_file *);
+
+struct fs_ops nandfs_fsops = {
+ "nandfs",
+ nandfs_open,
+ nandfs_close,
+ nandfs_read,
+ null_write,
+ nandfs_seek,
+ nandfs_stat,
+ nandfs_readdir
+};
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+static int
+nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
+{
+ uint32_t fsdata_crc, comp_crc;
+
+ if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
+ return (0);
+
+ /* Preserve crc */
+ fsdata_crc = fsdata->f_sum;
+
+ /* Calculate */
+ fsdata->f_sum = (0);
+ comp_crc = crc32(0, (uint8_t *)fsdata, fsdata->f_bytes);
+
+ /* Restore */
+ fsdata->f_sum = fsdata_crc;
+
+ /* Check CRC */
+ return (fsdata_crc == comp_crc);
+}
+
+static int
+nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ uint32_t super_crc, comp_crc;
+
+ /* Check super block magic */
+ if (super->s_magic != NANDFS_SUPER_MAGIC)
+ return (0);
+
+ /* Preserve CRC */
+ super_crc = super->s_sum;
+
+ /* Calculate */
+ super->s_sum = (0);
+ comp_crc = crc32(0, (uint8_t *)super, fsdata->f_sbbytes);
+
+ /* Restore */
+ super->s_sum = super_crc;
+
+ /* Check CRC */
+ return (super_crc == comp_crc);
+}
+
+static int
+nandfs_find_super_block(struct nandfs *fs, struct open_file *f)
+{
+ struct nandfs_super_block *sb;
+ int i, j, n;
+ int sectors_to_read, error;
+
+ sb = malloc(fs->nf_sectorsize);
+ if (sb == NULL)
+ return (ENOMEM);
+
+ memset(fs->nf_sb, 0, sizeof(*fs->nf_sb));
+
+ sectors_to_read = (NANDFS_NFSAREAS * fs->nf_fsdata->f_erasesize) /
+ fs->nf_sectorsize;
+ for (i = 0; i < sectors_to_read; i++) {
+ NANDFS_DEBUG("reading i %d offset %d\n", i,
+ i * fs->nf_sectorsize);
+ error = ioread(f, i * fs->nf_sectorsize, (char *)sb,
+ fs->nf_sectorsize);
+ if (error) {
+ NANDFS_DEBUG("error %d\n", error);
+ continue;
+ }
+ n = fs->nf_sectorsize / sizeof(struct nandfs_super_block);
+ if ((i * fs->nf_sectorsize) % fs->nf_fsdata->f_erasesize == 0) {
+ if (fs->nf_sectorsize == sizeof(struct nandfs_fsdata))
+ continue;
+ else {
+ sb += (sizeof(struct nandfs_fsdata) /
+ sizeof(struct nandfs_super_block));
+ n -= (sizeof(struct nandfs_fsdata) /
+ sizeof(struct nandfs_super_block));
+ }
+ }
+
+ for (j = 0; j < n; j++) {
+ if (!nandfs_check_superblock_crc(fs->nf_fsdata, &sb[j]))
+ continue;
+ NANDFS_DEBUG("magic %x wtime %jd\n", sb->s_magic,
+ sb->s_wtime);
+ if (sb[j].s_wtime > fs->nf_sb->s_wtime)
+ memcpy(fs->nf_sb, &sb[j], sizeof(*fs->nf_sb));
+ }
+ }
+
+ free(sb);
+
+ return (fs->nf_sb->s_magic != 0 ? 0 : EINVAL);
+}
+
+static int
+nandfs_find_fsdata(struct nandfs *fs, struct open_file *f)
+{
+ int offset, error, i;
+
+ NANDFS_DEBUG("starting\n");
+
+ offset = 0;
+ for (i = 0; i < 64 * NANDFS_NFSAREAS; i++) {
+ error = ioread(f, offset, (char *)fs->nf_fsdata,
+ sizeof(struct nandfs_fsdata));
+ if (error)
+ return (error);
+ if (fs->nf_fsdata->f_magic == NANDFS_FSDATA_MAGIC) {
+ NANDFS_DEBUG("found at %x, volume %s\n", offset,
+ fs->nf_fsdata->f_volume_name);
+ if (nandfs_check_fsdata_crc(fs->nf_fsdata))
+ break;
+ }
+ offset += fs->nf_sectorsize;
+ }
+
+ return (error);
+}
+
+static int
+nandfs_read_structures(struct nandfs *fs, struct open_file *f)
+{
+ int error;
+
+ error = nandfs_find_fsdata(fs, f);
+ if (error)
+ return (error);
+
+ error = nandfs_find_super_block(fs, f);
+
+ if (error == 0)
+ NANDFS_DEBUG("selected sb with w_time %jd last_pseg %jx\n",
+ fs->nf_sb->s_wtime, fs->nf_sb->s_last_pseg);
+
+ return (error);
+}
+
+static int
+nandfs_mount(struct nandfs *fs, struct open_file *f)
+{
+ int err = 0, level;
+ uint64_t last_pseg;
+
+ fs->nf_fsdata = malloc(sizeof(struct nandfs_fsdata));
+ fs->nf_sb = malloc(sizeof(struct nandfs_super_block));
+
+ err = nandfs_read_structures(fs, f);
+ if (err) {
+ free(fs->nf_fsdata);
+ free(fs->nf_sb);
+ return (err);
+ }
+
+ fs->nf_blocksize = 1 << (fs->nf_fsdata->f_log_block_size + 10);
+
+ NANDFS_DEBUG("using superblock with wtime %jd\n", fs->nf_sb->s_wtime);
+
+ fs->nf_cpno = fs->nf_sb->s_last_cno;
+ last_pseg = fs->nf_sb->s_last_pseg;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_datfile_mdt,
+ fs->nf_fsdata->f_dat_entry_size);
+
+ nandfs_calc_mdt_consts(fs->nf_blocksize, &fs->nf_ifile_mdt,
+ fs->nf_fsdata->f_inode_size);
+
+ err = ioread(f, last_pseg * fs->nf_blocksize, &fs->nf_segsum,
+ sizeof(struct nandfs_segment_summary));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ err = ioread(f, (last_pseg + fs->nf_segsum.ss_nblocks - 1) *
+ fs->nf_blocksize, &fs->nf_sroot, sizeof(struct nandfs_super_root));
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ fs->nf_datfile.inode = &fs->nf_sroot.sr_dat;
+ LIST_INIT(&fs->nf_datfile.bmap_bufs);
+ fs->nf_cpfile.inode = &fs->nf_sroot.sr_cpfile;
+ LIST_INIT(&fs->nf_cpfile.bmap_bufs);
+
+ err = nandfs_get_checkpoint(fs, fs->nf_cpno, &fs->nf_checkpoint);
+ if (err) {
+ free(fs->nf_sb);
+ free(fs->nf_fsdata);
+ return (err);
+ }
+
+ NANDFS_DEBUG("checkpoint cp_cno=%lld\n", fs->nf_checkpoint.cp_cno);
+ NANDFS_DEBUG("checkpoint cp_inodes_count=%lld\n",
+ fs->nf_checkpoint.cp_inodes_count);
+ NANDFS_DEBUG("checkpoint cp_ifile_inode.i_blocks=%lld\n",
+ fs->nf_checkpoint.cp_ifile_inode.i_blocks);
+
+ fs->nf_ifile.inode = &fs->nf_checkpoint.cp_ifile_inode;
+ LIST_INIT(&fs->nf_ifile.bmap_bufs);
+ return (0);
+}
+
+#define NINDIR(fs) ((fs)->nf_blocksize / sizeof(nandfs_daddr_t))
+
+static int
+nandfs_open(const char *path, struct open_file *f)
+{
+ struct nandfs *fs;
+ struct nandfs_node *node;
+ int err, bsize, level;
+
+ NANDFS_DEBUG("nandfs_open('%s', %p)\n", path, f);
+
+ fs = malloc(sizeof(struct nandfs));
+ f->f_fsdata = fs;
+ fs->nf_file = f;
+
+ bsize = nandfs_probe_sectorsize(f);
+ if (bsize < 0) {
+ printf("Cannot probe medium sector size\n");
+ return (EINVAL);
+ }
+
+ fs->nf_sectorsize = bsize;
+
+ /*
+ * Calculate indirect block levels.
+ */
+ nandfs_daddr_t mult;
+
+ mult = 1;
+ for (level = 0; level < NIADDR; level++) {
+ mult *= NINDIR(fs);
+ fs->nf_nindir[level] = mult;
+ }
+
+ NANDFS_DEBUG("fs %p nf_sectorsize=%x\n", fs, fs->nf_sectorsize);
+
+ err = nandfs_mount(fs, f);
+ if (err) {
+ NANDFS_DEBUG("Cannot mount nandfs: %s\n", strerror(err));
+ return (err);
+ }
+
+ node = nandfs_lookup_path(fs, path);
+ if (node == NULL)
+ return (EINVAL);
+
+ fs->nf_offset = 0;
+ fs->nf_buf = NULL;
+ fs->nf_buf_blknr = -1;
+ fs->nf_opened_node = node;
+ LIST_INIT(&fs->nf_opened_node->bmap_bufs);
+ return (0);
+}
+
+static int
+nandfs_free_node(struct nandfs_node *node)
+{
+ struct bmap_buf *bmap, *tmp;
+
+ free(node->inode);
+ LIST_FOREACH_SAFE(bmap, &node->bmap_bufs, list, tmp) {
+ LIST_REMOVE(bmap, list);
+ free(bmap->map);
+ free(bmap);
+ }
+ free(node);
+}
+
+static int
+nandfs_close(struct open_file *f)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_close(%p)\n", f);
+
+ if (fs->nf_buf != NULL)
+ free(fs->nf_buf);
+
+ nandfs_free_node(fs->nf_opened_node);
+ free(fs->nf_sb);
+ free(fs);
+}
+
+static int
+nandfs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
+{
+ struct nandfs *fs = (struct nandfs *)f->f_fsdata;
+ size_t csize, buf_size;
+ uint8_t *buf;
+ int error = 0;
+
+ NANDFS_DEBUG("nandfs_read(file=%p, addr=%p, size=%d)\n", f, addr, size);
+
+ while (size != 0) {
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size)
+ break;
+
+ error = nandfs_buf_read(fs, (void *)&buf, &buf_size);
+ if (error)
+ break;
+
+ csize = size;
+ if (csize > buf_size)
+ csize = buf_size;
+
+ bcopy(buf, addr, csize);
+
+ fs->nf_offset += csize;
+ addr = (char *)addr + csize;
+ size -= csize;
+ }
+
+ if (resid)
+ *resid = size;
+ return (error);
+}
+
+static off_t
+nandfs_seek(struct open_file *f, off_t offset, int where)
+{
+ struct nandfs *fs = f->f_fsdata;
+ off_t off;
+ u_int size;
+
+ NANDFS_DEBUG("nandfs_seek(file=%p, offset=%lld, where=%d)\n", f,
+ offset, where);
+
+ size = fs->nf_opened_node->inode->i_size;
+
+ switch (where) {
+ case SEEK_SET:
+ off = 0;
+ break;
+ case SEEK_CUR:
+ off = fs->nf_offset;
+ break;
+ case SEEK_END:
+ off = size;
+ break;
+ default:
+ errno = EINVAL;
+ return (-1);
+ }
+
+ off += offset;
+ if (off < 0 || off > size) {
+ errno = EINVAL;
+ return(-1);
+ }
+
+ fs->nf_offset = (u_int)off;
+
+ return (off);
+}
+
+static int
+nandfs_stat(struct open_file *f, struct stat *sb)
+{
+ struct nandfs *fs = f->f_fsdata;
+
+ NANDFS_DEBUG("nandfs_stat(file=%p, stat=%p)\n", f, sb);
+
+ sb->st_size = fs->nf_opened_node->inode->i_size;
+ sb->st_mode = fs->nf_opened_node->inode->i_mode;
+ sb->st_uid = fs->nf_opened_node->inode->i_uid;
+ sb->st_gid = fs->nf_opened_node->inode->i_gid;
+ return (0);
+}
+
+static int
+nandfs_readdir(struct open_file *f, struct dirent *d)
+{
+ struct nandfs *fs = f->f_fsdata;
+ struct nandfs_dir_entry *dirent;
+ uint8_t *buf;
+ size_t buf_size;
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)\n", f, d);
+
+ if (fs->nf_offset >= fs->nf_opened_node->inode->i_size) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) ENOENT\n",
+ f, d);
+ return (ENOENT);
+ }
+
+ if (nandfs_buf_read(fs, (void *)&buf, &buf_size)) {
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p)"
+ "buf_read failed\n", f, d);
+ return (EIO);
+ }
+
+ NANDFS_DEBUG("nandfs_readdir(file=%p, dirent=%p) moving forward\n",
+ f, d);
+
+ dirent = (struct nandfs_dir_entry *)buf;
+ fs->nf_offset += dirent->rec_len;
+ strncpy(d->d_name, dirent->name, dirent->name_len);
+ d->d_name[dirent->name_len] = '\0';
+ d->d_type = dirent->file_type;
+ return (0);
+}
+
+static int
+nandfs_buf_read(struct nandfs *fs, char **buf_p, size_t *size_p)
+{
+ nandfs_daddr_t blknr, blkoff;
+
+ blknr = fs->nf_offset / fs->nf_blocksize;
+ blkoff = fs->nf_offset % fs->nf_blocksize;
+
+ if (blknr != fs->nf_buf_blknr) {
+ if (fs->nf_buf == NULL)
+ fs->nf_buf = malloc(fs->nf_blocksize);
+
+ if (nandfs_read_inode(fs, fs->nf_opened_node, blknr, 1,
+ fs->nf_buf, 0))
+ return (EIO);
+
+ fs->nf_buf_blknr = blknr;
+ }
+
+ *buf_p = fs->nf_buf + blkoff;
+ *size_p = fs->nf_blocksize - blkoff;
+
+ NANDFS_DEBUG("nandfs_buf_read buf_p=%p size_p=%d\n", *buf_p, *size_p);
+
+ if (*size_p > fs->nf_opened_node->inode->i_size - fs->nf_offset)
+ *size_p = fs->nf_opened_node->inode->i_size - fs->nf_offset;
+
+ return (0);
+}
+
+static struct nandfs_node *
+nandfs_lookup_node(struct nandfs *fs, uint64_t ino)
+{
+ uint64_t blocknr;
+ int entrynr;
+ struct nandfs_inode *buffer;
+ struct nandfs_node *node;
+ struct nandfs_inode *inode;
+
+ NANDFS_DEBUG("nandfs_lookup_node ino=%lld\n", ino);
+
+ if (ino == 0) {
+ printf("nandfs_lookup_node: invalid inode requested\n");
+ return (NULL);
+ }
+
+ buffer = malloc(fs->nf_blocksize);
+ inode = malloc(sizeof(struct nandfs_inode));
+ node = malloc(sizeof(struct nandfs_node));
+
+ nandfs_mdt_trans(&fs->nf_ifile_mdt, ino, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_ifile, blocknr, 1, buffer, 0))
+ return (NULL);
+
+ memcpy(inode, &buffer[entrynr], sizeof(struct nandfs_inode));
+ node->inode = inode;
+ free(buffer);
+ return (node);
+}
+
+static struct nandfs_node *
+nandfs_lookup_path(struct nandfs *fs, const char *path)
+{
+ struct nandfs_node *node;
+ struct nandfs_dir_entry *dirent;
+ char *namebuf;
+ uint64_t i, j, done, counter, pinode, inode;
+ int nlinks = 0, len, link_len, nameidx;
+ uint8_t *buffer, *orig;
+ char *strp, *lpath;
+
+ buffer = malloc(fs->nf_blocksize);
+ orig = buffer;
+
+ namebuf = malloc(2 * MAXPATHLEN + 2);
+ strncpy(namebuf, path, MAXPATHLEN);
+ namebuf[MAXPATHLEN] = '\0';
+ done = nameidx = 0;
+ lpath = namebuf;
+
+ /* Get the root inode */
+ node = nandfs_lookup_node(fs, NANDFS_ROOT_INO);
+ inode = NANDFS_ROOT_INO;
+
+ while ((strp = strsep(&lpath, "/")) != NULL) {
+ if (*strp == '\0')
+ continue;
+ if ((node->inode->i_mode & IFMT) != IFDIR) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ len = strlen(strp);
+ NANDFS_DEBUG("%s: looking for %s\n", __func__, strp);
+ for (i = 0; i < node->inode->i_blocks; i++) {
+ if (nandfs_read_inode(fs, node, i, 1, orig, 0)) {
+ node = NULL;
+ goto out;
+ }
+
+ buffer = orig;
+ done = counter = 0;
+ while (1) {
+ dirent = (struct nandfs_dir_entry *)buffer;
+ NANDFS_DEBUG("%s: dirent.name = %s\n",
+ __func__, dirent->name);
+ NANDFS_DEBUG("%s: dirent.rec_len = %d\n",
+ __func__, dirent->rec_len);
+ NANDFS_DEBUG("%s: dirent.inode = %lld\n",
+ __func__, dirent->inode);
+ if (len == dirent->name_len &&
+ (strncmp(strp, dirent->name, len) == 0) &&
+ dirent->inode != 0) {
+ nandfs_free_node(node);
+ node = nandfs_lookup_node(fs,
+ dirent->inode);
+ pinode = inode;
+ inode = dirent->inode;
+ done = 1;
+ break;
+ }
+
+ counter += dirent->rec_len;
+ buffer += dirent->rec_len;
+
+ if (counter == fs->nf_blocksize)
+ break;
+ }
+
+ if (done)
+ break;
+ }
+
+ if (!done) {
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: %.*s has mode %o\n", __func__,
+ dirent->name_len, dirent->name, node->inode->i_mode);
+
+ if ((node->inode->i_mode & IFMT) == IFLNK) {
+ NANDFS_DEBUG("%s: %.*s is symlink\n",
+ __func__, dirent->name_len, dirent->name);
+ link_len = node->inode->i_size;
+
+ if (++nlinks > MAXSYMLINKS) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ if (nandfs_read_inode(fs, node, 0, 1, orig, 0)) {
+ nandfs_free_node(node);
+ node = NULL;
+ goto out;
+ }
+
+ NANDFS_DEBUG("%s: symlink is %.*s\n",
+ __func__, link_len, (char *)orig);
+
+ nameidx = (nameidx == 0) ? MAXPATHLEN + 1 : 0;
+ bcopy((char *)orig, namebuf + nameidx,
+ (unsigned)link_len);
+ if (lpath != NULL) {
+ namebuf[nameidx + link_len++] = '/';
+ strncpy(namebuf + nameidx + link_len, lpath,
+ MAXPATHLEN - link_len);
+ namebuf[nameidx + MAXPATHLEN] = '\0';
+ } else
+ namebuf[nameidx + link_len] = '\0';
+
+ NANDFS_DEBUG("%s: strp=%s, lpath=%s, namebuf0=%s, "
+ "namebuf1=%s, idx=%d\n", __func__, strp, lpath,
+ namebuf + 0, namebuf + MAXPATHLEN + 1, nameidx);
+
+ lpath = namebuf + nameidx;
+
+ nandfs_free_node(node);
+
+ /*
+ * If absolute pathname, restart at root. Otherwise
+ * continue with out parent inode.
+ */
+ inode = (orig[0] == '/') ? NANDFS_ROOT_INO : pinode;
+ node = nandfs_lookup_node(fs, inode);
+ }
+ }
+
+out:
+ free(namebuf);
+ free(orig);
+ return (node);
+}
+
+static int
+nandfs_read_inode(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_daddr_t blknr, u_int nblks, void *buf, int raw)
+{
+ int i;
+ uint64_t *pblks;
+ uint64_t *vblks;
+ int error;
+
+ pblks = malloc(nblks * sizeof(uint64_t));
+ vblks = malloc(nblks * sizeof(uint64_t));
+
+ NANDFS_DEBUG("nandfs_read_inode fs=%p node=%p blknr=%lld nblks=%d\n",
+ fs, node, blknr, nblks);
+ for (i = 0; i < nblks; i++) {
+ error = nandfs_bmap_lookup(fs, node, blknr + i, &vblks[i], raw);
+ if (error) {
+ free(pblks);
+ free(vblks);
+ return (error);
+ }
+ if (raw == 0)
+ pblks[i] = nandfs_vtop(fs, vblks[i]);
+ else
+ pblks[i] = vblks[i];
+ }
+
+ for (i = 0; i < nblks; i++) {
+ if (ioread(fs->nf_file, pblks[i] * fs->nf_blocksize, buf,
+ fs->nf_blocksize)) {
+ free(pblks);
+ free(vblks);
+ return (EIO);
+ }
+
+ buf += fs->nf_blocksize;
+ }
+
+ free(pblks);
+ free(vblks);
+ return (0);
+}
+
+static int
+nandfs_read_blk(struct nandfs *fs, nandfs_daddr_t blknr, void *buf, int phys)
+{
+ uint64_t pblknr;
+
+ pblknr = (phys ? blknr : nandfs_vtop(fs, blknr));
+
+ return (ioread(fs->nf_file, pblknr * fs->nf_blocksize, buf,
+ fs->nf_blocksize));
+}
+
+static int
+nandfs_get_checkpoint(struct nandfs *fs, uint64_t cpno,
+ struct nandfs_checkpoint *cp)
+{
+ uint64_t blocknr;
+ int blockoff, cp_per_block, dlen;
+ uint8_t *buf;
+
+ NANDFS_DEBUG("nandfs_get_checkpoint(fs=%p cpno=%lld)\n", fs, cpno);
+
+ buf = malloc(fs->nf_blocksize);
+
+ cpno += NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
+ dlen = fs->nf_fsdata->f_checkpoint_size;
+ cp_per_block = fs->nf_blocksize / dlen;
+ blocknr = cpno / cp_per_block;
+ blockoff = (cpno % cp_per_block) * dlen;
+
+ if (nandfs_read_inode(fs, &fs->nf_cpfile, blocknr, 1, buf, 0)) {
+ free(buf);
+ return (EINVAL);
+ }
+
+ memcpy(cp, buf + blockoff, sizeof(struct nandfs_checkpoint));
+ free(buf);
+
+ return (0);
+}
+
+static uint64_t *
+nandfs_get_map(struct nandfs *fs, struct nandfs_node *node, nandfs_daddr_t blknr,
+ int phys)
+{
+ struct bmap_buf *bmap;
+ uint64_t *map;
+
+ LIST_FOREACH(bmap, &node->bmap_bufs, list) {
+ if (bmap->blknr == blknr)
+ return (bmap->map);
+ }
+
+ map = malloc(fs->nf_blocksize);
+ if (nandfs_read_blk(fs, blknr, map, phys)) {
+ free(map);
+ return (NULL);
+ }
+
+ bmap = malloc(sizeof(struct bmap_buf));
+ bmap->blknr = blknr;
+ bmap->map = map;
+
+ LIST_INSERT_HEAD(&node->bmap_bufs, bmap, list);
+
+ NANDFS_DEBUG("%s:(node=%p, map=%p)\n", __func__, node, map);
+ return (map);
+}
+
+static int
+nandfs_bmap_lookup(struct nandfs *fs, struct nandfs_node *node,
+ nandfs_lbn_t lblknr, nandfs_daddr_t *vblknr, int phys)
+{
+ struct nandfs_inode *ino;
+ nandfs_daddr_t ind_block_num;
+ uint64_t *map, *indir;
+ uint64_t idx0, idx1, vblk, tmp;
+ int idx;
+ int level;
+
+ ino = node->inode;
+
+ if (lblknr < NDADDR) {
+ *vblknr = ino->i_db[lblknr];
+ return (0);
+ }
+
+ lblknr -= NDADDR;
+
+ /*
+ * nindir[0] = NINDIR
+ * nindir[1] = NINDIR**2
+ * nindir[2] = NINDIR**3
+ * etc
+ */
+ for (level = 0; level < NIADDR; level++) {
+ NANDFS_DEBUG("lblknr=%jx fs->nf_nindir[%d]=%d\n", lblknr, level, fs->nf_nindir[level]);
+ if (lblknr < fs->nf_nindir[level])
+ break;
+ lblknr -= fs->nf_nindir[level];
+ }
+
+ if (level == NIADDR) {
+ /* Block number too high */
+ NANDFS_DEBUG("lblknr %jx too high\n", lblknr);
+ return (EFBIG);
+ }
+
+ ind_block_num = ino->i_ib[level];
+
+ for (; level >= 0; level--) {
+ if (ind_block_num == 0) {
+ *vblknr = 0; /* missing */
+ return (0);
+ }
+
+ twiddle();
+ NANDFS_DEBUG("calling get_map with %jx\n", ind_block_num);
+ map = nandfs_get_map(fs, node, ind_block_num, phys);
+ if (map == NULL)
+ return (EIO);
+
+ if (level > 0) {
+ idx = lblknr / fs->nf_nindir[level - 1];
+ lblknr %= fs->nf_nindir[level - 1];
+ } else
+ idx = lblknr;
+
+ ind_block_num = ((nandfs_daddr_t *)map)[idx];
+ }
+
+ *vblknr = ind_block_num;
+
+ return (0);
+}
+
+static nandfs_daddr_t
+nandfs_vtop(struct nandfs *fs, nandfs_daddr_t vblocknr)
+{
+ nandfs_lbn_t blocknr;
+ nandfs_daddr_t pblocknr;
+ int entrynr;
+ struct nandfs_dat_entry *dat;
+
+ dat = malloc(fs->nf_blocksize);
+ nandfs_mdt_trans(&fs->nf_datfile_mdt, vblocknr, &blocknr, &entrynr);
+
+ if (nandfs_read_inode(fs, &fs->nf_datfile, blocknr, 1, dat, 1)) {
+ free(dat);
+ return (0);
+ }
+
+ NANDFS_DEBUG("nandfs_vtop entrynr=%d vblocknr=%lld pblocknr=%lld\n",
+ entrynr, vblocknr, dat[entrynr].de_blocknr);
+
+ pblocknr = dat[entrynr].de_blocknr;
+ free(dat);
+ return (pblocknr);
+}
+
+static void
+nandfs_calc_mdt_consts(int blocksize, struct nandfs_mdt *mdt, int entry_size)
+{
+
+ mdt->entries_per_group = blocksize * 8; /* bits in sector */
+ mdt->entries_per_block = blocksize / entry_size;
+ mdt->blocks_per_group =
+ (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
+ mdt->groups_per_desc_block =
+ blocksize / sizeof(struct nandfs_block_group_desc);
+ mdt->blocks_per_desc_block =
+ mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
+}
+
+static void
+nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
+ nandfs_daddr_t *blocknr, uint32_t *entry_in_block)
+{
+ nandfs_daddr_t blknr;
+ uint64_t group, group_offset, blocknr_in_group;
+ uint64_t desc_block, desc_offset;
+
+ /* Calculate our offset in the file */
+ group = index / mdt->entries_per_group;
+ group_offset = index % mdt->entries_per_group;
+ desc_block = group / mdt->groups_per_desc_block;
+ desc_offset = group % mdt->groups_per_desc_block;
+ blocknr_in_group = group_offset / mdt->entries_per_block;
+
+ /* To descgroup offset */
+ blknr = 1 + desc_block * mdt->blocks_per_desc_block;
+
+ /* To group offset */
+ blknr += desc_offset * mdt->blocks_per_group;
+
+ /* To actual file block */
+ blknr += 1 + blocknr_in_group;
+
+ *blocknr = blknr;
+ *entry_in_block = group_offset % mdt->entries_per_block;
+}
+
+static int
+ioread(struct open_file *f, off_t pos, void *buf, u_int length)
+{
+ void *buffer;
+ int err;
+ int bsize = ((struct nandfs *)f->f_fsdata)->nf_sectorsize;
+ u_int off, nsec;
+
+ off = pos % bsize;
+ pos /= bsize;
+ nsec = (length + (bsize - 1)) / bsize;
+
+ NANDFS_DEBUG("pos=%lld length=%d off=%d nsec=%d\n", pos, length,
+ off, nsec);
+
+ buffer = malloc(nsec * bsize);
+
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, pos,
+ nsec * bsize, buffer, NULL);
+
+ memcpy(buf, buffer + off, length);
+ free(buffer);
+
+ return (err);
+}
+
+static int
+nandfs_probe_sectorsize(struct open_file *f)
+{
+ void *buffer;
+ int i, err;
+
+ buffer = malloc(16 * 1024);
+
+ NANDFS_DEBUG("probing for sector size: ");
+
+ for (i = 512; i < (16 * 1024); i <<= 1) {
+ NANDFS_DEBUG("%d ", i);
+ err = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 0, i,
+ buffer, NULL);
+
+ if (err == 0) {
+ NANDFS_DEBUG("found");
+ free(buffer);
+ return (i);
+ }
+ }
+
+ free(buffer);
+ NANDFS_DEBUG("not found\n");
+ return (-1);
+}
diff --git a/lib/libstand/stand.h b/lib/libstand/stand.h
index db0490f..10bb829 100644
--- a/lib/libstand/stand.h
+++ b/lib/libstand/stand.h
@@ -118,6 +118,7 @@ extern struct fs_ops ufs_fsops;
extern struct fs_ops tftp_fsops;
extern struct fs_ops nfs_fsops;
extern struct fs_ops cd9660_fsops;
+extern struct fs_ops nandfs_fsops;
extern struct fs_ops gzipfs_fsops;
extern struct fs_ops bzipfs_fsops;
extern struct fs_ops dosfs_fsops;
diff --git a/sbin/Makefile b/sbin/Makefile
index 0cb421f..fcdb567 100644
--- a/sbin/Makefile
+++ b/sbin/Makefile
@@ -92,6 +92,11 @@ SUBDIR+= ipfw
SUBDIR+= natd
.endif
+.if ${MK_NAND} != "no"
+SUBDIR+= nandfs
+SUBDIR+= newfs_nandfs
+.endif
+
.if ${MK_PF} != "no"
SUBDIR+= pfctl
SUBDIR+= pflogd
diff --git a/sbin/nandfs/Makefile b/sbin/nandfs/Makefile
new file mode 100644
index 0000000..8474b09
--- /dev/null
+++ b/sbin/nandfs/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= nandfs
+SRCS= nandfs.c lssnap.c mksnap.c rmsnap.c
+MAN= nandfs.8
+
+DPADD= ${LIBNANDFS}
+LDADD= -lnandfs
+
+.include <bsd.prog.mk>
diff --git a/sbin/nandfs/lssnap.c b/sbin/nandfs/lssnap.c
new file mode 100644
index 0000000..fbee3cd
--- /dev/null
+++ b/sbin/nandfs/lssnap.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <time.h>
+
+#include <fs/nandfs/nandfs_fs.h>
+#include <libnandfs.h>
+
+#include "nandfs.h"
+
+#define NCPINFO 512
+
+static void
+lssnap_usage(void)
+{
+
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "\tlssnap node\n");
+}
+
+static void
+print_cpinfo(struct nandfs_cpinfo *cpinfo)
+{
+ struct tm tm;
+ time_t t;
+ char timebuf[128];
+
+ t = (time_t)cpinfo->nci_create;
+ localtime_r(&t, &tm);
+ strftime(timebuf, sizeof(timebuf), "%F %T", &tm);
+
+ printf("%20llu %s\n", (unsigned long long)cpinfo->nci_cno, timebuf);
+}
+
+int
+nandfs_lssnap(int argc, char **argv)
+{
+ struct nandfs_cpinfo *cpinfos;
+ struct nandfs fs;
+ uint64_t next;
+ int error, nsnap, i;
+
+ if (argc != 1) {
+ lssnap_usage();
+ return (EX_USAGE);
+ }
+
+ cpinfos = malloc(sizeof(*cpinfos) * NCPINFO);
+ if (cpinfos == NULL) {
+ fprintf(stderr, "cannot allocate memory\n");
+ return (-1);
+ }
+
+ nandfs_init(&fs, argv[0]);
+ error = nandfs_open(&fs);
+ if (error == -1) {
+ fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
+ goto out;
+ }
+
+ for (next = 1; next != 0; next = cpinfos[nsnap - 1].nci_next) {
+ nsnap = nandfs_get_snap(&fs, next, cpinfos, NCPINFO);
+ if (nsnap < 1)
+ break;
+
+ for (i = 0; i < nsnap; i++)
+ print_cpinfo(&cpinfos[i]);
+ }
+
+ if (nsnap == -1)
+ fprintf(stderr, "nandfs_get_snap: %s\n", nandfs_errmsg(&fs));
+
+out:
+ nandfs_close(&fs);
+ nandfs_destroy(&fs);
+ free(cpinfos);
+ return (error);
+}
diff --git a/sbin/nandfs/mksnap.c b/sbin/nandfs/mksnap.c
new file mode 100644
index 0000000..cd2d130
--- /dev/null
+++ b/sbin/nandfs/mksnap.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 <sys/types.h>
+
+#include <stdio.h>
+#include <sysexits.h>
+
+#include <fs/nandfs/nandfs_fs.h>
+#include <libnandfs.h>
+
+#include "nandfs.h"
+
+static void
+mksnap_usage(void)
+{
+
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "\tmksnap node\n");
+}
+
+int
+nandfs_mksnap(int argc, char **argv)
+{
+ struct nandfs fs;
+ uint64_t cpno;
+ int error;
+
+ if (argc != 1) {
+ mksnap_usage();
+ return (EX_USAGE);
+ }
+
+ nandfs_init(&fs, argv[0]);
+ error = nandfs_open(&fs);
+ if (error == -1) {
+ fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
+ goto out;
+ }
+
+ error = nandfs_make_snap(&fs, &cpno);
+ if (error == -1)
+ fprintf(stderr, "nandfs_make_snap: %s\n", nandfs_errmsg(&fs));
+ else
+ printf("%jd\n", cpno);
+
+out:
+ nandfs_close(&fs);
+ nandfs_destroy(&fs);
+ return (error);
+}
diff --git a/sbin/nandfs/nandfs.8 b/sbin/nandfs/nandfs.8
new file mode 100644
index 0000000..b1cfa05
--- /dev/null
+++ b/sbin/nandfs/nandfs.8
@@ -0,0 +1,75 @@
+.\"
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Semihalf under sponsorship
+.\" from the FreeBSD Foundation.
+.\"
+.\" 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 February 28, 2012
+.Dt NANDFS 8
+.Os
+.Sh NAME
+.Nm nandfs
+.Nd manage mounted NAND FS
+.Sh SYNOPSIS
+.Nm
+.Cm lssnap
+.Ar node
+.Nm
+.Cm mksnap
+.Ar node
+.Nm
+.Cm rmsnap
+.Ar snapshot node
+.Sh DESCRIPTION
+The
+.Nm
+utility allows to manage snapshots of a mounted NAND FS.
+.Sh EXAMPLES
+.Pp
+Create a snapshot of filesystem mounted on
+.Em /nand .
+.Bd -literal -offset 2n
+.Li # Ic nandfs mksnap /nand
+1
+.Ed
+.Pp
+List snapshots of filesystem mounted on
+.Em /nand .
+.Bd -literal -offset 2n
+.Li # Ic nandfs lssnap /nand
+1 2012-02-28 18:49:45 ss 138 2
+.Ed
+.Pp
+Remove snapshot 1 of filesystem mounted on
+.Em /nand .
+.Bd -literal -offset 2n
+.Li # Ic nandfs rmsnap 1 /nand
+.Ed
+.Sh AUTHORS
+This utility and manual page were written by
+.An Mateusz Guzik .
diff --git a/sbin/nandfs/nandfs.c b/sbin/nandfs/nandfs.c
new file mode 100644
index 0000000..f7ddda1
--- /dev/null
+++ b/sbin/nandfs/nandfs.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "nandfs.h"
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: nandfs [lssnap | mksnap | rmsnap <snap>] "
+ "node\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int error = 0;
+ char *cmd;
+
+ if (argc < 2)
+ usage();
+
+ cmd = argv[1];
+ argc -= 2;
+ argv += 2;
+
+ if (strcmp(cmd, "lssnap") == 0)
+ error = nandfs_lssnap(argc, argv);
+ else if (strcmp(cmd, "mksnap") == 0)
+ error = nandfs_mksnap(argc, argv);
+ else if (strcmp(cmd, "rmsnap") == 0)
+ error = nandfs_rmsnap(argc, argv);
+ else
+ usage();
+
+ return (error);
+}
diff --git a/sbin/nandfs/nandfs.h b/sbin/nandfs/nandfs.h
new file mode 100644
index 0000000..f544cc2
--- /dev/null
+++ b/sbin/nandfs/nandfs.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 NANDFS_H
+#define NANDFS_H
+
+int nandfs_lssnap(int, char **);
+int nandfs_mksnap(int, char **);
+int nandfs_rmsnap(int, char **);
+
+#endif /* !NANDFS_H */
diff --git a/sbin/nandfs/rmsnap.c b/sbin/nandfs/rmsnap.c
new file mode 100644
index 0000000..df2cbd5
--- /dev/null
+++ b/sbin/nandfs/rmsnap.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2012 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Semihalf under sponsorship
+ * from the FreeBSD Foundation.
+ *
+ * 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 <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sysexits.h>
+
+#include <fs/nandfs/nandfs_fs.h>
+#include <libnandfs.h>
+
+#include "nandfs.h"
+
+static void
+rmsnap_usage(void)
+{
+
+ fprintf(stderr, "usage:\n");
+ fprintf(stderr, "\trmsnap snap node\n");
+}
+
+int
+nandfs_rmsnap(int argc, char **argv)
+{
+ struct nandfs fs;
+ uint64_t cpno;
+ int error;
+
+ if (argc != 2) {
+ rmsnap_usage();
+ return (EX_USAGE);
+ }
+
+ cpno = strtoll(argv[0], (char **)NULL, 10);
+ if (cpno == 0) {
+ fprintf(stderr, "%s must be a number greater than 0\n",
+ argv[0]);
+ return (EX_USAGE);
+ }
+
+ nandfs_init(&fs, argv[1]);
+ error = nandfs_open(&fs);
+ if (error == -1) {
+ fprintf(stderr, "nandfs_open: %s\n", nandfs_errmsg(&fs));
+ goto out;
+ }
+
+ error = nandfs_delete_snap(&fs, cpno);
+ if (error == -1)
+ fprintf(stderr, "nandfs_delete_snap: %s\n", nandfs_errmsg(&fs));
+
+out:
+ nandfs_close(&fs);
+ nandfs_destroy(&fs);
+ return (error);
+}
diff --git a/sbin/newfs_nandfs/Makefile b/sbin/newfs_nandfs/Makefile
new file mode 100644
index 0000000..1d990ed
--- /dev/null
+++ b/sbin/newfs_nandfs/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= newfs_nandfs
+MAN= newfs_nandfs.8
+
+LDADD+= -lgeom
+DPADD+= ${LIBGEOM}
+
+.include <bsd.prog.mk>
diff --git a/sbin/newfs_nandfs/newfs_nandfs.8 b/sbin/newfs_nandfs/newfs_nandfs.8
new file mode 100644
index 0000000..401b5c2
--- /dev/null
+++ b/sbin/newfs_nandfs/newfs_nandfs.8
@@ -0,0 +1,77 @@
+.\"
+.\" 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 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 11, 2009
+.Dt NEWFS_NANDFS 8
+.Os
+.Sh NAME
+.Nm newfs_nandfs
+.Nd construct a new NAND FS file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl b Ar blocsize
+.Op Fl B Ar blocks-per-segment
+.Op Fl L Ar label
+.Op Fl m Ar reserved-segment-percent
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a NAND FS file system on device.
+.Pp
+The options are as follow:
+.Bl -tag -width indent
+.It Fl b Ar blocksize
+Size of block (1024 if not specified).
+.It Fl B Ar blocks_per_segment
+Number of blocks per segment (2048 if not specified).
+.It Fl L Ar label
+Volume label (up to 16 characters).
+.It Fl m Ar reserved_block_percent
+Percentage of reserved blocks (5 if not specified).
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success and 1 on error.
+.Sh EXAMPLES
+Create a file system, using default parameters, on
+.Pa /dev/ad0s1 :
+.Bd -literal -offset indent
+newfs_nandfs /dev/ad0s1
+.Ed
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr disklabel 8 ,
+.Xr fdisk 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+.An Grzegorz Bernacki
diff --git a/sbin/newfs_nandfs/newfs_nandfs.c b/sbin/newfs_nandfs/newfs_nandfs.c
new file mode 100644
index 0000000..c996674
--- /dev/null
+++ b/sbin/newfs_nandfs/newfs_nandfs.c
@@ -0,0 +1,1176 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/fdcio.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/endian.h>
+#include <sys/stddef.h>
+#include <sys/uuid.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <fs/nandfs/nandfs_fs.h>
+#include <dev/nand/nand_dev.h>
+
+#define DEBUG
+#undef DEBUG
+#ifdef DEBUG
+#define debug(fmt, args...) do { \
+ printf("nandfs:" fmt "\n", ##args); } while (0)
+#else
+#define debug(fmt, args...)
+#endif
+
+#define NANDFS_FIRST_BLOCK nandfs_first_block()
+#define NANDFS_FIRST_CNO 1
+#define NANDFS_BLOCK_BAD 1
+#define NANDFS_BLOCK_GOOD 0
+
+struct file_info {
+ uint64_t ino;
+ const char *name;
+ uint32_t mode;
+ uint64_t size;
+ uint8_t nblocks;
+ uint32_t *blocks;
+ struct nandfs_inode *inode;
+};
+
+struct file_info user_files[] =
+{
+ {NANDFS_ROOT_INO, NULL, S_IFDIR | 0755, 0, 1, NULL, NULL},
+};
+
+struct file_info ifile = {NANDFS_IFILE_INO, NULL, 0, 0, -1, NULL, NULL};
+struct file_info sufile = {NANDFS_SUFILE_INO, NULL, 0, 0, -1, NULL, NULL};
+struct file_info cpfile = {NANDFS_CPFILE_INO, NULL, 0, 0, -1, NULL, NULL};
+struct file_info datfile = {NANDFS_DAT_INO, NULL, 0, 0, -1, NULL, NULL};
+
+struct nandfs_block {
+ LIST_ENTRY(nandfs_block) block_link;
+ uint32_t number;
+ uint64_t offset;
+ void *data;
+};
+
+static LIST_HEAD(, nandfs_block) block_head = LIST_HEAD_INITIALIZER(&block_head);
+
+/* Storage geometry */
+static off_t mediasize;
+static ssize_t sectorsize;
+static uint64_t nsegments;
+static uint64_t erasesize;
+static uint64_t segsize;
+
+struct nandfs_fsdata fsdata;
+struct nandfs_super_block super_block;
+
+static int is_nand;
+
+/* Nandfs parameters */
+static size_t blocksize = NANDFS_DEF_BLOCKSIZE;
+static long blocks_per_segment;
+static long rsv_segment_percent = 5;
+static time_t nandfs_time;
+static uint32_t bad_segments_count = 0;
+static uint32_t *bad_segments = NULL;
+static uint8_t fsdata_blocks_state[NANDFS_NFSAREAS];
+
+u_char *volumelabel = NULL;
+
+struct nandfs_super_root *sr;
+
+uint32_t nuserfiles;
+uint32_t seg_segsum_size;
+uint32_t seg_nblocks;
+uint32_t seg_endblock;
+
+#define SIZE_TO_BLOCK(size) (((size) + (blocksize - 1)) / blocksize)
+
+static uint32_t
+nandfs_first_block(void)
+{
+ uint32_t i, first_free, start_bad_segments = 0;
+
+ for (i = 0; i < bad_segments_count; i++) {
+ if (i == bad_segments[i])
+ start_bad_segments++;
+ else
+ break;
+ }
+
+ first_free = SIZE_TO_BLOCK(NANDFS_DATA_OFFSET_BYTES(erasesize) +
+ (start_bad_segments * segsize));
+
+ if (first_free < (uint32_t)blocks_per_segment)
+ return (blocks_per_segment);
+ else
+ return (first_free);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: newfs_nandfs [ -options ] device\n"
+ "where the options are:\n"
+ "\t-b block-size\n"
+ "\t-B blocks-per-segment\n"
+ "\t-L volume label\n"
+ "\t-m reserved-segments-percentage\n");
+ exit(1);
+}
+
+static int
+nandfs_log2(unsigned n)
+{
+ unsigned count;
+
+ /*
+ * N.B. this function will return 0 if supplied 0.
+ */
+ for (count = 0; n/2; count++)
+ n /= 2;
+ return count;
+}
+
+/* from NetBSD's src/sys/net/if_ethersubr.c */
+static uint32_t
+crc32_le(uint32_t crc, const uint8_t *buf, size_t len)
+{
+ static const uint32_t crctab[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+ };
+ size_t i;
+
+ crc = crc ^ ~0U;
+
+ for (i = 0; i < len; i++) {
+ crc ^= buf[i];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ crc = (crc >> 4) ^ crctab[crc & 0xf];
+ }
+
+ return (crc ^ ~0U);
+}
+
+static void *
+get_block(uint32_t block_nr, uint64_t offset)
+{
+ struct nandfs_block *block, *new_block;
+
+ LIST_FOREACH(block, &block_head, block_link) {
+ if (block->number == block_nr)
+ return block->data;
+ }
+
+ debug("allocating block %x\n", block_nr);
+
+ new_block = malloc(sizeof(*block));
+ if (!new_block)
+ err(1, "cannot allocate block");
+
+ new_block->number = block_nr;
+ new_block->offset = offset;
+ new_block->data = malloc(blocksize);
+ if (!new_block->data)
+ err(1, "cannot allocate block data");
+
+ memset(new_block->data, 0, blocksize);
+
+ LIST_INSERT_HEAD(&block_head, new_block, block_link);
+
+ return (new_block->data);
+}
+
+static int
+nandfs_seg_usage_blk_offset(uint64_t seg, uint64_t *blk, uint64_t *offset)
+{
+ uint64_t off;
+ uint16_t seg_size;
+
+ seg_size = sizeof(struct nandfs_segment_usage);
+
+ off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
+ off += (seg * seg_size);
+
+ *blk = off / blocksize;
+ *offset = (off % blocksize) / seg_size;
+ return (0);
+}
+
+static uint32_t
+segment_size(void)
+{
+ u_int size;
+
+ size = sizeof(struct nandfs_segment_summary );
+ size += seg_nblocks * sizeof(struct nandfs_binfo_v);
+
+ if (size > blocksize)
+ err(1, "segsum info bigger that blocksize");
+
+ return (size);
+}
+
+
+static void
+prepare_blockgrouped_file(uint32_t block)
+{
+ struct nandfs_block_group_desc *desc;
+ uint32_t i, entries;
+
+ desc = (struct nandfs_block_group_desc *)get_block(block, 0);
+ entries = blocksize / sizeof(struct nandfs_block_group_desc);
+ for (i = 0; i < entries; i++)
+ desc[i].bg_nfrees = blocksize * 8;
+}
+
+static void
+alloc_blockgrouped_file(uint32_t block, uint32_t entry)
+{
+ struct nandfs_block_group_desc *desc;
+ uint32_t desc_nr;
+ uint32_t *bitmap;
+
+ desc = (struct nandfs_block_group_desc *)get_block(block, 0);
+ bitmap = (uint32_t *)get_block(block + 1, 1);
+
+ bitmap += (entry >> 5);
+ if (*bitmap & (1 << (entry % 32))) {
+ printf("nandfs: blockgrouped entry %d already allocated\n",
+ entry);
+ }
+ *bitmap |= (1 << (entry % 32));
+
+ desc_nr = entry / (blocksize * 8);
+ desc[desc_nr].bg_nfrees--;
+}
+
+
+static uint64_t
+count_su_blocks(void)
+{
+ uint64_t maxblk, blk, offset, i;
+
+ maxblk = blk = 0;
+
+ for (i = 0; i < bad_segments_count; i++) {
+ nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &offset);
+ debug("bad segment at block:%jx off: %jx", blk, offset);
+ if (blk > maxblk)
+ maxblk = blk;
+ }
+
+ debug("bad segment needs %#jx", blk);
+ if (blk >= NDADDR) {
+ printf("nandfs: file too big (%jd > %d)\n", blk, NDADDR);
+ exit(2);
+ }
+
+ sufile.size = (blk + 1) * blocksize;
+ return (blk + 1);
+}
+
+static void
+count_seg_blocks(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < nuserfiles; i++)
+ if (user_files[i].nblocks) {
+ seg_nblocks += user_files[i].nblocks;
+ user_files[i].blocks = malloc(user_files[i].nblocks * sizeof(uint32_t));
+ }
+
+ ifile.nblocks = 2 +
+ SIZE_TO_BLOCK(sizeof(struct nandfs_inode) * (NANDFS_USER_INO + 1));
+ ifile.blocks = malloc(ifile.nblocks * sizeof(uint32_t));
+ seg_nblocks += ifile.nblocks;
+
+ cpfile.nblocks =
+ SIZE_TO_BLOCK((NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1) *
+ sizeof(struct nandfs_checkpoint));
+ cpfile.blocks = malloc(cpfile.nblocks * sizeof(uint32_t));
+ seg_nblocks += cpfile.nblocks;
+
+ if (!bad_segments) {
+ sufile.nblocks =
+ SIZE_TO_BLOCK((NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET + 1) *
+ sizeof(struct nandfs_segment_usage));
+ } else {
+ debug("bad blocks found: extra space for sufile");
+ sufile.nblocks = count_su_blocks();
+ }
+
+ sufile.blocks = malloc(sufile.nblocks * sizeof(uint32_t));
+ seg_nblocks += sufile.nblocks;
+
+ datfile.nblocks = 2 +
+ SIZE_TO_BLOCK((seg_nblocks) * sizeof(struct nandfs_dat_entry));
+ datfile.blocks = malloc(datfile.nblocks * sizeof(uint32_t));
+ seg_nblocks += datfile.nblocks;
+}
+
+static void
+assign_file_blocks(uint64_t start_block)
+{
+ uint32_t i, j;
+
+ for (i = 0; i < nuserfiles; i++)
+ for (j = 0; j < user_files[i].nblocks; j++) {
+ debug("user file %d at block %d at %#jx",
+ i, j, (uintmax_t)start_block);
+ user_files[i].blocks[j] = start_block++;
+ }
+
+ for (j = 0; j < ifile.nblocks; j++) {
+ debug("ifile block %d at %#jx", j, (uintmax_t)start_block);
+ ifile.blocks[j] = start_block++;
+ }
+
+ for (j = 0; j < cpfile.nblocks; j++) {
+ debug("cpfile block %d at %#jx", j, (uintmax_t)start_block);
+ cpfile.blocks[j] = start_block++;
+ }
+
+ for (j = 0; j < sufile.nblocks; j++) {
+ debug("sufile block %d at %#jx", j, (uintmax_t)start_block);
+ sufile.blocks[j] = start_block++;
+ }
+
+ for (j = 0; j < datfile.nblocks; j++) {
+ debug("datfile block %d at %#jx", j, (uintmax_t)start_block);
+ datfile.blocks[j] = start_block++;
+ }
+
+ /* add one for superroot */
+ debug("sr at block %#jx", (uintmax_t)start_block);
+ sr = (struct nandfs_super_root *)get_block(start_block++, 0);
+ seg_endblock = start_block;
+}
+
+static void
+save_datfile(void)
+{
+
+ prepare_blockgrouped_file(datfile.blocks[0]);
+}
+
+static uint64_t
+update_datfile(uint64_t block)
+{
+ struct nandfs_dat_entry *dat;
+ static uint64_t vblock = 0;
+ uint64_t allocated, i, off;
+
+ if (vblock == 0) {
+ alloc_blockgrouped_file(datfile.blocks[0], vblock);
+ vblock++;
+ }
+ allocated = vblock;
+ i = vblock / (blocksize / sizeof(*dat));
+ off = vblock % (blocksize / sizeof(*dat));
+ vblock++;
+
+ dat = (struct nandfs_dat_entry *)get_block(datfile.blocks[2 + i], 2 + i);
+
+ alloc_blockgrouped_file(datfile.blocks[0], allocated);
+ dat[off].de_blocknr = block;
+ dat[off].de_start = NANDFS_FIRST_CNO;
+ dat[off].de_end = UINTMAX_MAX;
+
+ return (allocated);
+}
+
+static union nandfs_binfo *
+update_block_info(union nandfs_binfo *binfo, struct file_info *file)
+{
+ nandfs_daddr_t vblock;
+ uint32_t i;
+
+ for (i = 0; i < file->nblocks; i++) {
+ debug("%s: blk %x", __func__, i);
+ if (file->ino != NANDFS_DAT_INO) {
+ vblock = update_datfile(file->blocks[i]);
+ binfo->bi_v.bi_vblocknr = vblock;
+ binfo->bi_v.bi_blkoff = i;
+ binfo->bi_v.bi_ino = file->ino;
+ file->inode->i_db[i] = vblock;
+ } else {
+ binfo->bi_dat.bi_blkoff = i;
+ binfo->bi_dat.bi_ino = file->ino;
+ file->inode->i_db[i] = datfile.blocks[i];
+ }
+ binfo++;
+ }
+
+ return (binfo);
+}
+
+static void
+save_segsum(struct nandfs_segment_summary *ss)
+{
+ union nandfs_binfo *binfo;
+ struct nandfs_block *block;
+ uint32_t sum_bytes, i;
+ uint8_t crc_data, crc_skip;
+
+ sum_bytes = segment_size();
+ ss->ss_magic = NANDFS_SEGSUM_MAGIC;
+ ss->ss_bytes = sizeof(struct nandfs_segment_summary);
+ ss->ss_flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND | NANDFS_SS_SR;
+ ss->ss_seq = 1;
+ ss->ss_create = nandfs_time;
+
+ ss->ss_next = nandfs_first_block() + blocks_per_segment;
+ /* nblocks = segment blocks + segsum block + superroot */
+ ss->ss_nblocks = seg_nblocks + 2;
+ ss->ss_nbinfos = seg_nblocks;
+ ss->ss_sumbytes = sum_bytes;
+
+ crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum);
+ ss->ss_sumsum = crc32_le(0, (uint8_t *)ss + crc_skip,
+ sum_bytes - crc_skip);
+ crc_data = 0;
+
+ binfo = (union nandfs_binfo *)(ss + 1);
+ for (i = 0; i < nuserfiles; i++) {
+ if (user_files[i].nblocks)
+ binfo = update_block_info(binfo, &user_files[i]);
+ }
+
+ binfo = update_block_info(binfo, &ifile);
+ binfo = update_block_info(binfo, &cpfile);
+ binfo = update_block_info(binfo, &sufile);
+ update_block_info(binfo, &datfile);
+
+ /* save superroot crc */
+ crc_skip = sizeof(sr->sr_sum);
+ sr->sr_sum = crc32_le(0, (uint8_t *)sr + crc_skip,
+ NANDFS_SR_BYTES - crc_skip);
+
+ /* segment checksup */
+ crc_skip = sizeof(ss->ss_datasum);
+ LIST_FOREACH(block, &block_head, block_link) {
+ if (block->number < NANDFS_FIRST_BLOCK)
+ continue;
+ if (block->number == NANDFS_FIRST_BLOCK)
+ crc_data = crc32_le(0,
+ (uint8_t *)block->data + crc_skip,
+ blocksize - crc_skip);
+ else
+ crc_data = crc32_le(crc_data, (uint8_t *)block->data,
+ blocksize);
+ }
+ ss->ss_datasum = crc_data;
+}
+
+static void
+create_fsdata(void)
+{
+
+ memset(&fsdata, 0, sizeof(struct nandfs_fsdata));
+
+ fsdata.f_magic = NANDFS_FSDATA_MAGIC;
+ fsdata.f_nsegments = nsegments;
+ fsdata.f_erasesize = erasesize;
+ fsdata.f_first_data_block = NANDFS_FIRST_BLOCK;
+ fsdata.f_blocks_per_segment = blocks_per_segment;
+ fsdata.f_r_segments_percentage = rsv_segment_percent;
+ fsdata.f_rev_level = NANDFS_CURRENT_REV;
+ fsdata.f_sbbytes = NANDFS_SB_BYTES;
+ fsdata.f_bytes = NANDFS_FSDATA_CRC_BYTES;
+ fsdata.f_ctime = nandfs_time;
+ fsdata.f_log_block_size = nandfs_log2(blocksize) - 10;
+ fsdata.f_errors = 1;
+ fsdata.f_inode_size = sizeof(struct nandfs_inode);
+ fsdata.f_dat_entry_size = sizeof(struct nandfs_dat_entry);
+ fsdata.f_checkpoint_size = sizeof(struct nandfs_checkpoint);
+ fsdata.f_segment_usage_size = sizeof(struct nandfs_segment_usage);
+
+ uuidgen(&fsdata.f_uuid, 1);
+
+ if (volumelabel)
+ memcpy(fsdata.f_volume_name, volumelabel, 16);
+
+ fsdata.f_sum = crc32_le(0, (const uint8_t *)&fsdata,
+ NANDFS_FSDATA_CRC_BYTES);
+}
+
+static void
+save_fsdata(void *data)
+{
+
+ memcpy(data, &fsdata, sizeof(fsdata));
+}
+
+static void
+create_super_block(void)
+{
+
+ memset(&super_block, 0, sizeof(struct nandfs_super_block));
+
+ super_block.s_magic = NANDFS_SUPER_MAGIC;
+ super_block.s_last_cno = NANDFS_FIRST_CNO;
+ super_block.s_last_pseg = NANDFS_FIRST_BLOCK;
+ super_block.s_last_seq = 1;
+ super_block.s_free_blocks_count =
+ (nsegments - bad_segments_count) * blocks_per_segment;
+ super_block.s_mtime = 0;
+ super_block.s_wtime = nandfs_time;
+ super_block.s_state = NANDFS_VALID_FS;
+
+ super_block.s_sum = crc32_le(0, (const uint8_t *)&super_block,
+ NANDFS_SB_BYTES);
+}
+
+static void
+save_super_block(void *data)
+{
+
+ memcpy(data, &super_block, sizeof(super_block));
+}
+
+static void
+save_super_root(void)
+{
+
+ sr->sr_bytes = NANDFS_SR_BYTES;
+ sr->sr_flags = 0;
+ sr->sr_nongc_ctime = nandfs_time;
+ datfile.inode = &sr->sr_dat;
+ cpfile.inode = &sr->sr_cpfile;
+ sufile.inode = &sr->sr_sufile;
+}
+
+static struct nandfs_dir_entry *
+add_de(void *block, struct nandfs_dir_entry *de, uint64_t ino,
+ const char *name, uint8_t type)
+{
+ uint16_t reclen;
+
+ /* modify last de */
+ de->rec_len = NANDFS_DIR_REC_LEN(de->name_len);
+ de = (void *)((uint8_t *)de + de->rec_len);
+
+ reclen = blocksize - ((uintptr_t)de - (uintptr_t)block);
+ if (reclen < NANDFS_DIR_REC_LEN(strlen(name))) {
+ printf("nandfs: too many dir entries for one block\n");
+ return (NULL);
+ }
+
+ de->inode = ino;
+ de->rec_len = reclen;
+ de->name_len = strlen(name);
+ de->file_type = type;
+ memset(de->name, 0,
+ (strlen(name) + NANDFS_DIR_PAD - 1) & ~NANDFS_DIR_ROUND);
+ memcpy(de->name, name, strlen(name));
+
+ return (de);
+}
+
+static struct nandfs_dir_entry *
+make_dir(void *block, uint64_t ino, uint64_t parent_ino)
+{
+ struct nandfs_dir_entry *de = (struct nandfs_dir_entry *)block;
+
+ /* create '..' entry */
+ de->inode = parent_ino;
+ de->rec_len = NANDFS_DIR_REC_LEN(2);
+ de->name_len = 2;
+ de->file_type = DT_DIR;
+ memset(de->name, 0, NANDFS_DIR_NAME_LEN(2));
+ memcpy(de->name, "..", 2);
+
+ /* create '.' entry */
+ de = (void *)((uint8_t *)block + NANDFS_DIR_REC_LEN(2));
+ de->inode = ino;
+ de->rec_len = blocksize - NANDFS_DIR_REC_LEN(2);
+ de->name_len = 1;
+ de->file_type = DT_DIR;
+ memset(de->name, 0, NANDFS_DIR_NAME_LEN(1));
+ memcpy(de->name, ".", 1);
+
+ return (de);
+}
+
+static void
+save_root_dir(void)
+{
+ struct file_info *root = &user_files[0];
+ struct nandfs_dir_entry *de;
+ uint32_t i;
+ void *block;
+
+ block = get_block(root->blocks[0], 0);
+
+ de = make_dir(block, root->ino, root->ino);
+ for (i = 1; i < nuserfiles; i++)
+ de = add_de(block, de, user_files[i].ino, user_files[i].name,
+ IFTODT(user_files[i].mode));
+
+ root->size = ((uintptr_t)de - (uintptr_t)block) +
+ NANDFS_DIR_REC_LEN(de->name_len);
+}
+
+static void
+save_sufile(void)
+{
+ struct nandfs_sufile_header *header;
+ struct nandfs_segment_usage *su;
+ uint64_t blk, i, off;
+ void *block;
+ int start;
+
+ /*
+ * At the beginning just zero-out everything
+ */
+ for (i = 0; i < sufile.nblocks; i++)
+ get_block(sufile.blocks[i], 0);
+
+ start = 0;
+
+ block = get_block(sufile.blocks[start], 0);
+ header = (struct nandfs_sufile_header *)block;
+ header->sh_ncleansegs = nsegments - bad_segments_count - 1;
+ header->sh_ndirtysegs = 1;
+ header->sh_last_alloc = 1;
+
+ su = (struct nandfs_segment_usage *)header;
+ off = NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET;
+ /* Allocate data segment */
+ su[off].su_lastmod = nandfs_time;
+ /* nblocks = segment blocks + segsum block + superroot */
+ su[off].su_nblocks = seg_nblocks + 2;
+ su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
+ off++;
+ /* Allocate next segment */
+ su[off].su_lastmod = nandfs_time;
+ su[off].su_nblocks = 0;
+ su[off].su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
+ for (i = 0; i < bad_segments_count; i++) {
+ nandfs_seg_usage_blk_offset(bad_segments[i], &blk, &off);
+ debug("storing bad_segments[%jd]=%x at %jx off %jx\n", i,
+ bad_segments[i], blk, off);
+ block = get_block(sufile.blocks[blk],
+ off * sizeof(struct nandfs_segment_usage *));
+ su = (struct nandfs_segment_usage *)block;
+ su[off].su_lastmod = nandfs_time;
+ su[off].su_nblocks = 0;
+ su[off].su_flags = NANDFS_SEGMENT_USAGE_ERROR;
+ }
+}
+
+static void
+save_cpfile(void)
+{
+ struct nandfs_cpfile_header *header;
+ struct nandfs_checkpoint *cp, *initial_cp;
+ int i, entries = blocksize / sizeof(struct nandfs_checkpoint);
+ uint64_t cno;
+
+ header = (struct nandfs_cpfile_header *)get_block(cpfile.blocks[0], 0);
+ header->ch_ncheckpoints = 1;
+ header->ch_nsnapshots = 0;
+
+ cp = (struct nandfs_checkpoint *)header;
+
+ /* fill first checkpoint data*/
+ initial_cp = &cp[NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET];
+ initial_cp->cp_flags = 0;
+ initial_cp->cp_checkpoints_count = 0;
+ initial_cp->cp_cno = NANDFS_FIRST_CNO;
+ initial_cp->cp_create = nandfs_time;
+ initial_cp->cp_nblk_inc = seg_endblock - 1;
+ initial_cp->cp_blocks_count = seg_nblocks;
+ memset(&initial_cp->cp_snapshot_list, 0,
+ sizeof(struct nandfs_snapshot_list));
+
+ ifile.inode = &initial_cp->cp_ifile_inode;
+
+ /* mark rest of cp as invalid */
+ cno = NANDFS_FIRST_CNO + 1;
+ i = NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET + 1;
+ for (; i < entries; i++) {
+ cp[i].cp_cno = cno++;
+ cp[i].cp_flags = NANDFS_CHECKPOINT_INVALID;
+ }
+}
+
+static void
+init_inode(struct nandfs_inode *inode, struct file_info *file)
+{
+
+ inode->i_blocks = file->nblocks;
+ inode->i_ctime = nandfs_time;
+ inode->i_mtime = nandfs_time;
+ inode->i_mode = file->mode & 0xffff;
+ inode->i_links_count = 1;
+
+ if (file->size > 0)
+ inode->i_size = file->size;
+ else
+ inode->i_size = 0;
+
+ if (file->ino == NANDFS_USER_INO)
+ inode->i_flags = SF_NOUNLINK|UF_NOUNLINK;
+ else
+ inode->i_flags = 0;
+}
+
+static void
+save_ifile(void)
+{
+ struct nandfs_inode *inode;
+ struct file_info *file;
+ uint64_t ino, blk, off;
+ uint32_t i;
+
+ prepare_blockgrouped_file(ifile.blocks[0]);
+ for (i = 0; i <= NANDFS_USER_INO; i++)
+ alloc_blockgrouped_file(ifile.blocks[0], i);
+
+ for (i = 0; i < nuserfiles; i++) {
+ file = &user_files[i];
+ ino = file->ino;
+ blk = ino / (blocksize / sizeof(*inode));
+ off = ino % (blocksize / sizeof(*inode));
+ inode =
+ (struct nandfs_inode *)get_block(ifile.blocks[2 + blk], 2 + blk);
+ file->inode = &inode[off];
+ init_inode(file->inode, file);
+ }
+
+ init_inode(ifile.inode, &ifile);
+ init_inode(cpfile.inode, &cpfile);
+ init_inode(sufile.inode, &sufile);
+ init_inode(datfile.inode, &datfile);
+}
+
+static int
+create_fs(void)
+{
+ uint64_t start_block;
+ uint32_t segsum_size;
+ char *data;
+ int i;
+
+ nuserfiles = (sizeof(user_files) / sizeof(user_files[0]));
+
+ /* Count and assign blocks */
+ count_seg_blocks();
+ segsum_size = segment_size();
+ start_block = NANDFS_FIRST_BLOCK + SIZE_TO_BLOCK(segsum_size);
+ assign_file_blocks(start_block);
+
+ /* Create super root structure */
+ save_super_root();
+
+ /* Create root directory */
+ save_root_dir();
+
+ /* Fill in file contents */
+ save_sufile();
+ save_cpfile();
+ save_ifile();
+ save_datfile();
+
+ /* Save fsdata and superblocks */
+ create_fsdata();
+ create_super_block();
+
+ for (i = 0; i < NANDFS_NFSAREAS; i++) {
+ if (fsdata_blocks_state[i] != NANDFS_BLOCK_GOOD)
+ continue;
+
+ data = get_block((i * erasesize)/blocksize, 0);
+ save_fsdata(data);
+
+ data = get_block((i * erasesize + NANDFS_SBLOCK_OFFSET_BYTES) /
+ blocksize, 0);
+ if (blocksize > NANDFS_SBLOCK_OFFSET_BYTES)
+ data += NANDFS_SBLOCK_OFFSET_BYTES;
+ save_super_block(data);
+ memset(data + sizeof(struct nandfs_super_block), 0xff,
+ (blocksize - sizeof(struct nandfs_super_block) -
+ NANDFS_SBLOCK_OFFSET_BYTES));
+ }
+
+ /* Save segment summary and CRCs */
+ save_segsum(get_block(NANDFS_FIRST_BLOCK, 0));
+
+ return (0);
+}
+
+static void
+write_fs(int fda)
+{
+ struct nandfs_block *block;
+ char *data;
+ u_int ret;
+
+ /* Overwrite next block with ff if not nand device */
+ if (!is_nand) {
+ data = get_block(seg_endblock, 0);
+ memset(data, 0xff, blocksize);
+ }
+
+ LIST_FOREACH(block, &block_head, block_link) {
+ lseek(fda, block->number * blocksize, SEEK_SET);
+ ret = write(fda, block->data, blocksize);
+ if (ret != blocksize)
+ err(1, "cannot write filesystem data");
+ }
+}
+
+static void
+check_parameters(void)
+{
+ int i;
+
+ /* check blocksize */
+ if ((blocksize < NANDFS_MIN_BLOCKSIZE) || (blocksize > MAXBSIZE) ||
+ ((blocksize - 1) & blocksize)) {
+ errx(1, "Bad blocksize (%zu). Must be in range [%u-%u] "
+ "and a power of two.", blocksize, NANDFS_MIN_BLOCKSIZE,
+ MAXBSIZE);
+ }
+
+ /* check blocks per segments */
+ if ((blocks_per_segment < NANDFS_SEG_MIN_BLOCKS) ||
+ ((blocksize - 1) & blocksize))
+ errx(1, "Bad blocks per segment (%lu). Must be greater than "
+ "%u and a power of two.", blocks_per_segment,
+ NANDFS_SEG_MIN_BLOCKS);
+
+ /* check reserved segment percentage */
+ if ((rsv_segment_percent < 1) && (rsv_segment_percent > 99))
+ errx(1, "Bad reserved segment percentage. "
+ "Must in range 1..99.");
+
+ /* check volume label */
+ i = 0;
+ if (volumelabel) {
+ while (isalnum(volumelabel[++i]))
+ ;
+
+ if (volumelabel[i] != '\0') {
+ errx(1, "bad volume label. "
+ "Valid characters are alphanumerics.");
+ }
+
+ if (strlen(volumelabel) >= 16)
+ errx(1, "Bad volume label. Length is longer than %d.",
+ 16);
+ }
+
+ nandfs_time = time(NULL);
+}
+
+static void
+print_parameters(void)
+{
+
+ printf("filesystem parameters:\n");
+ printf("blocksize: %#zx sectorsize: %#zx\n", blocksize, sectorsize);
+ printf("erasesize: %#jx mediasize: %#jx\n", erasesize, mediasize);
+ printf("segment size: %#jx blocks per segment: %#x\n", segsize,
+ (uint32_t)blocks_per_segment);
+}
+
+/*
+ * Exit with error if file system is mounted.
+ */
+static void
+check_mounted(const char *fname, mode_t mode)
+{
+ struct statfs *mp;
+ const char *s1, *s2;
+ size_t len;
+ int n, r;
+
+ if (!(n = getmntinfo(&mp, MNT_NOWAIT)))
+ err(1, "getmntinfo");
+
+ len = strlen(_PATH_DEV);
+ s1 = fname;
+ if (!strncmp(s1, _PATH_DEV, len))
+ s1 += len;
+
+ r = S_ISCHR(mode) && s1 != fname && *s1 == 'r';
+
+ for (; n--; mp++) {
+ s2 = mp->f_mntfromname;
+
+ if (!strncmp(s2, _PATH_DEV, len))
+ s2 += len;
+ if ((r && s2 != mp->f_mntfromname && !strcmp(s1 + 1, s2)) ||
+ !strcmp(s1, s2))
+ errx(1, "%s is mounted on %s", fname, mp->f_mntonname);
+ }
+}
+
+static void
+calculate_geometry(int fd)
+{
+ struct chip_param_io chip_params;
+ char ident[DISK_IDENT_SIZE];
+ char medianame[MAXPATHLEN];
+
+ /* Check storage type */
+ g_get_ident(fd, ident, DISK_IDENT_SIZE);
+ g_get_name(ident, medianame, MAXPATHLEN);
+ debug("device name: %s", medianame);
+
+ is_nand = (strstr(medianame, "gnand") != NULL);
+ debug("is_nand = %d", is_nand);
+
+ sectorsize = g_sectorsize(fd);
+ debug("sectorsize: %#zx", sectorsize);
+
+ /* Get storage size */
+ mediasize = g_mediasize(fd);
+ debug("mediasize: %#jx", mediasize);
+
+ /* Get storage erase unit size */
+ if (!is_nand)
+ erasesize = NANDFS_DEF_ERASESIZE;
+ else if (ioctl(fd, NAND_IO_GET_CHIP_PARAM, &chip_params) == -1)
+ errx(1, "Cannot ioctl(NAND_IO_GET_CHIP_PARAM)");
+ else
+ erasesize = chip_params.page_size * chip_params.pages_per_block;
+
+ debug("erasesize: %#jx", (uintmax_t)erasesize);
+
+ if (blocks_per_segment == 0) {
+ if (erasesize >= NANDFS_MIN_SEGSIZE)
+ blocks_per_segment = erasesize / blocksize;
+ else
+ blocks_per_segment = NANDFS_MIN_SEGSIZE / blocksize;
+ }
+
+ /* Calculate number of segments */
+ segsize = blocksize * blocks_per_segment;
+ nsegments = ((mediasize - NANDFS_NFSAREAS * erasesize) / segsize) - 2;
+ debug("segsize: %#jx", segsize);
+ debug("nsegments: %#jx", nsegments);
+}
+
+static void
+erase_device(int fd)
+{
+ int rest, failed;
+ uint64_t i, nblocks;
+ off_t offset;
+
+ failed = 0;
+ for (i = 0; i < NANDFS_NFSAREAS; i++) {
+ debug("Deleting %jx\n", i * erasesize);
+ if (g_delete(fd, i * erasesize, erasesize)) {
+ printf("cannot delete %jx\n", i * erasesize);
+ fsdata_blocks_state[i] = NANDFS_BLOCK_BAD;
+ failed++;
+ } else
+ fsdata_blocks_state[i] = NANDFS_BLOCK_GOOD;
+ }
+
+ if (failed == NANDFS_NFSAREAS) {
+ printf("%d first blocks not usable. Unable to create "
+ "filesystem.\n", failed);
+ exit(1);
+ }
+
+ for (i = 0; i < nsegments; i++) {
+ offset = NANDFS_NFSAREAS * erasesize + i * segsize;
+ if (g_delete(fd, offset, segsize)) {
+ printf("cannot delete segment %jx (offset %jd)\n",
+ i, offset);
+ bad_segments_count++;
+ bad_segments = realloc(bad_segments,
+ bad_segments_count * sizeof(uint32_t));
+ bad_segments[bad_segments_count - 1] = i;
+ }
+ }
+
+ if (bad_segments_count == nsegments) {
+ printf("no valid segments\n");
+ exit(1);
+ }
+
+ /* Delete remaining blocks at the end of device */
+ rest = mediasize % segsize;
+ nblocks = rest / erasesize;
+ for (i = 0; i < nblocks; i++) {
+ offset = (segsize * nsegments) + (i * erasesize);
+ if (g_delete(fd, offset, erasesize)) {
+ printf("cannot delete space after last segment "
+ "- probably a bad block\n");
+ }
+ }
+}
+
+static void
+erase_initial(int fd)
+{
+ char buf[512];
+ u_int i;
+
+ memset(buf, 0xff, sizeof(buf));
+
+ lseek(fd, 0, SEEK_SET);
+ for (i = 0; i < NANDFS_NFSAREAS * erasesize; i += sizeof(buf))
+ write(fd, buf, sizeof(buf));
+}
+
+static void
+create_nandfs(int fd)
+{
+
+ create_fs();
+
+ write_fs(fd);
+}
+
+static void
+print_summary(void)
+{
+
+ printf("filesystem created succesfully\n");
+ printf("total segments: %#jx valid segments: %#jx\n", nsegments,
+ nsegments - bad_segments_count);
+ printf("total space: %ju MB free: %ju MB\n",
+ (nsegments *
+ blocks_per_segment * blocksize) / (1024 * 1024),
+ ((nsegments - bad_segments_count) *
+ blocks_per_segment * blocksize) / (1024 * 1024));
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct stat sb;
+ char buf[MAXPATHLEN];
+ const char opts[] = "b:B:L:m:";
+ const char *fname;
+ int ch, fd;
+
+ while ((ch = getopt(argc, argv, opts)) != -1) {
+ switch (ch) {
+ case 'b':
+ blocksize = strtol(optarg, (char **)NULL, 10);
+ if (blocksize == 0)
+ usage();
+ break;
+ case 'B':
+ blocks_per_segment = strtol(optarg, (char **)NULL, 10);
+ if (blocks_per_segment == 0)
+ usage();
+ break;
+ case 'L':
+ volumelabel = optarg;
+ break;
+ case 'm':
+ rsv_segment_percent = strtol(optarg, (char **)NULL, 10);
+ if (rsv_segment_percent == 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ if (argc < 1 || argc > 2)
+ usage();
+
+ /* construct proper device path */
+ fname = *argv++;
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ if (!(fname = strdup(buf)))
+ err(1, NULL);
+ }
+
+ fd = g_open(fname, 1);
+ if (fd == -1)
+ err(1, "Cannot open %s", fname);
+
+ if (fstat(fd, &sb) == -1)
+ err(1, "Cannot stat %s", fname);
+ if (!S_ISCHR(sb.st_mode))
+ warnx("%s is not a character device", fname);
+
+ check_mounted(fname, sb.st_mode);
+
+ calculate_geometry(fd);
+
+ check_parameters();
+
+ print_parameters();
+
+ if (is_nand)
+ erase_device(fd);
+ else
+ erase_initial(fd);
+
+ create_nandfs(fd);
+
+ print_summary();
+
+ g_close(fd);
+
+ return (0);
+}
+
+
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index dffe304..2c23a9c 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -250,6 +250,8 @@ MAN= aac.4 \
mwlfw.4 \
mxge.4 \
my.4 \
+ nand.4 \
+ nandsim.4 \
natm.4 \
natmip.4 \
ncr.4 \
diff --git a/share/man/man4/nand.4 b/share/man/man4/nand.4
new file mode 100644
index 0000000..d23e292
--- /dev/null
+++ b/share/man/man4/nand.4
@@ -0,0 +1,140 @@
+.\"
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This documentation was written by Semihalf under sponsorship from
+.\" the FreeBSD Foundation.
+.\"
+.\" 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 March 8, 2012
+.Dt NAND 4
+.Os
+.Sh NAME
+.Nm nand
+.Nd NAND Flash framework
+.Sh SYNOPSIS
+.Cd "device nand"
+.Sh DESCRIPTION
+The
+.Fx
+.Nm
+framework consists of a set of interfaces that aim to provide an extensible,
+object oriented environement for NAND controllers and NAND Flash memory chips
+from various hardware vendors, and to allow for uniform and flexible
+management of the NAND devices. It comprises of the following major
+components:
+.Bl -bullet
+.It
+NAND Flash controller (NFC) interface.
+.Pp
+Defines methods which allow to send commands as well as send/receive data
+between the controller and a NAND chip. Back-end drivers for specific NAND
+controllers plug into this interface and implement low-level routines for a
+given NAND controller.
+.Pp
+This layer implements basic functionality of a NAND Flash controller. It
+allows to send command and address to chip, drive CS (chip select line), as
+well as read/write to the selected NAND chip. This layer is independent of
+NAND chip devices actually connected to the controller.
+.It
+NAND chip interface.
+.Pp
+Provides basic operations like read page, program page, erase block. Currently
+three generic classes of drivers are available, which provide support for the
+following chips:
+.Bl -bullet
+.It
+large page
+.It
+small page
+.It
+ONFI-compliant
+.El
+.Pp
+This layer implements basic operations to be performed on a NAND chip, like
+read, program, erase, get status etc. Since these operations use specific
+commands (depending on the vendor), each chip has potentially its own
+implementation of the commands set.
+.Pp
+The framework is extensible so it is also possible to create a custom command
+set for a non standard chip support.
+.It
+NANDbus.
+.Pp
+This layer is responsible for enumerating NAND chips in the system and
+establishing the hierarchy between chips and their supervising controllers.
+.Pp
+Its main purpose is detecting type of NAND chips connected to a given chip
+select (CS line). It also allows manages locking access to the NAND
+controller. NANDbus passes requests from an active chip to the chip
+controller.
+.It
+NAND character / GEOM device.
+.Pp
+For each NAND chip found in a system a character and GEOM devices are created
+which allows to read / write directly to a device, as well as perform other
+specific operations (like via ioctl).
+.Pp
+There are two GEOM devices created for each NAND chip:
+.Bl -bullet
+.It
+raw device
+.It
+normal device
+.El
+.Pp
+Raw device allows to bypass ECC checking when reading/writing to it, while
+normal device always uses ECC algorithm to validate the read data.
+.Pp
+NAND character devices will be created for each NAND chip detected while
+probing the NAND controller.
+.El
+.Sh SEE ALSO
+.Xr libnandfs 3 ,
+.Xr gnand 4 ,
+.Xr nandsim 4 ,
+.Xr nandfs 5 ,
+.Xr makefs 8 ,
+.Xr mount_nandfs 8 ,
+.Xr nandfs 8 ,
+.Xr nandsim 8 ,
+.Xr nandtool 8 ,
+.Xr newfs_nandfs 8 ,
+.Xr umount_nandfs 8
+.Sh STANDARDS
+Open NAND Flash Interface Working Group
+.Pq Vt ONFI .
+.Sh HISTORY
+The
+.Nm
+framework support first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+The
+.Nm
+framework was designed and developed by
+.An Grzegorz Bernacki .
+This manual page was written by
+.An Rafal Jaworowski .
diff --git a/share/man/man4/nandsim.4 b/share/man/man4/nandsim.4
new file mode 100644
index 0000000..154d1ff
--- /dev/null
+++ b/share/man/man4/nandsim.4
@@ -0,0 +1,184 @@
+.\"
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This documentation was written by Semihalf under sponsorship from
+.\" the FreeBSD Foundation.
+.\"
+.\" 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 March 8, 2012
+.Dt NANDSIM 4
+.Os
+.Sh NAME
+.Nm nandsim
+.Nd NAND Flash simulator driver
+.Sh SYNOPSIS
+.Cd "device nand"
+.Cd "device nandsim"
+.Cd "options ALQ"
+.Sh DESCRIPTION
+The
+.Nm
+is part of the
+.Fx
+NAND framework
+.Xr nand 4
+and can be characterized with the following highlights:
+.Bl -bullet
+.It
+plugs into the
+.Xr nand 4
+framework APIs as if it were a hardware controller (hanging on the nexus bus)
+with real NAND chips connected to it
+.It
+physically part of the kernel code (either statically linked into the kernel
+image or built as a module)
+.It
+controlled with a user space program
+.Xr nandsim 8
+.El
+.Pp
+From the user perspective, the
+.Nm
+allows for imitating ONFI-compliant NAND Flash devices as if they were
+attached to the system via a virtual controller.
+.Pp
+Some
+.Nm
+features rely on the ability to log contents to a file, which is achieved
+through the
+.Xr alq 9
+facility.
+.Sh SEE ALSO
+.Xr nand 4 ,
+.Xr nandsim.conf 5 ,
+.Xr nandsim 8
+.Sh STANDARDS
+Open NAND Flash Interface Working Group
+.Pq Vt ONFI .
+.Sh HISTORY
+The
+.Nm
+support first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+The
+.Nm
+kernel driver was developed by
+.An Grzegorz Bernacki .
+This manual page was written by
+.An Rafal Jaworowski .
+.\"
+.\" Copyright (c) 2012 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This documentation was written by Semihalf under sponsorship from
+.\" the FreeBSD Foundation.
+.\"
+.\" 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 March 8, 2012
+.Dt NANDSIM 4
+.Os
+.Sh NAME
+.Nm nandsim
+.Nd NAND Flash simulator driver
+.Sh SYNOPSIS
+.Cd "device nand"
+.Cd "device nandsim"
+.Cd "options ALQ"
+.Sh DESCRIPTION
+The
+.Nm
+is part of the
+.Fx
+NAND framework
+.Xr nand 4
+and can be characterized with the following highlights:
+.Bl -bullet
+.It
+plugs into the
+.Xr nand 4
+framework APIs as if it were a hardware controller (hanging on the nexus bus)
+with real NAND chips connected to it
+.It
+physically part of the kernel code (either statically linked into the kernel
+image or built as a module)
+.It
+controlled with a user space program
+.Xr nandsim 8
+.El
+.Pp
+From the user perspective, the
+.Nm
+allows for imitating ONFI-compliant NAND Flash devices as if they were
+attached to the system via a virtual controller.
+.Pp
+Some
+.Nm
+features rely on the ability to log contents to a file, which is achieved
+through the
+.Xr alq 9
+facility.
+.Sh SEE ALSO
+.Xr nand 4 ,
+.Xr nandsim.conf 5 ,
+.Xr nandsim 8
+.Sh STANDARDS
+Open NAND Flash Interface Working Group
+.Pq Vt ONFI .
+.Sh HISTORY
+The
+.Nm
+support first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+The
+.Nm
+kernel driver was developed by
+.An Grzegorz Bernacki .
+This manual page was written by
+.An Rafal Jaworowski .
diff --git a/share/man/man5/Makefile b/share/man/man5/Makefile
index b05c1eb..c7a3c59 100644
--- a/share/man/man5/Makefile
+++ b/share/man/man5/Makefile
@@ -85,6 +85,10 @@ MLINKS+=resolver.5 resolv.conf.5
MAN+= hesiod.conf.5
.endif
+.if ${MK_NAND} != "no"
+MAN+= nandfs.5
+.endif
+
.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386"
_boot.config.5= boot.config.5
.endif
diff --git a/share/man/man5/nandfs.5 b/share/man/man5/nandfs.5
new file mode 100644
index 0000000..43d586c
--- /dev/null
+++ b/share/man/man5/nandfs.5
@@ -0,0 +1,129 @@
+.\"
+.\" 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 Nov 11, 2010
+.Dt NANDFS 5
+.Os
+.Sh NAME
+.Nm nandfs
+.Nd NAND Flash file system
+.Sh SYNOPSIS
+To compile support for the
+.Nm ,
+place the following in your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "options NANDFS"
+.Ed
+.Pp
+Even though the NAND FS can be used with any storage media, it has been
+optimized and designed towards NAND Flash devices, so typically the following
+driver is used:
+.Bd -ragged -offset indent
+.Cd "device nand"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver enables
+.Fx
+with support for NAND-oriented file system.
+.Pp
+It is a log-structured style file system with the following major features and
+characteristics:
+.Bl -bullet
+.It
+Hard links, symbolic links support
+.It
+Block journaling
+.It
+Copy-On-Write
+.It
+Snapshots (continuous, taken automatically, simultaneously mountable)
+.It
+Quick crash recovery at mount time
+.It
+64-bit data structures; supports many files, large files and volumes
+.It
+POSIX file permissions
+.It
+Checksum / ECC
+.El
+.Sh EXAMPLES
+The most common usage is mounting the file system:
+.Pp
+.Dl "mount -t nandfs /dev/<gnandN> /mnt"
+.Pp
+or:
+.Dl "mount_nandfs /dev/<gnandN> /mnt"
+.Pp
+where
+.Ar gnandN
+is the GEOM device representing a Flash partition (slice) containing the
+.Nm
+structure, and
+.Pa /mnt
+is a mount point.
+.Pp
+.Pp
+It is possible to define an entry in
+.Pa /etc/fstab
+for the
+.Nm :
+.Bd -literal
+/dev/gnand0 /flash nandfs rw 0 0
+.Ed
+.Pp
+This will mount a
+.Nm
+partition at the specified mount point during system boot.
+.Sh SEE ALSO
+.Xr gnand 4 ,
+.Xr nand 4 ,
+.Xr mount_nandfs 8 ,
+.Xr nandfs 8 ,
+.Xr nandsim 8 ,
+.Xr nandtool 8 ,
+.Xr umount_nandfs 8
+.Sh HISTORY
+The NAND FS concepts are based on NILFS principles and initial implementation
+was derived from early NILFS NetBSD code (read only). Since then the NAND FS
+code diverged significantly and is by no means compatible with NILFS.
+.Pp
+The NAND Flash file system first appeared in
+.Fx 10.0 .
+.Sh AUTHOR
+The NAND FS was written by
+.An Grzegorz Bernacki with the help of
+.An Mateusz Guzik ,
+based on the NetBSD code created by
+.An Reinoud Zandijk .
+Additional help and support by
+.An Lukasz Plachno ,
+.An Jan Sieka and
+.An Lukasz Wojcik .
+This manual page was written by
+.An Rafal Jaworowski .
diff --git a/share/mk/bsd.own.mk b/share/mk/bsd.own.mk
index 9802e79..b19253e 100644
--- a/share/mk/bsd.own.mk
+++ b/share/mk/bsd.own.mk
@@ -426,6 +426,7 @@ __DEFAULT_NO_OPTIONS = \
ICONV \
IDEA \
LIBCPLUSPLUS \
+ NAND \
OFED \
SHARED_TOOLCHAIN
diff --git a/sys/boot/arm/uboot/Makefile b/sys/boot/arm/uboot/Makefile
index 906fc87..b69d38c 100644
--- a/sys/boot/arm/uboot/Makefile
+++ b/sys/boot/arm/uboot/Makefile
@@ -15,6 +15,11 @@ LOADER_DISK_SUPPORT?= yes
LOADER_UFS_SUPPORT?= yes
LOADER_CD9660_SUPPORT?= no
LOADER_EXT2FS_SUPPORT?= no
+.if ${MK_NAND} != "no"
+LOADER_NANDFS_SUPPORT?= yes
+.else
+LOADER_NANDFS_SUPPORT?= no
+.endif
LOADER_NET_SUPPORT?= yes
LOADER_NFS_SUPPORT?= yes
LOADER_TFTP_SUPPORT?= no
@@ -38,6 +43,9 @@ CFLAGS+= -DLOADER_CD9660_SUPPORT
.if ${LOADER_EXT2FS_SUPPORT} == "yes"
CFLAGS+= -DLOADER_EXT2FS_SUPPORT
.endif
+.if ${LOADER_NANDFS_SUPPORT} == "yes"
+CFLAGS+= -DLOADER_NANDFS_SUPPORT
+.endif
.if ${LOADER_GZIP_SUPPORT} == "yes"
CFLAGS+= -DLOADER_GZIP_SUPPORT
.endif
diff --git a/sys/boot/arm/uboot/conf.c b/sys/boot/arm/uboot/conf.c
index b797565..03dc641 100644
--- a/sys/boot/arm/uboot/conf.c
+++ b/sys/boot/arm/uboot/conf.c
@@ -56,6 +56,9 @@ struct fs_ops *file_system[] = {
#if defined(LOADER_EXT2FS_SUPPORT)
&ext2fs_fsops,
#endif
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
#if defined(LOADER_NFS_SUPPORT)
&nfs_fsops,
#endif
diff --git a/sys/boot/arm/uboot/version b/sys/boot/arm/uboot/version
index 38c7eb6..486c412 100644
--- a/sys/boot/arm/uboot/version
+++ b/sys/boot/arm/uboot/version
@@ -3,6 +3,7 @@ $FreeBSD$
NOTE ANY CHANGES YOU MAKE TO THE BOOTBLOCKS HERE. The format of this
file is important. Make sure the current version number is on line 6.
+1.2: Extended with NAND FS support.
1.1: Flattened Device Tree blob support.
1.0: Added storage support. Booting from HDD, USB, etc. is now possible.
0.5: Initial U-Boot/arm version (netbooting only).
diff --git a/sys/boot/i386/loader/Makefile b/sys/boot/i386/loader/Makefile
index e2f8892..d3f01b6 100644
--- a/sys/boot/i386/loader/Makefile
+++ b/sys/boot/i386/loader/Makefile
@@ -53,6 +53,9 @@ CFLAGS+= -DLOADER_GZIP_SUPPORT
.if !defined(LOADER_NO_GPT_SUPPORT)
CFLAGS+= -DLOADER_GPT_SUPPORT
.endif
+.if defined(LOADER_NANDFS_SUPPORT)
+CFLAGS+= -DLOADER_NANDFS_SUPPORT
+.endif
# Always add MI sources
.PATH: ${.CURDIR}/../../common
diff --git a/sys/boot/i386/loader/conf.c b/sys/boot/i386/loader/conf.c
index 09d6a4e..dda2eac 100644
--- a/sys/boot/i386/loader/conf.c
+++ b/sys/boot/i386/loader/conf.c
@@ -74,6 +74,9 @@ struct fs_ops *file_system[] = {
&ext2fs_fsops,
&dosfs_fsops,
&cd9660_fsops,
+#if defined(LOADER_NANDFS_SUPPORT)
+ &nandfs_fsops,
+#endif
&splitfs_fsops,
#if defined(LOADER_ZFS_SUPPORT)
&zfs_fsops,
diff --git a/sys/conf/files b/sys/conf/files
index 3c84cf6..3a2b65e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1660,6 +1660,21 @@ dev/mxge/mxge_ethp_z8e.c optional mxge pci
dev/mxge/mxge_rss_eth_z8e.c optional mxge pci
dev/mxge/mxge_rss_ethp_z8e.c optional mxge pci
dev/my/if_my.c optional my
+dev/nand/nand.c optional nand
+dev/nand/nand_bbt.c optional nand
+dev/nand/nand_cdev.c optional nand
+dev/nand/nand_generic.c optional nand
+dev/nand/nand_geom.c optional nand
+dev/nand/nand_id.c optional nand
+dev/nand/nandbus.c optional nand
+dev/nand/nandbus_if.m optional nand
+dev/nand/nand_if.m optional nand
+dev/nand/nandsim.c optional nandsim nand
+dev/nand/nandsim_chip.c optional nandsim nand
+dev/nand/nandsim_ctrl.c optional nandsim nand
+dev/nand/nandsim_log.c optional nandsim nand
+dev/nand/nandsim_swap.c optional nandsim nand
+dev/nand/nfc_if.m optional nand
dev/ncv/ncr53c500.c optional ncv
dev/ncv/ncr53c500_pccard.c optional ncv pccard
dev/netmap/netmap.c optional netmap
@@ -2252,6 +2267,20 @@ fs/msdosfs/msdosfs_iconv.c optional msdosfs_iconv
fs/msdosfs/msdosfs_lookup.c optional msdosfs
fs/msdosfs/msdosfs_vfsops.c optional msdosfs
fs/msdosfs/msdosfs_vnops.c optional msdosfs
+fs/nandfs/bmap.c optional nandfs
+fs/nandfs/nandfs_alloc.c optional nandfs
+fs/nandfs/nandfs_bmap.c optional nandfs
+fs/nandfs/nandfs_buffer.c optional nandfs
+fs/nandfs/nandfs_cleaner.c optional nandfs
+fs/nandfs/nandfs_cpfile.c optional nandfs
+fs/nandfs/nandfs_dat.c optional nandfs
+fs/nandfs/nandfs_dir.c optional nandfs
+fs/nandfs/nandfs_ifile.c optional nandfs
+fs/nandfs/nandfs_segment.c optional nandfs
+fs/nandfs/nandfs_subr.c optional nandfs
+fs/nandfs/nandfs_sufile.c optional nandfs
+fs/nandfs/nandfs_vfsops.c optional nandfs
+fs/nandfs/nandfs_vnops.c optional nandfs
fs/nfs/nfs_commonkrpc.c optional nfscl | nfsd
fs/nfs/nfs_commonsubs.c optional nfscl | nfsd
fs/nfs/nfs_commonport.c optional nfscl | nfsd
diff --git a/sys/conf/options b/sys/conf/options
index 4c95967..8f810f4 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -213,6 +213,7 @@ FDESCFS opt_dontuse.h
FFS opt_dontuse.h
HPFS opt_dontuse.h
MSDOSFS opt_dontuse.h
+NANDFS opt_dontuse.h
NTFS opt_dontuse.h
NULLFS opt_dontuse.h
NWFS opt_dontuse.h
diff --git a/sys/dev/nand/nand.c b/sys/dev/nand/nand.c
new file mode 100644
index 0000000..ad5bc40
--- /dev/null
+++ b/sys/dev/nand/nand.c
@@ -0,0 +1,832 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/callout.h>
+#include <sys/sysctl.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_ecc_pos.h>
+#include "nfc_if.h"
+#include "nand_if.h"
+#include "nandbus_if.h"
+#include <machine/stdarg.h>
+
+#define NAND_RESET_DELAY 1000 /* tRST */
+#define NAND_ERASE_DELAY 3000 /* tBERS */
+#define NAND_PROG_DELAY 700 /* tPROG */
+#define NAND_READ_DELAY 50 /* tR */
+
+#define BIT0(x) ((x) & 0x1)
+#define BIT1(x) (BIT0(x >> 1))
+#define BIT2(x) (BIT0(x >> 2))
+#define BIT3(x) (BIT0(x >> 3))
+#define BIT4(x) (BIT0(x >> 4))
+#define BIT5(x) (BIT0(x >> 5))
+#define BIT6(x) (BIT0(x >> 6))
+#define BIT7(x) (BIT0(x >> 7))
+
+#define SOFTECC_SIZE 256
+#define SOFTECC_BYTES 3
+
+int nand_debug_flag = 0;
+SYSCTL_INT(_debug, OID_AUTO, nand_debug, CTLFLAG_RW, &nand_debug_flag, 0,
+ "NAND subsystem debug flag");
+
+static void
+nand_tunable_init(void *arg)
+{
+
+ TUNABLE_INT_FETCH("debug.nand", &nand_debug_flag);
+}
+
+SYSINIT(nand_tunables, SI_SUB_VFS, SI_ORDER_ANY, nand_tunable_init, NULL);
+
+MALLOC_DEFINE(M_NAND, "NAND", "NAND dynamic data");
+
+static void calculate_ecc(const uint8_t *, uint8_t *);
+static int correct_ecc(uint8_t *, uint8_t *, uint8_t *);
+
+void
+nand_debug(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!(nand_debug_flag & level))
+ return;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ printf("\n");
+}
+
+void
+nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
+ int ecc_bytes, int ecc_size, uint16_t *eccposition, char *cdev_name)
+{
+
+ nand->ecc.eccmode = ecc_mode;
+ nand->chip_cdev_name = cdev_name;
+
+ if (ecc_mode == NAND_ECC_SOFT) {
+ nand->ecc.eccbytes = SOFTECC_BYTES;
+ nand->ecc.eccsize = SOFTECC_SIZE;
+ } else if (ecc_mode != NAND_ECC_NONE) {
+ nand->ecc.eccbytes = ecc_bytes;
+ nand->ecc.eccsize = ecc_size;
+ if (eccposition)
+ nand->ecc.eccpositions = eccposition;
+ }
+}
+
+void
+nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params)
+{
+ struct chip_geom *cg;
+
+ cg = &chip->chip_geom;
+
+ init_chip_geom(cg, params->luns, params->blocks_per_lun,
+ params->pages_per_block, params->bytes_per_page,
+ params->spare_bytes_per_page);
+ chip->t_bers = params->t_bers;
+ chip->t_prog = params->t_prog;
+ chip->t_r = params->t_r;
+ chip->t_ccs = params->t_ccs;
+
+ if (params->features & ONFI_FEAT_16BIT)
+ chip->flags |= NAND_16_BIT;
+}
+
+void
+nand_set_params(struct nand_chip *chip, struct nand_params *params)
+{
+ struct chip_geom *cg;
+ uint32_t blocks_per_chip;
+
+ cg = &chip->chip_geom;
+ blocks_per_chip = (params->chip_size << 20) /
+ (params->page_size * params->pages_per_block);
+
+ init_chip_geom(cg, 1, blocks_per_chip,
+ params->pages_per_block, params->page_size,
+ params->oob_size);
+
+ chip->t_bers = NAND_ERASE_DELAY;
+ chip->t_prog = NAND_PROG_DELAY;
+ chip->t_r = NAND_READ_DELAY;
+ chip->t_ccs = 0;
+
+ if (params->flags & NAND_16_BIT)
+ chip->flags |= NAND_16_BIT;
+}
+
+int
+nand_init_stat(struct nand_chip *chip)
+{
+ struct block_stat *blk_stat;
+ struct page_stat *pg_stat;
+ struct chip_geom *cg;
+ uint32_t blks, pgs;
+
+ cg = &chip->chip_geom;
+ blks = cg->blks_per_lun * cg->luns;
+ blk_stat = malloc(sizeof(struct block_stat) * blks, M_NAND,
+ M_WAITOK | M_ZERO);
+ if (!blk_stat)
+ return (ENOMEM);
+
+ pgs = blks * cg->pgs_per_blk;
+ pg_stat = malloc(sizeof(struct page_stat) * pgs, M_NAND,
+ M_WAITOK | M_ZERO);
+ if (!pg_stat) {
+ free(blk_stat, M_NAND);
+ return (ENOMEM);
+ }
+
+ chip->blk_stat = blk_stat;
+ chip->pg_stat = pg_stat;
+
+ return (0);
+}
+
+void
+nand_destroy_stat(struct nand_chip *chip)
+{
+
+ free(chip->pg_stat, M_NAND);
+ free(chip->blk_stat, M_NAND);
+}
+
+int
+init_chip_geom(struct chip_geom *cg, uint32_t luns, uint32_t blks_per_lun,
+ uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size)
+{
+ int shift;
+
+ if (!cg)
+ return (-1);
+
+ cg->luns = luns;
+ cg->blks_per_lun = blks_per_lun;
+ cg->blks_per_chip = blks_per_lun * luns;
+ cg->pgs_per_blk = pgs_per_blk;
+
+ cg->page_size = pg_size;
+ cg->oob_size = oob_size;
+ cg->block_size = cg->page_size * cg->pgs_per_blk;
+ cg->chip_size = cg->block_size * cg->blks_per_chip;
+
+ shift = fls(cg->pgs_per_blk - 1);
+ cg->pg_mask = (1 << shift) - 1;
+ cg->blk_shift = shift;
+
+ if (cg->blks_per_lun > 0) {
+ shift = fls(cg->blks_per_lun - 1);
+ cg->blk_mask = ((1 << shift) - 1) << cg->blk_shift;
+ } else {
+ shift = 0;
+ cg->blk_mask = 0;
+ }
+
+ cg->lun_shift = shift + cg->blk_shift;
+ shift = fls(cg->luns - 1);
+ cg->lun_mask = ((1 << shift) - 1) << cg->lun_shift;
+
+ nand_debug(NDBG_NAND, "Masks: lun 0x%x blk 0x%x page 0x%x\n"
+ "Shifts: lun %d blk %d",
+ cg->lun_mask, cg->blk_mask, cg->pg_mask,
+ cg->lun_shift, cg->blk_shift);
+
+ return (0);
+}
+
+int
+nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
+ uint32_t *blk, uint32_t *pg)
+{
+
+ if (!cg || !lun || !blk || !pg)
+ return (-1);
+
+ if (row & ~(cg->lun_mask | cg->blk_mask | cg->pg_mask)) {
+ nand_debug(NDBG_NAND,"Address out of bounds\n");
+ return (-1);
+ }
+
+ *lun = (row & cg->lun_mask) >> cg->lun_shift;
+ *blk = (row & cg->blk_mask) >> cg->blk_shift;
+ *pg = (row & cg->pg_mask);
+
+ nand_debug(NDBG_NAND,"address %x-%x-%x\n", *lun, *blk, *pg);
+
+ return (0);
+}
+
+int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row)
+{
+ uint32_t lun, block, pg_in_blk;
+
+ if (!cg || !row)
+ return (-1);
+
+ block = page / cg->pgs_per_blk;
+ pg_in_blk = page % cg->pgs_per_blk;
+
+ lun = block / cg->blks_per_lun;
+ block = block % cg->blks_per_lun;
+
+ *row = (lun << cg->lun_shift) & cg->lun_mask;
+ *row |= ((block << cg->blk_shift) & cg->blk_mask);
+ *row |= (pg_in_blk & cg->pg_mask);
+
+ return (0);
+}
+
+int
+nand_check_page_boundary(struct nand_chip *chip, uint32_t page)
+{
+ struct chip_geom* cg;
+
+ cg = &chip->chip_geom;
+ if (page >= (cg->pgs_per_blk * cg->blks_per_lun * cg->luns)) {
+ nand_debug(NDBG_GEN,"%s: page number too big %#x\n",
+ __func__, page);
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param)
+{
+ struct chip_geom *cg;
+
+ cg = &chip->chip_geom;
+ param->page_size = cg->page_size;
+ param->oob_size = cg->oob_size;
+
+ param->blocks = cg->blks_per_lun * cg->luns;
+ param->pages_per_block = cg->pgs_per_blk;
+}
+
+static uint16_t *
+default_software_ecc_positions(struct nand_chip *chip)
+{
+ struct nand_ecc_data *eccd;
+
+ eccd = &chip->nand->ecc;
+
+ if (eccd->eccpositions)
+ return (eccd->eccpositions);
+
+ switch (chip->chip_geom.oob_size) {
+ case 16:
+ return ((uint16_t *)&default_software_ecc_positions_16);
+ case 64:
+ return ((uint16_t *)&default_software_ecc_positions_64);
+ case 128:
+ return ((uint16_t *)&default_software_ecc_positions_128);
+ default:
+ return (NULL); /* No ecc bytes positions defs available */
+ }
+
+ return (NULL);
+}
+
+static void
+calculate_ecc(const uint8_t *buf, uint8_t *ecc)
+{
+ uint8_t p8, byte;
+ int i;
+
+ memset(ecc, 0, 3);
+
+ for (i = 0; i < 256; i++) {
+ byte = buf[i];
+ ecc[0] ^= (BIT0(byte) ^ BIT2(byte) ^ BIT4(byte) ^
+ BIT6(byte)) << 2;
+ ecc[0] ^= (BIT1(byte) ^ BIT3(byte) ^ BIT5(byte) ^
+ BIT7(byte)) << 3;
+ ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT4(byte) ^
+ BIT5(byte)) << 4;
+ ecc[0] ^= (BIT2(byte) ^ BIT3(byte) ^ BIT6(byte) ^
+ BIT7(byte)) << 5;
+ ecc[0] ^= (BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
+ BIT3(byte)) << 6;
+ ecc[0] ^= (BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
+ BIT7(byte)) << 7;
+
+ p8 = BIT0(byte) ^ BIT1(byte) ^ BIT2(byte) ^
+ BIT3(byte) ^ BIT4(byte) ^ BIT5(byte) ^ BIT6(byte) ^
+ BIT7(byte);
+
+ if (p8) {
+ ecc[2] ^= (0x1 << BIT0(i));
+ ecc[2] ^= (0x4 << BIT1(i));
+ ecc[2] ^= (0x10 << BIT2(i));
+ ecc[2] ^= (0x40 << BIT3(i));
+
+ ecc[1] ^= (0x1 << BIT4(i));
+ ecc[1] ^= (0x4 << BIT5(i));
+ ecc[1] ^= (0x10 << BIT6(i));
+ ecc[1] ^= (0x40 << BIT7(i));
+ }
+ }
+ ecc[0] = ~ecc[0];
+ ecc[1] = ~ecc[1];
+ ecc[2] = ~ecc[2];
+ ecc[0] |= 3;
+}
+
+static int
+correct_ecc(uint8_t *buf, uint8_t *calc_ecc, uint8_t *read_ecc)
+{
+ uint8_t ecc0, ecc1, ecc2, onesnum, bit, byte;
+ uint16_t addr = 0;
+
+ ecc0 = calc_ecc[0] ^ read_ecc[0];
+ ecc1 = calc_ecc[1] ^ read_ecc[1];
+ ecc2 = calc_ecc[2] ^ read_ecc[2];
+
+ if (!ecc0 && !ecc1 && !ecc2)
+ return (ECC_OK);
+
+ addr = BIT3(ecc0) | (BIT5(ecc0) << 1) | (BIT7(ecc0) << 2);
+ addr |= (BIT1(ecc2) << 3) | (BIT3(ecc2) << 4) |
+ (BIT5(ecc2) << 5) | (BIT7(ecc2) << 6);
+ addr |= (BIT1(ecc1) << 7) | (BIT3(ecc1) << 8) |
+ (BIT5(ecc1) << 9) | (BIT7(ecc1) << 10);
+
+ onesnum = 0;
+ while (ecc0 || ecc1 || ecc2) {
+ if (ecc0 & 1)
+ onesnum++;
+ if (ecc1 & 1)
+ onesnum++;
+ if (ecc2 & 1)
+ onesnum++;
+
+ ecc0 >>= 1;
+ ecc1 >>= 1;
+ ecc2 >>= 1;
+ }
+
+ if (onesnum == 11) {
+ /* Correctable error */
+ bit = addr & 7;
+ byte = addr >> 3;
+ buf[byte] ^= (1 << bit);
+ return (ECC_CORRECTABLE);
+ } else if (onesnum == 1) {
+ /* ECC error */
+ return (ECC_ERROR_ECC);
+ } else {
+ /* Uncorrectable error */
+ return (ECC_UNCORRECTABLE);
+ }
+
+ return (0);
+}
+
+int
+nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc)
+{
+ int steps = pagesize / SOFTECC_SIZE;
+ int i = 0, j = 0;
+
+ for (; i < (steps * SOFTECC_BYTES);
+ i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
+ calculate_ecc(&buf[j], &ecc[i]);
+ }
+
+ return (0);
+}
+
+int
+nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
+ uint8_t *readecc, uint8_t *calcecc)
+{
+ int steps = pagesize / SOFTECC_SIZE;
+ int i = 0, j = 0, ret = 0;
+
+ for (i = 0; i < (steps * SOFTECC_BYTES);
+ i += SOFTECC_BYTES, j += SOFTECC_SIZE) {
+ ret += correct_ecc(&buf[j], &calcecc[i], &readecc[i]);
+ if (ret < 0)
+ return (ret);
+ }
+
+ return (ret);
+}
+
+static int
+offset_to_page(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset / cg->page_size);
+}
+
+int
+nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ struct nand_ecc_data *eccd;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ void *oob = NULL;
+ uint8_t *ptr;
+ uint16_t *eccpos = NULL;
+ uint32_t page, num, steps = 0;
+ int i, retval = 0, needwrite;
+
+ nand_debug(NDBG_NAND,"%p read page %x[%x]", chip, offset, len);
+ cg = &chip->chip_geom;
+ eccd = &chip->nand->ecc;
+ page = offset_to_page(cg, offset);
+ num = len / cg->page_size;
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ steps = cg->page_size / eccd->eccsize;
+ eccpos = default_software_ecc_positions(chip);
+ oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ }
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ while (num--) {
+ pg_stat = &(chip->pg_stat[page]);
+
+ if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ retval = ENXIO;
+ break;
+ }
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ if (NAND_GET_ECC(chip->dev, ptr, eccd->ecccalculated,
+ &needwrite)) {
+ retval = ENXIO;
+ break;
+ }
+ nand_debug(NDBG_ECC,"%s: ECC calculated:",
+ __func__);
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->ecccalculated[i]);
+
+ nand_debug(NDBG_ECC,"\n");
+
+ if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
+ 0)) {
+ retval = ENXIO;
+ break;
+ }
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ eccd->eccread[i] = ((uint8_t *)oob)[eccpos[i]];
+
+ nand_debug(NDBG_ECC,"%s: ECC read:", __func__);
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->eccread[i]);
+ nand_debug(NDBG_ECC,"\n");
+
+ retval = NAND_CORRECT_ECC(chip->dev, ptr, eccd->eccread,
+ eccd->ecccalculated);
+
+ nand_debug(NDBG_ECC, "NAND_CORRECT_ECC() returned %d",
+ retval);
+
+ if (retval == 0)
+ pg_stat->ecc_stat.ecc_succeded++;
+ else if (retval > 0) {
+ pg_stat->ecc_stat.ecc_corrected += retval;
+ retval = ECC_CORRECTABLE;
+ } else {
+ pg_stat->ecc_stat.ecc_failed++;
+ break;
+ }
+ }
+
+ pg_stat->page_read++;
+ page++;
+ ptr += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (oob)
+ free(oob, M_NAND);
+
+ return (retval);
+}
+
+int
+nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ uint8_t *ptr;
+ uint32_t page, num, end, begin = 0, begin_off;
+ int retval = 0;
+
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ begin_off = offset - page * cg->page_size;
+ if (begin_off) {
+ begin = cg->page_size - begin_off;
+ len -= begin;
+ }
+ num = len / cg->page_size;
+ end = len % cg->page_size;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ if (begin_off) {
+ if (NAND_READ_PAGE(chip->dev, page, ptr, begin, begin_off)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += begin;
+ }
+
+ while (num--) {
+ if (NAND_READ_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += cg->page_size;
+ }
+
+ if (end)
+ if (NAND_READ_PAGE(chip->dev, page, ptr, end, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+
+int
+nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ struct page_stat *pg_stat;
+ struct nand_ecc_data *eccd;
+ device_t nandbus;
+ uint32_t page, num;
+ uint8_t *oob = NULL;
+ uint16_t *eccpos = NULL;
+ int steps = 0, i, needwrite, err = 0;
+
+ nand_debug(NDBG_NAND,"%p prog page %x[%x]", chip, offset, len);
+
+ eccd = &chip->nand->ecc;
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ num = len / cg->page_size;
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ steps = cg->page_size / eccd->eccsize;
+ oob = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ eccpos = default_software_ecc_positions(chip);
+ }
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ while (num--) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, buf, cg->page_size, 0)) {
+ err = ENXIO;
+ break;
+ }
+
+ if (eccd->eccmode != NAND_ECC_NONE) {
+ if (NAND_GET_ECC(chip->dev, buf, &eccd->ecccalculated,
+ &needwrite)) {
+ err = ENXIO;
+ break;
+ }
+ nand_debug(NDBG_ECC,"ECC calculated:");
+ if (nand_debug_flag & NDBG_ECC)
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ printf("%x ", eccd->ecccalculated[i]);
+
+ nand_debug(NDBG_ECC,"\n");
+
+ if (needwrite) {
+ if (NAND_READ_OOB(chip->dev, page, oob, cg->oob_size,
+ 0)) {
+ err = ENXIO;
+ break;
+ }
+
+ for (i = 0; i < (eccd->eccbytes * steps); i++)
+ oob[eccpos[i]] = eccd->ecccalculated[i];
+
+ if (NAND_PROGRAM_OOB(chip->dev, page, oob,
+ cg->oob_size, 0)) {
+ err = ENXIO;
+ break;
+ }
+ }
+ }
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_written++;
+
+ page++;
+ buf += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (oob)
+ free(oob, M_NAND);
+
+ return (err);
+}
+
+int
+nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ uint8_t *ptr;
+ uint32_t page, num, end, begin = 0, begin_off;
+ int retval = 0;
+
+ cg = &chip->chip_geom;
+ page = offset_to_page(cg, offset);
+ begin_off = offset - page * cg->page_size;
+ if (begin_off) {
+ begin = cg->page_size - begin_off;
+ len -= begin;
+ }
+ num = len / cg->page_size;
+ end = len % cg->page_size;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ ptr = (uint8_t *)buf;
+ if (begin_off) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, begin, begin_off)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += begin;
+ }
+
+ while (num--) {
+ if (NAND_PROGRAM_PAGE(chip->dev, page, ptr, cg->page_size, 0)) {
+ NANDBUS_UNLOCK(nandbus);
+ return (ENXIO);
+ }
+
+ page++;
+ ptr += cg->page_size;
+ }
+
+ if (end)
+ retval = NAND_PROGRAM_PAGE(chip->dev, page, ptr, end, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+int
+nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len)
+{
+ device_t nandbus;
+ int retval = 0;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ retval = NAND_READ_OOB(chip->dev, page, buf, len, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+
+int
+nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len)
+{
+ device_t nandbus;
+ int retval = 0;
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ retval = NAND_PROGRAM_OOB(chip->dev, page, buf, len, 0);
+
+ NANDBUS_UNLOCK(nandbus);
+
+ return (retval);
+}
+
+int
+nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len)
+{
+ device_t nandbus;
+ struct chip_geom *cg;
+ uint32_t block, num_blocks;
+ int err = 0;
+
+ cg = &chip->chip_geom;
+ if ((offset % cg->block_size) || (len % cg->block_size))
+ return (EINVAL);
+
+ block = offset / cg->block_size;
+ num_blocks = len / cg->block_size;
+ nand_debug(NDBG_NAND,"%p erase blocks %d[%d]", chip, block, num_blocks);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ while (num_blocks--) {
+ if (!nand_check_bad_block(chip, block)) {
+ if (NAND_ERASE_BLOCK(chip->dev, block)) {
+ nand_debug(NDBG_NAND,"%p erase blocks %d error",
+ chip, block);
+ nand_mark_bad_block(chip, block);
+ err = ENXIO;
+ }
+ } else
+ err = ENXIO;
+
+ block++;
+ };
+
+ NANDBUS_UNLOCK(nandbus);
+
+ if (err)
+ nand_update_bbt(chip);
+
+ return (err);
+}
diff --git a/sys/dev/nand/nand.h b/sys/dev/nand/nand.h
new file mode 100644
index 0000000..05101ac
--- /dev/null
+++ b/sys/dev/nand/nand.h
@@ -0,0 +1,385 @@
+/*-
+ * 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$
+ */
+
+#ifndef _DEV_NAND_H_
+#define _DEV_NAND_H_
+
+#include <sys/bus.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+#include <sys/taskqueue.h>
+#include <sys/queue.h>
+#include <sys/bio.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand_dev.h>
+
+MALLOC_DECLARE(M_NAND);
+
+/* Read commands */
+#define NAND_CMD_READ 0x00
+#define NAND_CMD_CHNG_READ_COL 0x05
+#define NAND_CMD_READ_END 0x30
+#define NAND_CMD_READ_CACHE 0x31
+#define NAND_CMD_READ_CPBK 0x35
+#define NAND_CMD_READ_CACHE_END 0x3F
+#define NAND_CMD_CHNG_READ_COL_END 0xE0
+
+/* Erase commands */
+#define NAND_CMD_ERASE 0x60
+#define NAND_CMD_ERASE_END 0xD0
+#define NAND_CMD_ERASE_INTLV 0xD1
+
+/* Program commands */
+#define NAND_CMD_PROG 0x80
+#define NAND_CMD_CHNG_WRITE_COL 0x85
+#define NAND_CMD_PROG_END 0x10
+#define NAND_CMD_PROG_INTLV 0x11
+#define NAND_CMD_PROG_CACHE 0x15
+
+/* Misc commands */
+#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_STATUS_ENH 0x78
+#define NAND_CMD_READ_ID 0x90
+#define NAND_CMD_READ_PARAMETER 0xec
+#define NAND_CMD_READ_UNIQUE_ID 0xed
+#define NAND_CMD_GET_FEATURE 0xee
+#define NAND_CMD_SET_FEATURE 0xef
+
+/* Reset commands */
+#define NAND_CMD_SYNCH_RESET 0xfc
+#define NAND_CMD_RESET 0xff
+
+/* Small page flash commands */
+#define NAND_CMD_SMALLA 0x00
+#define NAND_CMD_SMALLB 0x01
+#define NAND_CMD_SMALLOOB 0x50
+
+#define NAND_STATUS_FAIL 0x1
+#define NAND_STATUS_FAILC 0x2
+#define NAND_STATUS_ARDY 0x20
+#define NAND_STATUS_RDY 0x40
+#define NAND_STATUS_WP 0x80
+
+#define NAND_LP_OOB_COLUMN_START 0x800
+#define NAND_LP_OOBSZ 0x40
+#define NAND_SP_OOB_COLUMN_START 0x200
+#define NAND_SP_OOBSZ 0x10
+
+#define PAGE_PARAM_LENGTH 0x100
+#define PAGE_PARAMETER_DEF 0x0
+#define PAGE_PARAMETER_RED_1 0x100
+#define PAGE_PARAMETER_RED_2 0x200
+
+#define ONFI_SIG_ADDR 0x20
+
+#define NAND_MAX_CHIPS 0x4
+#define NAND_MAX_OOBSZ 512
+#define NAND_MAX_PAGESZ 16384
+
+#define NAND_SMALL_PAGE_SIZE 0x200
+
+#define NAND_16_BIT 0x00000001
+
+#define NAND_ECC_NONE 0x0
+#define NAND_ECC_SOFT 0x1
+#define NAND_ECC_FULLHW 0x2
+#define NAND_ECC_PARTHW 0x4
+#define NAND_ECC_MODE_MASK 0x7
+
+#define ECC_OK 0
+#define ECC_CORRECTABLE 1
+#define ECC_ERROR_ECC (-1)
+#define ECC_UNCORRECTABLE (-2)
+
+#define NAND_MAN_SAMSUNG 0xec
+#define NAND_MAN_HYNIX 0xad
+#define NAND_MAN_STMICRO 0x20
+
+struct nand_id {
+ uint8_t man_id;
+ uint8_t dev_id;
+};
+
+struct nand_params {
+ struct nand_id id;
+ char *name;
+ uint32_t chip_size;
+ uint32_t page_size;
+ uint32_t oob_size;
+ uint32_t pages_per_block;
+ uint32_t flags;
+};
+
+/* nand debug levels */
+#define NDBG_NAND 0x01
+#define NDBG_CDEV 0x02
+#define NDBG_GEN 0x04
+#define NDBG_GEOM 0x08
+#define NDBG_BUS 0x10
+#define NDBG_SIM 0x20
+#define NDBG_CTRL 0x40
+#define NDBG_DRV 0x80
+#define NDBG_ECC 0x100
+
+/* nand_debug_function */
+void nand_debug(int level, const char *fmt, ...);
+extern int nand_debug_flag;
+
+/* ONFI features bit*/
+#define ONFI_FEAT_16BIT 0x01
+#define ONFI_FEAT_MULT_LUN 0x02
+#define ONFI_FEAT_INTLV_OPS 0x04
+#define ONFI_FEAT_CPBK_RESTRICT 0x08
+#define ONFI_FEAT_SRC_SYNCH 0x10
+
+/* ONFI optional commands bits */
+#define ONFI_OPTCOM_PROG_CACHE 0x01
+#define ONFI_OPTCOM_READ_CACHE 0x02
+#define ONFI_OPTCOM_GETSET_FEAT 0x04
+#define ONFI_OPTCOM_STATUS_ENH 0x08
+#define ONFI_OPTCOM_COPYBACK 0x10
+#define ONFI_OPTCOM_UNIQUE_ID 0x20
+
+
+/* Layout of parameter page is defined in ONFI */
+struct onfi_params {
+ char signature[4];
+ uint16_t rev;
+ uint16_t features;
+ uint16_t optional_commands;
+ uint8_t res1[22];
+ char manufacturer_name[12];
+ char device_model[20];
+ uint8_t manufacturer_id;
+ uint16_t date;
+ uint8_t res2[13];
+ uint32_t bytes_per_page;
+ uint16_t spare_bytes_per_page;
+ uint32_t bytes_per_partial_page;
+ uint16_t spare_bytes_per_partial_page;
+ uint32_t pages_per_block;
+ uint32_t blocks_per_lun;
+ uint8_t luns;
+ uint8_t address_cycles;
+ uint8_t bits_per_cell;
+ uint16_t max_bad_block_per_lun;
+ uint16_t block_endurance;
+ uint8_t guaranteed_valid_blocks;
+ uint16_t valid_block_endurance;
+ uint8_t programs_per_page;
+ uint8_t partial_prog_attr;
+ uint8_t bits_of_ecc;
+ uint8_t interleaved_addr_bits;
+ uint8_t interleaved_oper_attr;
+ uint8_t res3[13];
+ uint8_t pin_capacitance;
+ uint16_t asynch_timing_mode_support;
+ uint16_t asynch_prog_cache_timing_mode_support;
+ uint16_t t_prog; /* us, max page program time */
+ uint16_t t_bers; /* us, max block erase time */
+ uint16_t t_r; /* us, max page read time */
+ uint16_t t_ccs; /* ns, min change column setup time */
+ uint16_t source_synch_timing_mode_support;
+ uint8_t source_synch_feat;
+ uint16_t clk_input_capacitance;
+ uint16_t io_capacitance;
+ uint16_t input_capacitance;
+ uint8_t input_capacitance_max;
+ uint8_t driver_strength_support;
+ uint8_t res4[12];
+ uint16_t vendor_rev;
+ uint8_t vendor_spec[8];
+ uint16_t crc;
+};
+
+struct nand_ecc_data {
+ int eccsize; /* Number of data bytes per ECC step */
+ int eccmode;
+ int eccbytes; /* Number of ECC bytes per step */
+
+ uint16_t *eccpositions; /* Positions of ecc bytes */
+ uint8_t ecccalculated[NAND_MAX_OOBSZ];
+ uint8_t eccread[NAND_MAX_OOBSZ];
+};
+
+struct ecc_stat {
+ uint32_t ecc_succeded;
+ uint32_t ecc_corrected;
+ uint32_t ecc_failed;
+};
+
+struct page_stat {
+ struct ecc_stat ecc_stat;
+ uint32_t page_read;
+ uint32_t page_raw_read;
+ uint32_t page_written;
+ uint32_t page_raw_written;
+};
+
+struct block_stat {
+ uint32_t block_erased;
+};
+
+struct chip_geom {
+ uint32_t chip_size;
+ uint32_t block_size;
+ uint32_t page_size;
+ uint32_t oob_size;
+
+ uint32_t luns;
+ uint32_t blks_per_lun;
+ uint32_t blks_per_chip;
+ uint32_t pgs_per_blk;
+
+ uint32_t pg_mask;
+ uint32_t blk_mask;
+ uint32_t lun_mask;
+ uint8_t blk_shift;
+ uint8_t lun_shift;
+};
+
+struct nand_chip {
+ device_t dev;
+ struct nand_id id;
+ struct chip_geom chip_geom;
+
+ uint16_t t_prog; /* us, max page program time */
+ uint16_t t_bers; /* us, max block erase time */
+ uint16_t t_r; /* us, max page read time */
+ uint16_t t_ccs; /* ns, min change column setup time */
+ uint8_t num;
+ uint8_t flags;
+
+ struct page_stat *pg_stat;
+ struct block_stat *blk_stat;
+ struct nand_softc *nand;
+ struct nand_bbt *bbt;
+ struct nand_ops *ops;
+ struct cdev *cdev;
+
+ struct disk *ndisk;
+ struct disk *rdisk;
+ struct bio_queue_head bioq; /* bio queue */
+ struct mtx qlock; /* bioq lock */
+ struct taskqueue *tq; /* private task queue for i/o request */
+ struct task iotask; /* i/o processing */
+
+};
+
+struct nand_softc {
+ uint8_t flags;
+
+ char *chip_cdev_name;
+ struct nand_ecc_data ecc;
+};
+
+/* NAND ops */
+int nand_erase_blocks(struct nand_chip *chip, off_t offset, size_t len);
+int nand_prog_pages(struct nand_chip *chip, uint32_t offset, uint8_t *buf,
+ uint32_t len);
+int nand_read_pages(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_read_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_prog_pages_raw(struct nand_chip *chip, uint32_t offset, void *buf,
+ uint32_t len);
+int nand_read_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len);
+int nand_prog_oob(struct nand_chip *chip, uint32_t page, void *buf,
+ uint32_t len);
+
+int nand_select_cs(device_t dev, uint8_t cs);
+
+int nand_read_parameter(struct nand_softc *nand, struct onfi_params *param);
+int nand_synch_reset(struct nand_softc *nand);
+int nand_chng_read_col(device_t dev, uint32_t col, void *buf, size_t len);
+int nand_chng_write_col(device_t dev, uint32_t col, void *buf, size_t len);
+int nand_get_feature(device_t dev, uint8_t feat, void* buf);
+int nand_set_feature(device_t dev, uint8_t feat, void* buf);
+
+
+int nand_erase_block_intlv(device_t dev, uint32_t block);
+int nand_copyback_read(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_copyback_prog_intlv(device_t dev, uint32_t page);
+int nand_prog_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end);
+int nand_prog_intlv(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len);
+int nand_read_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end);
+
+int nand_write_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
+int nand_read_ecc(struct nand_softc *nand, uint32_t page, uint8_t *data);
+
+int nand_softecc_get(device_t dev, uint8_t *buf, int pagesize, uint8_t *ecc);
+int nand_softecc_correct(device_t dev, uint8_t *buf, int pagesize,
+ uint8_t *readecc, uint8_t *calcecc);
+
+/* Chip initialization */
+void nand_init(struct nand_softc *nand, device_t dev, int ecc_mode,
+ int ecc_bytes, int ecc_size, uint16_t* eccposition, char* cdev_name);
+void nand_detach(struct nand_softc *nand);
+struct nand_params *nand_get_params(struct nand_id *id);
+
+void nand_onfi_set_params(struct nand_chip *chip, struct onfi_params *params);
+void nand_set_params(struct nand_chip *chip, struct nand_params *params);
+int nand_init_stat(struct nand_chip *chip);
+void nand_destroy_stat(struct nand_chip *chip);
+
+/* BBT */
+int nand_init_bbt(struct nand_chip *chip);
+void nand_destroy_bbt(struct nand_chip *chip);
+int nand_update_bbt(struct nand_chip *chip);
+int nand_mark_bad_block(struct nand_chip* chip, uint32_t block_num);
+int nand_check_bad_block(struct nand_chip* chip, uint32_t block_num);
+
+/* cdev creation/removal */
+int nand_make_dev(struct nand_chip* chip);
+void nand_destroy_dev(struct nand_chip *chip);
+
+int create_geom_disk(struct nand_chip* chip);
+int create_geom_raw_disk(struct nand_chip *chip);
+void destroy_geom_disk(struct nand_chip *chip);
+void destroy_geom_raw_disk(struct nand_chip *chip);
+
+int init_chip_geom(struct chip_geom* cg, uint32_t luns, uint32_t blks_per_lun,
+ uint32_t pgs_per_blk, uint32_t pg_size, uint32_t oob_size);
+int nand_row_to_blkpg(struct chip_geom *cg, uint32_t row, uint32_t *lun,
+ uint32_t *blk, uint32_t *pg);
+int page_to_row(struct chip_geom *cg, uint32_t page, uint32_t *row);
+int nand_check_page_boundary(struct nand_chip *chip, uint32_t page);
+void nand_get_chip_param(struct nand_chip *chip, struct chip_param_io *param);
+
+#endif /* _DEV_NAND_H_ */
diff --git a/sys/dev/nand/nand_bbt.c b/sys/dev/nand/nand_bbt.c
new file mode 100644
index 0000000..d3f163a
--- /dev/null
+++ b/sys/dev/nand/nand_bbt.c
@@ -0,0 +1,273 @@
+/*-
+ * 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$
+ */
+
+#include <sys/cdefs.h>
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/bus.h>
+
+#include <dev/nand/nand.h>
+
+#include "nand_if.h"
+
+#define BBT_PRIMARY_PATTERN 0x01020304
+#define BBT_SECONDARY_PATTERN 0x05060708
+
+enum bbt_place {
+ BBT_NONE,
+ BBT_PRIMARY,
+ BBT_SECONDARY
+};
+
+struct nand_bbt {
+ struct nand_chip *chip;
+ uint32_t primary_map;
+ uint32_t secondary_map;
+ enum bbt_place active;
+ struct bbt_header *hdr;
+ uint32_t tab_len;
+ uint32_t *table;
+};
+
+struct bbt_header {
+ uint32_t pattern;
+ int32_t seq_nr;
+};
+
+static int nand_bbt_save(struct nand_bbt *);
+static int nand_bbt_load_hdr(struct nand_bbt *, struct bbt_header *, int8_t);
+static int nand_bbt_load_table(struct nand_bbt *);
+static int nand_bbt_prescan(struct nand_bbt *);
+
+int
+nand_init_bbt(struct nand_chip *chip)
+{
+ struct chip_geom *cg;
+ struct nand_bbt *bbt;
+ int err;
+
+ cg = &chip->chip_geom;
+
+ bbt = malloc(sizeof(struct nand_bbt), M_NAND, M_ZERO | M_WAITOK);
+ if (!bbt) {
+ device_printf(chip->dev,
+ "Cannot allocate memory for bad block struct");
+ return (ENOMEM);
+ }
+
+ bbt->chip = chip;
+ bbt->active = BBT_NONE;
+ bbt->primary_map = cg->chip_size - cg->block_size;
+ bbt->secondary_map = cg->chip_size - 2 * cg->block_size;
+ bbt->tab_len = cg->blks_per_chip * sizeof(uint32_t);
+ bbt->hdr = malloc(sizeof(struct bbt_header) + bbt->tab_len, M_NAND,
+ M_WAITOK);
+ if (!bbt->hdr) {
+ device_printf(chip->dev, "Cannot allocate %d bytes for BB "
+ "Table", bbt->tab_len);
+ free(bbt, M_NAND);
+ return (ENOMEM);
+ }
+ bbt->hdr->seq_nr = 0;
+ bbt->table = (uint32_t *)((uint8_t *)bbt->hdr +
+ sizeof(struct bbt_header));
+
+ err = nand_bbt_load_table(bbt);
+ if (err) {
+ free(bbt->table, M_NAND);
+ free(bbt, M_NAND);
+ return (err);
+ }
+
+ chip->bbt = bbt;
+ if (bbt->active == BBT_NONE) {
+ bbt->active = BBT_PRIMARY;
+ memset(bbt->table, 0xff, bbt->tab_len);
+ nand_bbt_prescan(bbt);
+ nand_bbt_save(bbt);
+ } else
+ device_printf(chip->dev, "Found BBT table for chip\n");
+
+ return (0);
+}
+
+void
+nand_destroy_bbt(struct nand_chip *chip)
+{
+
+ if (chip->bbt) {
+ nand_bbt_save(chip->bbt);
+
+ free(chip->bbt->hdr, M_NAND);
+ free(chip->bbt, M_NAND);
+ chip->bbt = NULL;
+ }
+}
+
+int
+nand_update_bbt(struct nand_chip *chip)
+{
+
+ nand_bbt_save(chip->bbt);
+
+ return (0);
+}
+
+static int
+nand_bbt_save(struct nand_bbt *bbt)
+{
+ enum bbt_place next;
+ uint32_t addr;
+ int32_t err;
+
+ if (bbt->active == BBT_PRIMARY) {
+ addr = bbt->secondary_map;
+ bbt->hdr->pattern = BBT_SECONDARY_PATTERN;
+ next = BBT_SECONDARY;
+ } else {
+ addr = bbt->primary_map;
+ bbt->hdr->pattern = BBT_PRIMARY_PATTERN;
+ next = BBT_PRIMARY;
+ }
+
+ err = nand_erase_blocks(bbt->chip, addr,
+ bbt->chip->chip_geom.block_size);
+ if (err)
+ return (err);
+
+ bbt->hdr->seq_nr++;
+
+ err = nand_prog_pages_raw(bbt->chip, addr, bbt->hdr,
+ bbt->tab_len + sizeof(struct bbt_header));
+ if (err)
+ return (err);
+
+ bbt->active = next;
+ return (0);
+}
+
+static int
+nand_bbt_load_hdr(struct nand_bbt *bbt, struct bbt_header *hdr, int8_t primary)
+{
+ uint32_t addr;
+
+ if (primary)
+ addr = bbt->primary_map;
+ else
+ addr = bbt->secondary_map;
+
+ return (nand_read_pages_raw(bbt->chip, addr, hdr,
+ sizeof(struct bbt_header)));
+}
+
+static int
+nand_bbt_load_table(struct nand_bbt *bbt)
+{
+ struct bbt_header hdr1, hdr2;
+ uint32_t address = 0;
+ int err = 0;
+
+ bzero(&hdr1, sizeof(hdr1));
+ bzero(&hdr2, sizeof(hdr2));
+
+ nand_bbt_load_hdr(bbt, &hdr1, 1);
+ if (hdr1.pattern == BBT_PRIMARY_PATTERN) {
+ bbt->active = BBT_PRIMARY;
+ address = bbt->primary_map;
+ } else
+ bzero(&hdr1, sizeof(hdr1));
+
+
+ nand_bbt_load_hdr(bbt, &hdr2, 0);
+ if ((hdr2.pattern == BBT_SECONDARY_PATTERN) &&
+ (hdr2.seq_nr > hdr1.seq_nr)) {
+ bbt->active = BBT_SECONDARY;
+ address = bbt->secondary_map;
+ } else
+ bzero(&hdr2, sizeof(hdr2));
+
+ if (bbt->active != BBT_NONE)
+ err = nand_read_pages_raw(bbt->chip, address, bbt->hdr,
+ bbt->tab_len + sizeof(struct bbt_header));
+
+ return (err);
+}
+
+static int
+nand_bbt_prescan(struct nand_bbt *bbt)
+{
+ int32_t i;
+ uint8_t bad;
+ bool printed_hash = 0;
+
+ device_printf(bbt->chip->dev, "No BBT found. Prescan chip...\n");
+ for (i = 0; i < bbt->chip->chip_geom.blks_per_chip; i++) {
+ if (NAND_IS_BLK_BAD(bbt->chip->dev, i, &bad))
+ return (ENXIO);
+
+ if (bad) {
+ device_printf(bbt->chip->dev, "Bad block(%d)\n", i);
+ bbt->table[i] = 0x0FFFFFFF;
+ }
+ if (!(i % 100)) {
+ printf("#");
+ printed_hash = 1;
+ }
+ }
+
+ if (printed_hash)
+ printf("\n");
+
+ return (0);
+}
+
+int
+nand_check_bad_block(struct nand_chip *chip, uint32_t block_number)
+{
+
+ if (!chip || !chip->bbt)
+ return (0);
+
+ if ((chip->bbt->table[block_number] & 0xF0000000) == 0)
+ return (1);
+
+ return (0);
+}
+
+int
+nand_mark_bad_block(struct nand_chip *chip, uint32_t block_number)
+{
+
+ chip->bbt->table[block_number] = 0x0FFFFFFF;
+
+ return (0);
+}
diff --git a/sys/dev/nand/nand_cdev.c b/sys/dev/nand/nand_cdev.c
new file mode 100644
index 0000000..ac27ff3
--- /dev/null
+++ b/sys/dev/nand/nand_cdev.c
@@ -0,0 +1,413 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_dev.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+static int nand_page_stat(struct nand_chip *, struct page_stat_io *);
+static int nand_block_stat(struct nand_chip *, struct block_stat_io *);
+
+static d_ioctl_t nand_ioctl;
+static d_open_t nand_open;
+static d_strategy_t nand_strategy;
+
+static struct cdevsw nand_cdevsw = {
+ .d_version = D_VERSION,
+ .d_name = "nand",
+ .d_open = nand_open,
+ .d_read = physread,
+ .d_write = physwrite,
+ .d_ioctl = nand_ioctl,
+ .d_strategy = nand_strategy,
+};
+
+static int
+offset_to_page(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset / cg->page_size);
+}
+
+static int
+offset_to_page_off(struct chip_geom *cg, uint32_t offset)
+{
+
+ return (offset % cg->page_size);
+}
+
+int
+nand_make_dev(struct nand_chip *chip)
+{
+ struct nandbus_ivar *ivar;
+ device_t parent, nandbus;
+ int parent_unit, unit;
+ char *name;
+
+ ivar = device_get_ivars(chip->dev);
+ nandbus = device_get_parent(chip->dev);
+
+ if (ivar->chip_cdev_name) {
+ name = ivar->chip_cdev_name;
+
+ /*
+ * If we got distinct name for chip device we can enumarete it
+ * based on contoller number.
+ */
+ parent = device_get_parent(nandbus);
+ } else {
+ name = "nand";
+ parent = nandbus;
+ }
+
+ parent_unit = device_get_unit(parent);
+ unit = parent_unit * 4 + chip->num;
+ chip->cdev = make_dev(&nand_cdevsw, unit, UID_ROOT, GID_WHEEL,
+ 0666, "%s%d.%d", name, parent_unit, chip->num);
+
+ if (chip->cdev == NULL)
+ return (ENXIO);
+
+ if (bootverbose)
+ device_printf(chip->dev, "Created cdev %s%d.%d for chip "
+ "[0x%0x, 0x%0x]\n", name, parent_unit, chip->num,
+ ivar->man_id, ivar->dev_id);
+
+ chip->cdev->si_drv1 = chip;
+
+ return (0);
+}
+
+void
+nand_destroy_dev(struct nand_chip *chip)
+{
+
+ if (chip->cdev)
+ destroy_dev(chip->cdev);
+}
+
+static int
+nand_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
+{
+
+ return (0);
+}
+
+static int
+nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ int start_page, count, off, err = 0;
+ uint8_t *ptr, *tmp;
+
+ nand_debug(NDBG_CDEV, "Read from chip%d [%p] at %d\n", chip->num,
+ chip, offset);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ cg = &chip->chip_geom;
+ start_page = offset_to_page(cg, offset);
+ off = offset_to_page_off(cg, offset);
+ count = (len > cg->page_size - off) ? cg->page_size - off : len;
+
+ ptr = (uint8_t *)buf;
+ while (len > 0) {
+ if (len < cg->page_size) {
+ tmp = malloc(cg->page_size, M_NAND, M_WAITOK);
+ if (!tmp) {
+ err = ENOMEM;
+ break;
+ }
+ err = NAND_READ_PAGE(chip->dev, start_page,
+ tmp, cg->page_size, 0);
+ if (err) {
+ free(tmp, M_NAND);
+ break;
+ }
+ bcopy(tmp + off, ptr, count);
+ free(tmp, M_NAND);
+ } else {
+ err = NAND_READ_PAGE(chip->dev, start_page,
+ ptr, cg->page_size, 0);
+ if (err)
+ break;
+ }
+
+ len -= count;
+ start_page++;
+ ptr += count;
+ count = (len > cg->page_size) ? cg->page_size : len;
+ off = 0;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+ return (err);
+}
+
+static int
+nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
+{
+ struct chip_geom *cg;
+ device_t nandbus;
+ int off, start_page, err = 0;
+ uint8_t *ptr;
+
+ nand_debug(NDBG_CDEV, "Write to chip %d [%p] at %d\n", chip->num,
+ chip, offset);
+
+ nandbus = device_get_parent(chip->dev);
+ NANDBUS_LOCK(nandbus);
+ NANDBUS_SELECT_CS(device_get_parent(chip->dev), chip->num);
+
+ cg = &chip->chip_geom;
+ start_page = offset_to_page(cg, offset);
+ off = offset_to_page_off(cg, offset);
+
+ if (off != 0 || (len % cg->page_size) != 0) {
+ printf("Not aligned write start [0x%08x] size [0x%08x]\n",
+ off, len);
+ NANDBUS_UNLOCK(nandbus);
+ return (EINVAL);
+ }
+
+ ptr = (uint8_t *)buf;
+ while (len > 0) {
+ err = NAND_PROGRAM_PAGE(chip->dev, start_page, ptr,
+ cg->page_size, 0);
+ if (err)
+ break;
+
+ len -= cg->page_size;
+ start_page++;
+ ptr += cg->page_size;
+ }
+
+ NANDBUS_UNLOCK(nandbus);
+ return (err);
+}
+
+static void
+nand_strategy(struct bio *bp)
+{
+ struct nand_chip *chip;
+ struct cdev *dev;
+ int err = 0;
+
+ dev = bp->bio_dev;
+ chip = dev->si_drv1;
+
+ nand_debug(NDBG_CDEV, "Strategy %s on chip %d [%p]\n",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" : "WRITE",
+ chip->num, chip);
+
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else {
+ err = nand_write(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+
+ if (err == 0)
+ bp->bio_resid = 0;
+ else {
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+ }
+
+ biodone(bp);
+}
+
+static int
+nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
+ uint32_t len, uint8_t *data, uint8_t write)
+{
+ struct chip_geom *cg;
+ uint8_t *buf = NULL;
+ int ret = 0;
+
+ cg = &chip->chip_geom;
+
+ buf = malloc(cg->oob_size, M_NAND, M_WAITOK);
+ if (!buf)
+ return (ENOMEM);
+
+ memset(buf, 0xff, cg->oob_size);
+
+ if (!write) {
+ ret = nand_read_oob(chip, page, buf, cg->oob_size);
+ copyout(buf, data, len);
+ } else {
+ copyin(data, buf, len);
+ ret = nand_prog_oob(chip, page, buf, cg->oob_size);
+ }
+
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static int
+nand_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
+ struct thread *td)
+{
+ struct nand_chip *chip;
+ struct nand_oob_rw *oob_rw = NULL;
+ struct nand_raw_rw *raw_rw = NULL;
+ device_t nandbus;
+ uint8_t *buf = NULL;
+ int ret = 0;
+ uint8_t status;
+
+ chip = (struct nand_chip *)dev->si_drv1;
+ nandbus = device_get_parent(chip->dev);
+
+ if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
+ raw_rw = (struct nand_raw_rw *)data;
+ buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+ }
+ switch(cmd) {
+ case NAND_IO_ERASE:
+ ret = nand_erase_blocks(chip, ((off_t *)data)[0],
+ ((off_t *)data)[1]);
+ break;
+
+ case NAND_IO_OOB_READ:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 0);
+ break;
+
+ case NAND_IO_OOB_PROG:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 1);
+ break;
+
+ case NAND_IO_GET_STATUS:
+ NANDBUS_LOCK(nandbus);
+ ret = NANDBUS_GET_STATUS(nandbus, &status);
+ if (ret == 0)
+ *(uint8_t *)data = status;
+ NANDBUS_UNLOCK(nandbus);
+ break;
+
+ case NAND_IO_RAW_PROG:
+ ret = copyin(raw_rw->data, buf, raw_rw->len);
+ if (ret)
+ break;
+ ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ break;
+
+ case NAND_IO_RAW_READ:
+ ret = nand_read_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ if (ret)
+ break;
+ ret = copyout(buf, raw_rw->data, raw_rw->len);
+ break;
+
+ case NAND_IO_PAGE_STAT:
+ ret = nand_page_stat(chip, (struct page_stat_io *)data);
+ break;
+
+ case NAND_IO_BLOCK_STAT:
+ ret = nand_block_stat(chip, (struct block_stat_io *)data);
+ break;
+
+ case NAND_IO_GET_CHIP_PARAM:
+ nand_get_chip_param(chip, (struct chip_param_io *)data);
+ break;
+
+ default:
+ printf("Unknown nand_ioctl request \n");
+ ret = EIO;
+ }
+
+ if (buf)
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static int
+nand_page_stat(struct nand_chip *chip, struct page_stat_io *page_stat)
+{
+ struct chip_geom *cg;
+ struct page_stat *stat;
+ int num_pages;
+
+ cg = &chip->chip_geom;
+ num_pages = cg->pgs_per_blk * cg->blks_per_lun * cg->luns;
+ if (page_stat->page_num >= num_pages)
+ return (EINVAL);
+
+ stat = &chip->pg_stat[page_stat->page_num];
+ page_stat->page_read = stat->page_read;
+ page_stat->page_written = stat->page_written;
+ page_stat->page_raw_read = stat->page_raw_read;
+ page_stat->page_raw_written = stat->page_raw_written;
+ page_stat->ecc_succeded = stat->ecc_stat.ecc_succeded;
+ page_stat->ecc_corrected = stat->ecc_stat.ecc_corrected;
+ page_stat->ecc_failed = stat->ecc_stat.ecc_failed;
+
+ return (0);
+}
+
+static int
+nand_block_stat(struct nand_chip *chip, struct block_stat_io *block_stat)
+{
+ struct chip_geom *cg;
+ uint32_t block_num = block_stat->block_num;
+
+ cg = &chip->chip_geom;
+ if (block_num >= cg->blks_per_lun * cg->luns)
+ return (EINVAL);
+
+ block_stat->block_erased = chip->blk_stat[block_num].block_erased;
+
+ return (0);
+}
diff --git a/sys/dev/nand/nand_dev.h b/sys/dev/nand/nand_dev.h
new file mode 100644
index 0000000..bc7d6c4
--- /dev/null
+++ b/sys/dev/nand/nand_dev.h
@@ -0,0 +1,90 @@
+/*-
+ * 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$
+ */
+
+#ifndef _DEV_NAND_CDEV_H_
+#define _DEV_NAND_CDEV_H_
+
+#include <sys/ioccom.h>
+#include <sys/param.h>
+
+struct nand_raw_rw {
+ off_t off;
+ off_t len;
+ uint8_t *data;
+};
+
+struct nand_oob_rw {
+ uint32_t page;
+ off_t len;
+ uint8_t *data;
+};
+
+#define NAND_IOCTL_GROUP 'N'
+#define NAND_IO_ERASE _IOWR(NAND_IOCTL_GROUP, 0x0, off_t[2])
+
+#define NAND_IO_OOB_READ _IOWR(NAND_IOCTL_GROUP, 0x1, struct nand_oob_rw)
+
+#define NAND_IO_OOB_PROG _IOWR(NAND_IOCTL_GROUP, 0x2, struct nand_oob_rw)
+
+#define NAND_IO_RAW_READ _IOWR(NAND_IOCTL_GROUP, 0x3, struct nand_raw_rw)
+
+#define NAND_IO_RAW_PROG _IOWR(NAND_IOCTL_GROUP, 0x4, struct nand_raw_rw)
+
+#define NAND_IO_GET_STATUS _IOWR(NAND_IOCTL_GROUP, 0x5, uint8_t)
+
+struct page_stat_io {
+ uint32_t page_num;
+ uint32_t page_read;
+ uint32_t page_written;
+ uint32_t page_raw_read;
+ uint32_t page_raw_written;
+ uint32_t ecc_succeded;
+ uint32_t ecc_corrected;
+ uint32_t ecc_failed;
+};
+#define NAND_IO_PAGE_STAT _IOWR(NAND_IOCTL_GROUP, 0x6, \
+ struct page_stat_io)
+
+struct block_stat_io {
+ uint32_t block_num;
+ uint32_t block_erased;
+};
+#define NAND_IO_BLOCK_STAT _IOWR(NAND_IOCTL_GROUP, 0x7, \
+ struct block_stat_io)
+
+struct chip_param_io {
+ uint32_t page_size;
+ uint32_t oob_size;
+
+ uint32_t blocks;
+ uint32_t pages_per_block;
+};
+#define NAND_IO_GET_CHIP_PARAM _IOWR(NAND_IOCTL_GROUP, 0x8, \
+ struct chip_param_io)
+
+#endif /* _DEV_NAND_CDEV_H_ */
diff --git a/sys/dev/nand/nand_ecc_pos.h b/sys/dev/nand/nand_ecc_pos.h
new file mode 100644
index 0000000..f40415c
--- /dev/null
+++ b/sys/dev/nand/nand_ecc_pos.h
@@ -0,0 +1,56 @@
+/*-
+ * 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$
+ */
+
+#ifndef _DEV_NAND_ECC_POS_H_
+#define _DEV_NAND_ECC_POS_H_
+
+static uint16_t default_software_ecc_positions_16[] = {2, 0, 1, 7, 4, 6};
+
+static uint16_t default_software_ecc_positions_64[] = {
+
+ 42, 40, 41, 45, 43, 44, 48, 46,
+ 47, 51, 49, 50, 54, 52, 53, 57,
+ 55, 56, 60, 58, 59, 63, 61, 62
+};
+
+static uint16_t default_software_ecc_positions_128[] = {
+ 8, 9, 10, 11, 12, 13,
+ 18, 19, 20, 21, 22, 23,
+ 28, 29, 30, 31, 32, 33,
+ 38, 39, 40, 41, 42, 43,
+ 48, 49, 50, 51, 52, 53,
+ 58, 59, 60, 61, 62, 63,
+ 68, 69, 70, 71, 72, 73,
+ 78, 79, 80, 81, 82, 83,
+ 88, 89, 90, 91, 92, 93,
+ 98, 99, 100, 101, 102, 103,
+ 108, 109, 110, 111, 112, 113,
+ 118, 119, 120, 121, 122, 123,
+};
+#endif /* _DEV_NAND_ECC_POS_H_ */
+
diff --git a/sys/dev/nand/nand_generic.c b/sys/dev/nand/nand_generic.c
new file mode 100644
index 0000000..85e81be
--- /dev/null
+++ b/sys/dev/nand/nand_generic.c
@@ -0,0 +1,1320 @@
+/*-
+ * 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.
+ */
+
+/* Generic NAND driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include "nfc_if.h"
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+
+static int onfi_nand_probe(device_t dev);
+static int large_nand_probe(device_t dev);
+static int small_nand_probe(device_t dev);
+static int generic_nand_attach(device_t dev);
+static int generic_nand_detach(device_t dev);
+
+static int generic_erase_block(device_t, uint32_t);
+static int generic_erase_block_intlv(device_t, uint32_t);
+static int generic_read_page (device_t, uint32_t, void *, uint32_t, uint32_t);
+static int generic_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int generic_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int generic_program_page_intlv(device_t, uint32_t, void *, uint32_t,
+ uint32_t);
+static int generic_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int generic_is_blk_bad(device_t, uint32_t, uint8_t *);
+static int generic_get_ecc(device_t, void *, void *, int *);
+static int generic_correct_ecc(device_t, void *, void *, void *);
+
+static int small_read_page(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int small_read_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int small_program_page(device_t, uint32_t, void *, uint32_t, uint32_t);
+static int small_program_oob(device_t, uint32_t, void *, uint32_t, uint32_t);
+
+static int onfi_is_blk_bad(device_t, uint32_t, uint8_t *);
+static int onfi_read_parameter(struct nand_chip *, struct onfi_params *);
+
+static int nand_send_address(device_t, int32_t, int32_t, int8_t);
+
+static device_method_t onand_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, onfi_nand_probe),
+ DEVMETHOD(device_attach, generic_nand_attach),
+ DEVMETHOD(device_detach, generic_nand_detach),
+
+ DEVMETHOD(nand_read_page, generic_read_page),
+ DEVMETHOD(nand_program_page, generic_program_page),
+ DEVMETHOD(nand_program_page_intlv, generic_program_page_intlv),
+ DEVMETHOD(nand_read_oob, generic_read_oob),
+ DEVMETHOD(nand_program_oob, generic_program_oob),
+ DEVMETHOD(nand_erase_block, generic_erase_block),
+ DEVMETHOD(nand_erase_block_intlv, generic_erase_block_intlv),
+
+ DEVMETHOD(nand_is_blk_bad, onfi_is_blk_bad),
+ DEVMETHOD(nand_get_ecc, generic_get_ecc),
+ DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
+ { 0, 0 }
+};
+
+static device_method_t lnand_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, large_nand_probe),
+ DEVMETHOD(device_attach, generic_nand_attach),
+ DEVMETHOD(device_detach, generic_nand_detach),
+
+ DEVMETHOD(nand_read_page, generic_read_page),
+ DEVMETHOD(nand_program_page, generic_program_page),
+ DEVMETHOD(nand_read_oob, generic_read_oob),
+ DEVMETHOD(nand_program_oob, generic_program_oob),
+ DEVMETHOD(nand_erase_block, generic_erase_block),
+
+ DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad),
+ DEVMETHOD(nand_get_ecc, generic_get_ecc),
+ DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
+ { 0, 0 }
+};
+
+static device_method_t snand_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, small_nand_probe),
+ DEVMETHOD(device_attach, generic_nand_attach),
+ DEVMETHOD(device_detach, generic_nand_detach),
+
+ DEVMETHOD(nand_read_page, small_read_page),
+ DEVMETHOD(nand_program_page, small_program_page),
+ DEVMETHOD(nand_read_oob, small_read_oob),
+ DEVMETHOD(nand_program_oob, small_program_oob),
+ DEVMETHOD(nand_erase_block, generic_erase_block),
+
+ DEVMETHOD(nand_is_blk_bad, generic_is_blk_bad),
+ DEVMETHOD(nand_get_ecc, generic_get_ecc),
+ DEVMETHOD(nand_correct_ecc, generic_correct_ecc),
+ { 0, 0 }
+};
+
+devclass_t onand_devclass;
+devclass_t lnand_devclass;
+devclass_t snand_devclass;
+
+driver_t onand_driver = {
+ "onand",
+ onand_methods,
+ sizeof(struct nand_chip)
+};
+
+driver_t lnand_driver = {
+ "lnand",
+ lnand_methods,
+ sizeof(struct nand_chip)
+};
+
+driver_t snand_driver = {
+ "snand",
+ snand_methods,
+ sizeof(struct nand_chip)
+};
+
+DRIVER_MODULE(onand, nandbus, onand_driver, onand_devclass, 0, 0);
+DRIVER_MODULE(lnand, nandbus, lnand_driver, lnand_devclass, 0, 0);
+DRIVER_MODULE(snand, nandbus, snand_driver, snand_devclass, 0, 0);
+
+static int
+onfi_nand_probe(device_t dev)
+{
+ struct nandbus_ivar *ivar;
+
+ ivar = device_get_ivars(dev);
+ if (ivar && ivar->is_onfi) {
+ device_set_desc(dev, "ONFI compliant NAND");
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENODEV);
+}
+
+static int
+large_nand_probe(device_t dev)
+{
+ struct nandbus_ivar *ivar;
+
+ ivar = device_get_ivars(dev);
+ if (ivar && !ivar->is_onfi && ivar->params->page_size >= 512) {
+ device_set_desc(dev, ivar->params->name);
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENODEV);
+}
+
+static int
+small_nand_probe(device_t dev)
+{
+ struct nandbus_ivar *ivar;
+
+ ivar = device_get_ivars(dev);
+ if (ivar && !ivar->is_onfi && ivar->params->page_size == 512) {
+ device_set_desc(dev, ivar->params->name);
+ return (BUS_PROBE_DEFAULT);
+ }
+
+ return (ENODEV);
+}
+
+static int
+generic_nand_attach(device_t dev)
+{
+ struct nand_chip *chip;
+ struct nandbus_ivar *ivar;
+ struct onfi_params *onfi_params;
+ device_t nandbus, nfc;
+ int err;
+
+ chip = device_get_softc(dev);
+ chip->dev = dev;
+
+ ivar = device_get_ivars(dev);
+ chip->id.man_id = ivar->man_id;
+ chip->id.dev_id = ivar->dev_id;
+ chip->num = ivar->cs;
+
+ /* TODO remove when HW ECC supported */
+ nandbus = device_get_parent(dev);
+ nfc = device_get_parent(nandbus);
+
+ chip->nand = device_get_softc(nfc);
+
+ if (ivar->is_onfi) {
+ onfi_params = malloc(sizeof(struct onfi_params),
+ M_NAND, M_WAITOK | M_ZERO);
+ if (onfi_params == NULL)
+ return (ENXIO);
+
+ if (onfi_read_parameter(chip, onfi_params)) {
+ nand_debug(NDBG_GEN,"Could not read parameter page!\n");
+ free(onfi_params, M_NAND);
+ return (ENXIO);
+ }
+
+ nand_onfi_set_params(chip, onfi_params);
+ /* Set proper column and row cycles */
+ ivar->cols = (onfi_params->address_cycles >> 4) & 0xf;
+ ivar->rows = onfi_params->address_cycles & 0xf;
+ free(onfi_params, M_NAND);
+
+ } else {
+
+ nand_set_params(chip, ivar->params);
+ }
+
+ err = nand_init_stat(chip);
+ if (err) {
+ generic_nand_detach(dev);
+ return (err);
+ }
+
+ err = nand_init_bbt(chip);
+ if (err) {
+ generic_nand_detach(dev);
+ return (err);
+ }
+
+ err = nand_make_dev(chip);
+ if (err) {
+ generic_nand_detach(dev);
+ return (err);
+ }
+
+ err = create_geom_disk(chip);
+ if (err) {
+ generic_nand_detach(dev);
+ return (err);
+ }
+
+ return (0);
+}
+
+static int
+generic_nand_detach(device_t dev)
+{
+ struct nand_chip *chip;
+
+ chip = device_get_softc(dev);
+
+ nand_destroy_bbt(chip);
+ destroy_geom_disk(chip);
+ nand_destroy_dev(chip);
+ nand_destroy_stat(chip);
+
+ return (0);
+}
+
+static int
+can_write(device_t nandbus)
+{
+ uint8_t status;
+
+ if (NANDBUS_WAIT_READY(nandbus, &status))
+ return (0);
+
+ if (!(status & NAND_STATUS_WP)) {
+ nand_debug(NDBG_GEN,"Chip is write-protected");
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+check_fail(device_t nandbus)
+{
+ uint8_t status;
+
+ NANDBUS_WAIT_READY(nandbus, &status);
+ if (status & NAND_STATUS_FAIL) {
+ nand_debug(NDBG_GEN,"Status failed %x", status);
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+onfi_read_parameter(struct nand_chip *chip, struct onfi_params *params)
+{
+ device_t nandbus;
+
+ nand_debug(NDBG_GEN,"read parameter");
+
+ nandbus = device_get_parent(chip->dev);
+
+ NANDBUS_SELECT_CS(nandbus, chip->num);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ_PARAMETER))
+ return (ENXIO);
+
+ if (nand_send_address(chip->dev, -1, -1, PAGE_PARAMETER_DEF))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ NANDBUS_READ_BUFFER(nandbus, params, sizeof(struct onfi_params));
+
+ /* TODO */
+ /* Check for signature */
+ /* Check CRC */
+ /* Use redundant page if necessary */
+
+ return (0);
+}
+
+static int
+send_read_page(device_t nand, uint8_t start_command, uint8_t end_command,
+ uint32_t row, uint32_t column)
+{
+ device_t nandbus = device_get_parent(nand);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, start_command))
+ return (ENXIO);
+
+ if (nand_send_address(nand, row, column, -1))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, end_command))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+generic_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p raw read page %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
+ offset))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_read++;
+
+ return (0);
+}
+
+static int
+generic_read_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p raw read oob %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page)) {
+ nand_debug(NDBG_GEN,"page boundary check failed: %08x\n", page);
+ return (ENXIO);
+ }
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ offset += chip->chip_geom.page_size;
+
+ if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_END, row,
+ offset))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+send_start_program_page(device_t nand, uint32_t row, uint32_t column)
+{
+ device_t nandbus = device_get_parent(nand);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_PROG))
+ return (ENXIO);
+
+ if (nand_send_address(nand, row, column, -1))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+send_end_program_page(device_t nandbus, uint8_t end_command)
+{
+
+ if (NANDBUS_SEND_COMMAND(nandbus, end_command))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+generic_program_page(device_t nand, uint32_t page, void *buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len,
+ offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (send_start_program_page(nand, row, offset))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_written++;
+
+ return (0);
+}
+
+static int
+generic_program_page_intlv(device_t nand, uint32_t page, void *buf,
+ uint32_t len, uint32_t offset)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p raw prog page %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (send_start_program_page(nand, row, offset))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_written++;
+
+ return (0);
+}
+
+static int
+generic_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p raw prog oob %x[%x] at %x", nand, page, len,
+ offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+ offset += chip->chip_geom.page_size;
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (send_start_program_page(nand, row, offset))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+send_erase_block(device_t nand, uint32_t row, uint8_t second_command)
+{
+ device_t nandbus = device_get_parent(nand);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_ERASE))
+ return (ENXIO);
+
+ if (nand_send_address(nand, row, -1, -1))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, second_command))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+generic_erase_block(device_t nand, uint32_t block)
+{
+ struct block_stat *blk_stat;
+ struct nand_chip *chip;
+ device_t nandbus;
+ int row;
+
+ nand_debug(NDBG_GEN,"%p erase block %x", nand, block);
+ nandbus = device_get_parent(nand);
+ chip = device_get_softc(nand);
+
+ if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
+ return (ENXIO);
+
+ row = (block << chip->chip_geom.blk_shift) &
+ chip->chip_geom.blk_mask;
+
+ nand_debug(NDBG_GEN,"%p erase block row %x", nand, row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ send_erase_block(nand, row, NAND_CMD_ERASE_END);
+
+ DELAY(chip->t_bers);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ blk_stat = &(chip->blk_stat[block]);
+ blk_stat->block_erased++;
+
+ return (0);
+}
+
+static int
+generic_erase_block_intlv(device_t nand, uint32_t block)
+{
+ struct block_stat *blk_stat;
+ struct nand_chip *chip;
+ device_t nandbus;
+ int row;
+
+ nand_debug(NDBG_GEN,"%p erase block %x", nand, block);
+ nandbus = device_get_parent(nand);
+ chip = device_get_softc(nand);
+
+ if (block >= (chip->chip_geom.blks_per_lun * chip->chip_geom.luns))
+ return (ENXIO);
+
+ row = (block << chip->chip_geom.blk_shift) &
+ chip->chip_geom.blk_mask;
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ send_erase_block(nand, row, NAND_CMD_ERASE_INTLV);
+
+ DELAY(chip->t_bers);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ blk_stat = &(chip->blk_stat[block]);
+ blk_stat->block_erased++;
+
+ return (0);
+
+}
+
+static int
+onfi_is_blk_bad(device_t device, uint32_t block_number, uint8_t *bad)
+{
+ struct nand_chip *chip;
+ int page_number, i, j, err;
+ uint8_t *oob;
+
+ chip = device_get_softc(device);
+
+ oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
+ if (!oob) {
+ device_printf(device, "%s: cannot allocate oob\n", __func__);
+ return (ENOMEM);
+ }
+
+ page_number = block_number * chip->chip_geom.pgs_per_blk;
+ *bad = 0;
+ /* Check OOB of first and last page */
+ for (i = 0; i < 2; i++, page_number+= chip->chip_geom.pgs_per_blk - 1) {
+ err = generic_read_oob(device, page_number, oob,
+ chip->chip_geom.oob_size, 0);
+ if (err) {
+ device_printf(device, "%s: cannot allocate oob\n",
+ __func__);
+ free(oob, M_NAND);
+ return (ENOMEM);
+ }
+
+ for (j = 0; j < chip->chip_geom.oob_size; j++) {
+ if (!oob[j]) {
+ *bad = 1;
+ free(oob, M_NAND);
+ return (0);
+ }
+ }
+ }
+
+ free(oob, M_NAND);
+
+ return (0);
+}
+
+static int
+send_small_read_page(device_t nand, uint8_t start_command,
+ uint32_t row, uint32_t column)
+{
+ device_t nandbus = device_get_parent(nand);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, start_command))
+ return (ENXIO);
+
+ if (nand_send_address(nand, row, column, -1))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+
+static int
+small_read_page(device_t nand, uint32_t page, void *buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p small read page %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (offset < 256) {
+ if (send_small_read_page(nand, NAND_CMD_SMALLA, row, offset))
+ return (ENXIO);
+ } else {
+ offset -= 256;
+ if (send_small_read_page(nandbus, NAND_CMD_SMALLB, row, offset))
+ return (ENXIO);
+ }
+
+ DELAY(chip->t_r);
+
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_read++;
+
+ return (0);
+}
+
+static int
+small_read_oob(device_t nand, uint32_t page, void *buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p small read oob %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (send_small_read_page(nand, NAND_CMD_SMALLOOB, row, 0))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_read++;
+
+ return (0);
+}
+
+static int
+small_program_page(device_t nand, uint32_t page, void* buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p small prog page %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (offset < 256) {
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLA))
+ return (ENXIO);
+ } else {
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLB))
+ return (ENXIO);
+ }
+
+ if (send_start_program_page(nand, row, offset))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+static int
+small_program_oob(device_t nand, uint32_t page, void* buf, uint32_t len,
+ uint32_t offset)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"%p small prog oob %x[%x] at %x", nand, page, len, offset);
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SMALLOOB))
+ return (ENXIO);
+
+ if (send_start_program_page(nand, row, offset))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+int
+nand_send_address(device_t nand, int32_t row, int32_t col, int8_t id)
+{
+ struct nandbus_ivar *ivar;
+ device_t nandbus;
+ uint8_t addr;
+ int err = 0;
+ int i;
+
+ nandbus = device_get_parent(nand);
+ ivar = device_get_ivars(nand);
+
+ if (id != -1) {
+ nand_debug(NDBG_GEN,"send_address: send id %02x", id);
+ err = NANDBUS_SEND_ADDRESS(nandbus, id);
+ }
+
+ if (!err && col != -1) {
+ for (i = 0; i < ivar->cols; i++, col >>= 8) {
+ addr = (uint8_t)(col & 0xff);
+ nand_debug(NDBG_GEN,"send_address: send address column "
+ "%02x", addr);
+ err = NANDBUS_SEND_ADDRESS(nandbus, addr);
+ if (err)
+ break;
+ }
+ }
+
+ if (!err && row != -1) {
+ for (i = 0; i < ivar->rows; i++, row >>= 8) {
+ addr = (uint8_t)(row & 0xff);
+ nand_debug(NDBG_GEN,"send_address: send address row "
+ "%02x", addr);
+ err = NANDBUS_SEND_ADDRESS(nandbus, addr);
+ if (err)
+ break;
+ }
+ }
+
+ return (err);
+}
+
+static int
+generic_is_blk_bad(device_t dev, uint32_t block, uint8_t *bad)
+{
+ struct nand_chip *chip;
+ int page_number, err, i;
+ uint8_t *oob;
+
+ chip = device_get_softc(dev);
+
+ oob = malloc(chip->chip_geom.oob_size, M_NAND, M_WAITOK);
+ if (!oob) {
+ device_printf(dev, "%s: cannot allocate OOB\n", __func__);
+ return (ENOMEM);
+ }
+
+ page_number = block * chip->chip_geom.pgs_per_blk;
+ *bad = 0;
+
+ /* Check OOB of first and second page */
+ for (i = 0; i < 2; i++) {
+ err = NAND_READ_OOB(dev, page_number + i, oob,
+ chip->chip_geom.oob_size, 0);
+ if (err) {
+ device_printf(dev, "%s: cannot allocate OOB\n",
+ __func__);
+ free(oob, M_NAND);
+ return (ENOMEM);
+ }
+
+ if (!oob[0]) {
+ *bad = 1;
+ free(oob, M_NAND);
+ return (0);
+ }
+ }
+
+ free(oob, M_NAND);
+
+ return (0);
+}
+
+static int
+generic_get_ecc(device_t dev, void *buf, void *ecc, int *needwrite)
+{
+ struct nand_chip *chip = device_get_softc(dev);
+ struct chip_geom *cg = &chip->chip_geom;
+
+ return (NANDBUS_GET_ECC(device_get_parent(dev), buf, cg->page_size,
+ ecc, needwrite));
+}
+
+static int
+generic_correct_ecc(device_t dev, void *buf, void *readecc, void *calcecc)
+{
+ struct nand_chip *chip = device_get_softc(dev);
+ struct chip_geom *cg = &chip->chip_geom;
+
+ return (NANDBUS_CORRECT_ECC(device_get_parent(dev), buf,
+ cg->page_size, readecc, calcecc));
+}
+
+
+#if 0
+int
+nand_chng_read_col(device_t nand, uint32_t col, void *buf, size_t len)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+
+ chip = device_get_softc(nand);
+ nandbus = device_get_parent(nand);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ if (buf != NULL && len > 0)
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ return (0);
+}
+
+int
+nand_chng_write_col(device_t dev, uint32_t col, void *buf,
+ size_t len)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, -1, col, -1))
+ return (ENXIO);
+
+ if (buf != NULL && len > 0)
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_READ_COL_END))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+
+int
+nand_copyback_read(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN," raw read page %x[%x] at %x", page, col, len);
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (send_read_page(nand, NAND_CMD_READ, NAND_CMD_READ_CPBK, row, 0))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ if (buf != NULL && len > 0)
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_read++;
+
+ return (0);
+}
+
+int
+nand_copyback_prog(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"copyback prog page %x[%x]", page, len);
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_CHNG_WRITE_COL))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
+ return (ENXIO);
+
+ if (buf != NULL && len > 0)
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_END))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_written++;
+
+ return (0);
+}
+
+int
+nand_copyback_prog_intlv(device_t dev, uint32_t page)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+
+ nand_debug(NDBG_GEN,"cache prog page %x", page);
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (send_start_program_page(nand, row, 0))
+ return (ENXIO);
+
+ if (send_end_program_page(nandbus, NAND_CMD_PROG_INTLV))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_written++;
+
+ return (0);
+}
+
+int
+nand_prog_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+ uint8_t command;
+
+ nand_debug(NDBG_GEN,"cache prog page %x[%x]", page, len);
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (!can_write(nandbus))
+ return (ENXIO);
+
+ if (send_start_program_page(dev, row, 0))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, len);
+
+ if (end)
+ command = NAND_CMD_PROG_END;
+ else
+ command = NAND_CMD_PROG_CACHE;
+
+ if (send_end_program_page(nandbus, command))
+ return (ENXIO);
+
+ DELAY(chip->t_prog);
+
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_written++;
+
+ return (0);
+}
+
+int
+nand_read_cache(device_t dev, uint32_t page, uint32_t col,
+ void *buf, size_t len, uint8_t end)
+{
+ struct nand_chip *chip;
+ struct page_stat *pg_stat;
+ device_t nandbus;
+ uint32_t row;
+ uint8_t command;
+
+ nand_debug(NDBG_GEN,"cache read page %x[%x] ", page, len);
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (nand_check_page_boundary(chip, page))
+ return (ENXIO);
+
+ page_to_row(&chip->chip_geom, page, &row);
+
+ if (page != -1) {
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_READ))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, row, col, -1))
+ return (ENXIO);
+ }
+
+ if (end)
+ command = NAND_CMD_READ_CACHE_END;
+ else
+ command = NAND_CMD_READ_CACHE;
+
+ if (NANDBUS_SEND_COMMAND(nandbus, command))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+ if (check_fail(nandbus))
+ return (ENXIO);
+
+ if (buf != NULL && len > 0)
+ NANDBUS_READ_BUFFER(nandbus, buf, len);
+
+ pg_stat = &(chip->pg_stat[page]);
+ pg_stat->page_raw_read++;
+
+ return (0);
+}
+
+int
+nand_get_feature(device_t dev, uint8_t feat, void *buf)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+
+ nand_debug(NDBG_GEN,"nand get feature");
+
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_GET_FEATURE))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
+ return (ENXIO);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ DELAY(chip->t_r);
+ NANDBUS_READ_BUFFER(nandbus, buf, 4);
+
+ return (0);
+}
+
+int
+nand_set_feature(device_t dev, uint8_t feat, void *buf)
+{
+ struct nand_chip *chip;
+ device_t nandbus;
+
+ nand_debug(NDBG_GEN,"nand set feature");
+
+ chip = device_get_softc(dev);
+ nandbus = device_get_parent(dev);
+
+ if (NANDBUS_SEND_COMMAND(nandbus, NAND_CMD_SET_FEATURE))
+ return (ENXIO);
+
+ if (NANDBUS_SEND_ADDRESS(nandbus, -1, -1, feat))
+ return (ENXIO);
+
+ NANDBUS_WRITE_BUFFER(nandbus, buf, 4);
+
+ if (NANDBUS_START_COMMAND(nandbus))
+ return (ENXIO);
+
+ return (0);
+}
+#endif
diff --git a/sys/dev/nand/nand_geom.c b/sys/dev/nand/nand_geom.c
new file mode 100644
index 0000000..a8bdba2
--- /dev/null
+++ b/sys/dev/nand/nand_geom.c
@@ -0,0 +1,414 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/bio.h>
+#include <geom/geom.h>
+#include <geom/geom_disk.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nand_dev.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+
+#define BIO_NAND_STD ((void *)1)
+#define BIO_NAND_RAW ((void *)2)
+
+static disk_ioctl_t nand_ioctl;
+static disk_getattr_t nand_getattr;
+static disk_strategy_t nand_strategy;
+static disk_strategy_t nand_strategy_raw;
+
+static int
+nand_read(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Read from chip %d [%p] at %d", chip->num, chip,
+ offset);
+
+ return (nand_read_pages(chip, offset, buf, len));
+}
+
+static int
+nand_write(struct nand_chip *chip, uint32_t offset, void* buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Write to chip %d [%p] at %d", chip->num, chip,
+ offset);
+
+ return (nand_prog_pages(chip, offset, buf, len));
+}
+
+static int
+nand_read_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+ nand_debug(NDBG_GEOM, "Raw read from chip %d [%p] at %d", chip->num,
+ chip, offset);
+
+ return (nand_read_pages_raw(chip, offset, buf, len));
+}
+
+static int
+nand_write_raw(struct nand_chip *chip, uint32_t offset, void *buf, uint32_t len)
+{
+
+ nand_debug(NDBG_GEOM, "Raw write to chip %d [%p] at %d", chip->num,
+ chip, offset);
+
+ return (nand_prog_pages_raw(chip, offset, buf, len));
+}
+
+static void
+nand_strategy(struct bio *bp)
+{
+ struct nand_chip *chip;
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+
+ bp->bio_driver1 = BIO_NAND_STD;
+
+ nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
+ ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
+ ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
+ chip->num, chip);
+
+ mtx_lock(&chip->qlock);
+ bioq_insert_tail(&chip->bioq, bp);
+ mtx_unlock(&chip->qlock);
+ taskqueue_enqueue(chip->tq, &chip->iotask);
+}
+
+static void
+nand_strategy_raw(struct bio *bp)
+{
+ struct nand_chip *chip;
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+
+ /* Inform taskqueue that it's a raw access */
+ bp->bio_driver1 = BIO_NAND_RAW;
+
+ nand_debug(NDBG_GEOM, "Strategy %s on chip %d [%p]",
+ (bp->bio_cmd & BIO_READ) == BIO_READ ? "READ" :
+ ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE ? "WRITE" :
+ ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE ? "DELETE" : "UNKNOWN")),
+ chip->num, chip);
+
+ mtx_lock(&chip->qlock);
+ bioq_insert_tail(&chip->bioq, bp);
+ mtx_unlock(&chip->qlock);
+ taskqueue_enqueue(chip->tq, &chip->iotask);
+}
+
+static int
+nand_oob_access(struct nand_chip *chip, uint32_t page, uint32_t offset,
+ uint32_t len, uint8_t *data, uint8_t write)
+{
+ struct chip_geom *cg;
+ int ret = 0;
+
+ cg = &chip->chip_geom;
+
+ if (!write)
+ ret = nand_read_oob(chip, page, data, cg->oob_size);
+ else
+ ret = nand_prog_oob(chip, page, data, cg->oob_size);
+
+ return (ret);
+}
+
+static int
+nand_getattr(struct bio *bp)
+{
+ struct nand_chip *chip;
+ struct chip_geom *cg;
+ device_t dev;
+
+ if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL)
+ return (ENXIO);
+
+ chip = (struct nand_chip *)bp->bio_disk->d_drv1;
+ cg = &(chip->chip_geom);
+
+ dev = device_get_parent(chip->dev);
+ dev = device_get_parent(dev);
+
+ do {
+ if (g_handleattr_int(bp, "NAND::oobsize", cg->oob_size))
+ break;
+ else if (g_handleattr_int(bp, "NAND::pagesize", cg->page_size))
+ break;
+ else if (g_handleattr_int(bp, "NAND::blocksize",
+ cg->block_size))
+ break;
+ else if (g_handleattr(bp, "NAND::device", &(dev),
+ sizeof(device_t)))
+ break;
+
+ return (ERESTART);
+ } while (0);
+
+ return (EJUSTRETURN);
+}
+
+static int
+nand_ioctl(struct disk *ndisk, u_long cmd, void *data, int fflag,
+ struct thread *td)
+{
+ struct nand_chip *chip;
+ struct nand_oob_rw *oob_rw = NULL;
+ struct nand_raw_rw *raw_rw = NULL;
+ device_t nandbus;
+ uint8_t *buf = NULL;
+ int ret = 0;
+ uint8_t status;
+
+ chip = (struct nand_chip *)ndisk->d_drv1;
+ nandbus = device_get_parent(chip->dev);
+
+ if ((cmd == NAND_IO_RAW_READ) || (cmd == NAND_IO_RAW_PROG)) {
+ raw_rw = (struct nand_raw_rw *)data;
+ buf = malloc(raw_rw->len, M_NAND, M_WAITOK);
+ }
+ switch (cmd) {
+ case NAND_IO_ERASE:
+ ret = nand_erase_blocks(chip, ((off_t *)data)[0],
+ ((off_t *)data)[1]);
+ break;
+
+ case NAND_IO_OOB_READ:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 0);
+ break;
+
+ case NAND_IO_OOB_PROG:
+ oob_rw = (struct nand_oob_rw *)data;
+ ret = nand_oob_access(chip, oob_rw->page, 0,
+ oob_rw->len, oob_rw->data, 1);
+ break;
+
+ case NAND_IO_GET_STATUS:
+ NANDBUS_LOCK(nandbus);
+ ret = NANDBUS_GET_STATUS(nandbus, &status);
+ if (ret == 0)
+ *(uint8_t *)data = status;
+ NANDBUS_UNLOCK(nandbus);
+ break;
+
+ case NAND_IO_RAW_PROG:
+ copyin(raw_rw->data, buf, raw_rw->len);
+ ret = nand_prog_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ break;
+
+ case NAND_IO_RAW_READ:
+ ret = nand_read_pages_raw(chip, raw_rw->off, buf,
+ raw_rw->len);
+ copyout(buf, raw_rw->data, raw_rw->len);
+ break;
+
+ case NAND_IO_GET_CHIP_PARAM:
+ nand_get_chip_param(chip, (struct chip_param_io *)data);
+ break;
+
+ default:
+ printf("Unknown nand_ioctl request \n");
+ ret = EIO;
+ }
+
+ if (buf)
+ free(buf, M_NAND);
+
+ return (ret);
+}
+
+static void
+nand_io_proc(void *arg, int pending)
+{
+ struct nand_chip *chip = arg;
+ struct bio *bp;
+ int err = 0;
+
+ for (;;) {
+ mtx_lock(&chip->qlock);
+ bp = bioq_takefirst(&chip->bioq);
+ mtx_unlock(&chip->qlock);
+ if (bp == NULL)
+ break;
+
+ if (bp->bio_driver1 == BIO_NAND_STD) {
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
+ err = nand_write(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+ } else if (bp->bio_driver1 == BIO_NAND_RAW) {
+ if ((bp->bio_cmd & BIO_READ) == BIO_READ) {
+ err = nand_read_raw(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ } else if ((bp->bio_cmd & BIO_WRITE) == BIO_WRITE) {
+ err = nand_write_raw(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_data, bp->bio_bcount);
+ }
+ } else
+ panic("Unknown access type in bio->bio_driver1\n");
+
+ if ((bp->bio_cmd & BIO_DELETE) == BIO_DELETE) {
+ nand_debug(NDBG_GEOM, "Delete on chip%d offset %lld "
+ "length %ld\n", chip->num, bp->bio_offset,
+ bp->bio_bcount);
+ err = nand_erase_blocks(chip,
+ bp->bio_offset & 0xffffffff,
+ bp->bio_bcount);
+ }
+
+ if (err == 0 || err == ECC_CORRECTABLE)
+ bp->bio_resid = 0;
+ else {
+ nand_debug(NDBG_GEOM,"nand_[read|write|erase_blocks] "
+ "error: %d\n", err);
+
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+ }
+ biodone(bp);
+ }
+}
+
+int
+create_geom_disk(struct nand_chip *chip)
+{
+ struct disk *ndisk, *rdisk;
+
+ /* Create the disk device */
+ ndisk = disk_alloc();
+ ndisk->d_strategy = nand_strategy;
+ ndisk->d_ioctl = nand_ioctl;
+ ndisk->d_getattr = nand_getattr;
+ ndisk->d_name = "gnand";
+ ndisk->d_drv1 = chip;
+ ndisk->d_maxsize = chip->chip_geom.block_size;
+ ndisk->d_sectorsize = chip->chip_geom.page_size;
+ ndisk->d_mediasize = chip->chip_geom.chip_size;
+ ndisk->d_unit = chip->num +
+ 10 * device_get_unit(device_get_parent(chip->dev));
+
+ /*
+ * When using BBT, make two last blocks of device unavailable
+ * to user (because those are used to store BBT table).
+ */
+ if (chip->bbt != NULL)
+ ndisk->d_mediasize -= (2 * chip->chip_geom.block_size);
+
+ ndisk->d_flags = DISKFLAG_CANDELETE;
+
+ snprintf(ndisk->d_ident, sizeof(ndisk->d_ident),
+ "nand: Man:0x%02x Dev:0x%02x", chip->id.man_id, chip->id.dev_id);
+
+ disk_create(ndisk, DISK_VERSION);
+
+ /* Create the RAW disk device */
+ rdisk = disk_alloc();
+ rdisk->d_strategy = nand_strategy_raw;
+ rdisk->d_ioctl = nand_ioctl;
+ rdisk->d_getattr = nand_getattr;
+ rdisk->d_name = "gnand.raw";
+ rdisk->d_drv1 = chip;
+ rdisk->d_maxsize = chip->chip_geom.block_size;
+ rdisk->d_sectorsize = chip->chip_geom.page_size;
+ rdisk->d_mediasize = chip->chip_geom.chip_size;
+ rdisk->d_unit = chip->num +
+ 10 * device_get_unit(device_get_parent(chip->dev));
+
+ rdisk->d_flags = DISKFLAG_CANDELETE;
+
+ snprintf(rdisk->d_ident, sizeof(rdisk->d_ident),
+ "nand_raw: Man:0x%02x Dev:0x%02x", chip->id.man_id,
+ chip->id.dev_id);
+
+ disk_create(rdisk, DISK_VERSION);
+
+ chip->ndisk = ndisk;
+ chip->rdisk = rdisk;
+
+ mtx_init(&chip->qlock, "NAND I/O lock", NULL, MTX_DEF);
+ bioq_init(&chip->bioq);
+
+ TASK_INIT(&chip->iotask, 0, nand_io_proc, chip);
+ chip->tq = taskqueue_create("nand_taskq", M_WAITOK,
+ taskqueue_thread_enqueue, &chip->tq);
+ taskqueue_start_threads(&chip->tq, 1, PI_DISK, "nand taskq");
+
+ if (bootverbose)
+ device_printf(chip->dev, "Created gnand%d for chip [0x%0x, "
+ "0x%0x]\n", ndisk->d_unit, chip->id.man_id,
+ chip->id.dev_id);
+
+ return (0);
+}
+
+void
+destroy_geom_disk(struct nand_chip *chip)
+{
+ struct bio *bp;
+
+ taskqueue_free(chip->tq);
+ disk_destroy(chip->ndisk);
+ disk_destroy(chip->rdisk);
+
+ mtx_lock(&chip->qlock);
+ for (;;) {
+ bp = bioq_takefirst(&chip->bioq);
+ if (bp == NULL)
+ break;
+ bp->bio_error = EIO;
+ bp->bio_flags |= BIO_ERROR;
+ bp->bio_resid = bp->bio_bcount;
+
+ biodone(bp);
+ }
+ mtx_unlock(&chip->qlock);
+
+ mtx_destroy(&chip->qlock);
+}
diff --git a/sys/dev/nand/nand_id.c b/sys/dev/nand/nand_id.c
new file mode 100644
index 0000000..75c5834
--- /dev/null
+++ b/sys/dev/nand/nand_id.c
@@ -0,0 +1,60 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/nand/nand.h>
+
+struct nand_params nand_ids[] = {
+ { { NAND_MAN_SAMSUNG, 0x75 }, "Samsung K9F5608U0B",
+ 0x20, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_SAMSUNG, 0xd3 }, "Samsung NAND 1GiB 3,3V 8-bit",
+ 0x400, 0x800, 0x40, 0x40, 0 },
+ { { NAND_MAN_HYNIX, 0x76 }, "Hynix NAND 64MiB 3,3V 8-bit",
+ 0x40, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_HYNIX, 0xdc }, "Hynix NAND 512MiB 3,3V 8-bit",
+ 0x200, 0x800, 0x40, 0x40, 0 },
+ { { NAND_MAN_HYNIX, 0x79 }, "NAND 128MB 3,3V 8-bit",
+ 0x80, 0x200, 0x10, 0x20, 0 },
+ { { NAND_MAN_STMICRO, 0xf1 }, "STMicro 128MB 3,3V 8-bit",
+ 0x80, 2048, 64, 0x40, 0 },
+};
+
+struct nand_params *nand_get_params(struct nand_id *id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(nand_ids) / sizeof(nand_ids[0]); i++)
+ if (nand_ids[i].id.man_id == id->man_id &&
+ nand_ids[i].id.dev_id == id->dev_id)
+ return (&nand_ids[i]);
+
+ return (NULL);
+}
diff --git a/sys/dev/nand/nand_if.m b/sys/dev/nand/nand_if.m
new file mode 100644
index 0000000..49c8879
--- /dev/null
+++ b/sys/dev/nand/nand_if.m
@@ -0,0 +1,168 @@
+#-
+# 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$
+
+# NAND chip interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nand;
+
+CODE {
+ static int nand_method_not_supported(device_t dev)
+ {
+ return (ENOENT);
+ }
+};
+
+# Read NAND page
+#
+# Return values:
+# 0: Success
+#
+METHOD int read_page {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND page
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_page {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND page interleaved
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_page_intlv {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+} DEFAULT nand_method_not_supported;
+
+# Read NAND oob
+#
+# Return values:
+# 0: Success
+#
+METHOD int read_oob {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Program NAND oob
+#
+# Return values:
+# 0: Success
+#
+METHOD int program_oob {
+ device_t dev;
+ uint32_t page;
+ void* buf;
+ uint32_t len;
+ uint32_t offset;
+};
+
+# Erase NAND block
+#
+# Return values:
+# 0: Success
+#
+METHOD int erase_block {
+ device_t dev;
+ uint32_t block;
+};
+
+# Erase NAND block interleaved
+#
+# Return values:
+# 0: Success
+#
+METHOD int erase_block_intlv {
+ device_t dev;
+ uint32_t block;
+} DEFAULT nand_method_not_supported;
+
+# NAND get status
+#
+# Return values:
+# 0: Success
+#
+METHOD int get_status {
+ device_t dev;
+ uint8_t *status;
+};
+
+# NAND check if block is bad
+#
+# Return values:
+# 0: Success
+#
+METHOD int is_blk_bad {
+ device_t dev;
+ uint32_t block_number;
+ uint8_t *bad;
+};
+
+# NAND get ECC
+#
+#
+METHOD int get_ecc {
+ device_t dev;
+ void *buf;
+ void *ecc;
+ int *needwrite;
+};
+
+# NAND correct ECC
+#
+#
+METHOD int correct_ecc {
+ device_t dev;
+ void *buf;
+ void *readecc;
+ void *calcecc;
+};
+
diff --git a/sys/dev/nand/nandbus.c b/sys/dev/nand/nandbus.c
new file mode 100644
index 0000000..322d708
--- /dev/null
+++ b/sys/dev/nand/nandbus.c
@@ -0,0 +1,530 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include "nand_if.h"
+#include "nandbus_if.h"
+#include "nfc_if.h"
+
+#define NAND_NCS 4
+
+static int nandbus_probe(device_t dev);
+static int nandbus_attach(device_t dev);
+static int nandbus_detach(device_t dev);
+
+static int nandbus_child_location_str(device_t, device_t, char *, size_t);
+static int nandbus_child_pnpinfo_str(device_t, device_t, char *, size_t);
+
+static int nandbus_get_status(device_t, uint8_t *);
+static void nandbus_read_buffer(device_t, void *, uint32_t);
+static int nandbus_select_cs(device_t, uint8_t);
+static int nandbus_send_command(device_t, uint8_t);
+static int nandbus_send_address(device_t, uint8_t);
+static int nandbus_start_command(device_t);
+static int nandbus_wait_ready(device_t, uint8_t *);
+static void nandbus_write_buffer(device_t, void *, uint32_t);
+static int nandbus_get_ecc(device_t, void *, uint32_t, void *, int *);
+static int nandbus_correct_ecc(device_t, void *, int, void *, void *);
+static void nandbus_lock(device_t);
+static void nandbus_unlock(device_t);
+
+static int nand_readid(device_t, uint8_t *, uint8_t *);
+static int nand_probe_onfi(device_t, uint8_t *);
+static int nand_reset(device_t);
+
+struct nandbus_softc {
+ device_t dev;
+ struct cv nandbus_cv;
+ struct mtx nandbus_mtx;
+ uint8_t busy;
+};
+
+static device_method_t nandbus_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_probe, nandbus_probe),
+ DEVMETHOD(device_attach, nandbus_attach),
+ DEVMETHOD(device_detach, nandbus_detach),
+ DEVMETHOD(device_shutdown, bus_generic_shutdown),
+
+ /* bus interface */
+ DEVMETHOD(bus_print_child, bus_generic_print_child),
+ DEVMETHOD(bus_driver_added, bus_generic_driver_added),
+ DEVMETHOD(bus_child_pnpinfo_str, nandbus_child_pnpinfo_str),
+ DEVMETHOD(bus_child_location_str, nandbus_child_location_str),
+
+ /* nandbus interface */
+ DEVMETHOD(nandbus_get_status, nandbus_get_status),
+ DEVMETHOD(nandbus_read_buffer, nandbus_read_buffer),
+ DEVMETHOD(nandbus_select_cs, nandbus_select_cs),
+ DEVMETHOD(nandbus_send_command, nandbus_send_command),
+ DEVMETHOD(nandbus_send_address, nandbus_send_address),
+ DEVMETHOD(nandbus_start_command,nandbus_start_command),
+ DEVMETHOD(nandbus_wait_ready, nandbus_wait_ready),
+ DEVMETHOD(nandbus_write_buffer, nandbus_write_buffer),
+ DEVMETHOD(nandbus_get_ecc, nandbus_get_ecc),
+ DEVMETHOD(nandbus_correct_ecc, nandbus_correct_ecc),
+ DEVMETHOD(nandbus_lock, nandbus_lock),
+ DEVMETHOD(nandbus_unlock, nandbus_unlock),
+ { 0, 0 }
+};
+
+devclass_t nandbus_devclass;
+
+driver_t nandbus_driver = {
+ "nandbus",
+ nandbus_methods,
+ sizeof(struct nandbus_softc)
+};
+
+DRIVER_MODULE(nandbus, nand, nandbus_driver, nandbus_devclass, 0, 0);
+
+int
+nandbus_create(device_t nfc)
+{
+ device_t child;
+
+ child = device_add_child(nfc, "nandbus", -1);
+ if (!child)
+ return (ENODEV);
+
+ bus_generic_attach(nfc);
+
+ return(0);
+}
+
+void
+nandbus_destroy(device_t nfc)
+{
+ device_t *children;
+ int nchildren, i;
+
+ mtx_lock(&Giant);
+ /* Detach & delete all children */
+ if (!device_get_children(nfc, &children, &nchildren)) {
+ for (i = 0; i < nchildren; i++)
+ device_delete_child(nfc, children[i]);
+
+ free(children, M_TEMP);
+ }
+ mtx_unlock(&Giant);
+}
+
+static int
+nandbus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "NAND bus");
+
+ return (0);
+}
+
+static int
+nandbus_attach(device_t dev)
+{
+ device_t child, nfc;
+ struct nand_id chip_id;
+ struct nandbus_softc *sc;
+ struct nandbus_ivar *ivar;
+ struct nand_softc *nfc_sc;
+ struct nand_params *chip_params;
+ uint8_t cs, onfi;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ nfc = device_get_parent(dev);
+ nfc_sc = device_get_softc(nfc);
+
+ mtx_init(&sc->nandbus_mtx, "nandbus lock", MTX_DEF, 0);
+ cv_init(&sc->nandbus_cv, "nandbus cv");
+
+ /* Check each possible CS for existing nand devices */
+ for (cs = 0; cs < NAND_NCS; cs++) {
+ nand_debug(NDBG_BUS,"probe chip select %x", cs);
+
+ /* Select & reset chip */
+ if (nandbus_select_cs(dev, cs))
+ break;
+
+ if (nand_reset(dev))
+ continue;
+
+ /* Read manufacturer and device id */
+ if (nand_readid(dev, &chip_id.man_id, &chip_id.dev_id))
+ continue;
+
+ if (chip_id.man_id == 0xff)
+ continue;
+
+ /* Check if chip is ONFI compliant */
+ if (nand_probe_onfi(dev, &onfi) != 0) {
+ continue;
+ }
+
+ ivar = malloc(sizeof(struct nandbus_ivar),
+ M_NAND, M_WAITOK);
+
+ if (onfi == 1) {
+ ivar->cs = cs;
+ ivar->cols = 0;
+ ivar->rows = 0;
+ ivar->params = NULL;
+ ivar->man_id = chip_id.man_id;
+ ivar->dev_id = chip_id.dev_id;
+ ivar->is_onfi = onfi;
+ ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
+
+ child = device_add_child(dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ continue;
+ }
+
+ chip_params = nand_get_params(&chip_id);
+ if (chip_params == NULL) {
+ nand_debug(NDBG_BUS,"Chip description not found! "
+ "(manuf: 0x%0x, chipid: 0x%0x)\n",
+ chip_id.man_id, chip_id.dev_id);
+ free(ivar, M_NAND);
+ continue;
+ }
+
+ ivar->cs = cs;
+ ivar->cols = 1;
+ ivar->rows = 2;
+ ivar->params = chip_params;
+ ivar->man_id = chip_id.man_id;
+ ivar->dev_id = chip_id.dev_id;
+ ivar->is_onfi = onfi;
+ ivar->chip_cdev_name = nfc_sc->chip_cdev_name;
+
+ /*
+ * Check what type of device we have.
+ * devices bigger than 32MiB have on more row (3)
+ */
+ if (chip_params->chip_size > 32)
+ ivar->rows++;
+ /* Large page devices have one more col (2) */
+ if (chip_params->chip_size >= 128 &&
+ chip_params->page_size > 512)
+ ivar->cols++;
+
+ child = device_add_child(dev, NULL, -1);
+ device_set_ivars(child, ivar);
+ }
+
+ bus_generic_attach(dev);
+ return (0);
+}
+
+static int
+nandbus_detach(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ bus_generic_detach(dev);
+
+ mtx_destroy(&sc->nandbus_mtx);
+ cv_destroy(&sc->nandbus_cv);
+
+ return (0);
+}
+
+static int
+nandbus_child_location_str(device_t bus, device_t child, char *buf,
+ size_t buflen)
+{
+ struct nandbus_ivar *ivar = device_get_ivars(child);
+
+ snprintf(buf, buflen, "at cs#%d", ivar->cs);
+ return (0);
+}
+
+static int
+nandbus_child_pnpinfo_str(device_t bus, device_t child, char *buf,
+ size_t buflen)
+{
+ // XXX man id, model id ????
+ *buf = '\0';
+ return (0);
+}
+
+static int
+nand_readid(device_t bus, uint8_t *man_id, uint8_t *dev_id)
+{
+ device_t nfc;
+
+ if (!bus || !man_id || !dev_id)
+ return (EINVAL);
+
+ nand_debug(NDBG_BUS,"read id");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
+ nand_debug(NDBG_BUS,"Error : could not send READ ID command");
+ return (ENXIO);
+ }
+
+ if (NFC_SEND_ADDRESS(nfc, 0)) {
+ nand_debug(NDBG_BUS,"Error : could not sent address to chip");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start command");
+ return (ENXIO);
+ }
+
+ DELAY(25);
+
+ *man_id = NFC_READ_BYTE(nfc);
+ *dev_id = NFC_READ_BYTE(nfc);
+
+ nand_debug(NDBG_BUS,"manufacturer id: %x chip id: %x", *man_id,
+ *dev_id);
+
+ return (0);
+}
+
+static int
+nand_probe_onfi(device_t bus, uint8_t *onfi_compliant)
+{
+ device_t nfc;
+ char onfi_id[] = {'o', 'n', 'f', 'i', '\0'};
+ int i;
+
+ nand_debug(NDBG_BUS,"probing ONFI");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_READ_ID)) {
+ nand_debug(NDBG_BUS,"Error : could not sent READ ID command");
+ return (ENXIO);
+ }
+
+ if (NFC_SEND_ADDRESS(nfc, ONFI_SIG_ADDR)) {
+ nand_debug(NDBG_BUS,"Error : could not sent address to chip");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start command");
+ return (ENXIO);
+ }
+ for (i = 0; onfi_id[i] != '\0'; i++)
+ if (NFC_READ_BYTE(nfc) != onfi_id[i]) {
+ nand_debug(NDBG_BUS,"ONFI non-compliant");
+ *onfi_compliant = 0;
+ return (0);
+ }
+
+ nand_debug(NDBG_BUS,"ONFI compliant");
+ *onfi_compliant = 1;
+
+ return (0);
+}
+
+static int
+nand_reset(device_t bus)
+{
+ device_t nfc;
+ nand_debug(NDBG_BUS,"resetting...");
+
+ nfc = device_get_parent(bus);
+
+ if (NFC_SEND_COMMAND(nfc, NAND_CMD_RESET) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not sent RESET command");
+ return (ENXIO);
+ }
+
+ if (NFC_START_COMMAND(nfc) != 0) {
+ nand_debug(NDBG_BUS,"Error : could not start RESET command");
+ return (ENXIO);
+ }
+
+ DELAY(1000);
+
+ return (0);
+}
+
+void
+nandbus_lock(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->nandbus_mtx);
+ if (sc->busy)
+ cv_wait(&sc->nandbus_cv, &sc->nandbus_mtx);
+ sc->busy = 1;
+ mtx_unlock(&sc->nandbus_mtx);
+}
+
+void
+nandbus_unlock(device_t dev)
+{
+ struct nandbus_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ mtx_lock(&sc->nandbus_mtx);
+ sc->busy = 0;
+ cv_signal(&sc->nandbus_cv);
+ mtx_unlock(&sc->nandbus_mtx);
+}
+
+int
+nandbus_select_cs(device_t dev, uint8_t cs)
+{
+
+ return (NFC_SELECT_CS(device_get_parent(dev), cs));
+}
+
+int
+nandbus_send_command(device_t dev, uint8_t command)
+{
+ int err;
+
+ if ((err = NFC_SEND_COMMAND(device_get_parent(dev), command)))
+ nand_debug(NDBG_BUS,"Err: Could not send command %x, err %x",
+ command, err);
+
+ return (err);
+}
+
+int
+nandbus_send_address(device_t dev, uint8_t address)
+{
+ int err;
+
+ if ((err = NFC_SEND_ADDRESS(device_get_parent(dev), address)))
+ nand_debug(NDBG_BUS,"Err: Could not send address %x, err %x",
+ address, err);
+
+ return (err);
+}
+
+int
+nandbus_start_command(device_t dev)
+{
+ int err;
+
+ if ((err = NFC_START_COMMAND(device_get_parent(dev))))
+ nand_debug(NDBG_BUS,"Err: Could not start command, err %x",
+ err);
+
+ return (err);
+}
+
+void
+nandbus_read_buffer(device_t dev, void *buf, uint32_t len)
+{
+
+ NFC_READ_BUF(device_get_parent(dev), buf, len);
+}
+
+void
+nandbus_write_buffer(device_t dev, void *buf, uint32_t len)
+{
+
+ NFC_WRITE_BUF(device_get_parent(dev), buf, len);
+}
+
+int
+nandbus_get_status(device_t dev, uint8_t *status)
+{
+ int err;
+
+ if ((err = NANDBUS_SEND_COMMAND(dev, NAND_CMD_STATUS)))
+ return (err);
+ if ((err = NANDBUS_START_COMMAND(dev)))
+ return (err);
+
+ *status = NFC_READ_BYTE(device_get_parent(dev));
+
+ return (0);
+}
+
+int
+nandbus_wait_ready(device_t dev, uint8_t *status)
+{
+ struct timeval tv, tv2;
+
+ tv2.tv_sec = 0;
+ tv2.tv_usec = 50 * 5000; /* 10ms */
+
+ getmicrotime(&tv);
+ timevaladd(&tv, &tv2);
+
+ do {
+ if (NANDBUS_GET_STATUS(dev, status))
+ return (ENXIO);
+
+ if (*status & NAND_STATUS_RDY)
+ return (0);
+
+ getmicrotime(&tv2);
+ } while (timevalcmp(&tv2, &tv, <=));
+
+ return (EBUSY);
+}
+
+int
+nandbus_get_ecc(device_t dev, void *buf, uint32_t pagesize, void *ecc,
+ int *needwrite)
+{
+
+ return (NFC_GET_ECC(device_get_parent(dev), buf, pagesize, ecc, needwrite));
+}
+
+int
+nandbus_correct_ecc(device_t dev, void *buf, int pagesize, void *readecc,
+ void *calcecc)
+{
+
+ return (NFC_CORRECT_ECC(device_get_parent(dev), buf, pagesize,
+ readecc, calcecc));
+}
+
diff --git a/sys/dev/nand/nandbus.h b/sys/dev/nand/nandbus.h
new file mode 100644
index 0000000..417fbf5
--- /dev/null
+++ b/sys/dev/nand/nandbus.h
@@ -0,0 +1,49 @@
+/*-
+ * 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$
+ */
+
+#ifndef _NANDBUS_H_
+#define _NANDBUS_H_
+
+struct nandbus_ivar {
+ uint8_t cs;
+ uint8_t cols;
+ uint8_t rows;
+ uint8_t man_id;
+ uint8_t dev_id;
+ uint8_t is_onfi;
+ char *chip_cdev_name;
+ struct nand_params *params;
+};
+
+extern devclass_t nandbus_devclass;
+extern driver_t nandbus_driver;
+
+int nandbus_create(device_t nfc);
+void nandbus_destroy(device_t nfc);
+
+#endif /* _NANDBUS_H_ */
diff --git a/sys/dev/nand/nandbus_if.m b/sys/dev/nand/nandbus_if.m
new file mode 100644
index 0000000..e914e18
--- /dev/null
+++ b/sys/dev/nand/nandbus_if.m
@@ -0,0 +1,100 @@
+#-
+# 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$
+
+# NAND bus interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nandbus;
+
+METHOD int get_status {
+ device_t dev;
+ uint8_t * status;
+};
+
+METHOD void read_buffer {
+ device_t dev;
+ void * buf;
+ uint32_t len;
+};
+
+METHOD int select_cs {
+ device_t dev;
+ uint8_t cs;
+};
+
+METHOD int send_command {
+ device_t dev;
+ uint8_t command;
+};
+
+METHOD int send_address {
+ device_t dev;
+ uint8_t address;
+};
+
+METHOD int start_command {
+ device_t dev;
+};
+
+METHOD int wait_ready {
+ device_t dev;
+ uint8_t * status;
+}
+
+METHOD void write_buffer {
+ device_t dev;
+ void * buf;
+ uint32_t len;
+};
+
+METHOD int get_ecc {
+ device_t dev;
+ void * buf;
+ uint32_t pagesize;
+ void * ecc;
+ int * needwrite;
+};
+
+METHOD int correct_ecc {
+ device_t dev;
+ void * buf;
+ int pagesize;
+ void * readecc;
+ void * calcecc;
+};
+
+METHOD void lock {
+ device_t dev;
+};
+
+METHOD void unlock {
+ device_t dev;
+};
+
diff --git a/sys/dev/nand/nandsim.c b/sys/dev/nand/nandsim.c
new file mode 100644
index 0000000..5390c01
--- /dev/null
+++ b/sys/dev/nand/nandsim.c
@@ -0,0 +1,665 @@
+/*-
+ * 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.
+ */
+
+/* Simulated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_swap.h>
+
+struct sim_param sim;
+struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
+
+static struct cdev *nandsim_dev;
+static d_ioctl_t nandsim_ioctl;
+
+static void nandsim_init_sim_param(struct sim_param *);
+static int nandsim_create_ctrl(struct sim_ctrl *);
+static int nandsim_destroy_ctrl(int);
+static int nandsim_ctrl_status(struct sim_ctrl *);
+static int nandsim_create_chip(struct sim_chip *);
+static int nandsim_destroy_chip(struct sim_ctrl_chip *);
+static int nandsim_chip_status(struct sim_chip *);
+static int nandsim_start_ctrl(int);
+static int nandsim_stop_ctrl(int);
+static int nandsim_inject_error(struct sim_error *);
+static int nandsim_get_block_state(struct sim_block_state *);
+static int nandsim_set_block_state(struct sim_block_state *);
+static int nandsim_modify(struct sim_mod *);
+static int nandsim_dump(struct sim_dump *);
+static int nandsim_restore(struct sim_dump *);
+static int nandsim_freeze(struct sim_ctrl_chip *);
+static void nandsim_print_log(struct sim_log *);
+static struct nandsim_chip *get_nandsim_chip(uint8_t, uint8_t);
+
+static struct cdevsw nandsim_cdevsw = {
+ .d_version = D_VERSION,
+ .d_ioctl = nandsim_ioctl,
+ .d_name = "nandsim",
+};
+
+int
+nandsim_ioctl(struct cdev *dev, u_long cmd, caddr_t data,
+ int flags, struct thread *td)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case NANDSIM_SIM_PARAM:
+ nandsim_init_sim_param((struct sim_param *)data);
+ break;
+ case NANDSIM_CREATE_CTRL:
+ ret = nandsim_create_ctrl((struct sim_ctrl *)data);
+ break;
+ case NANDSIM_DESTROY_CTRL:
+ ret = nandsim_destroy_ctrl(*(int *)data);
+ break;
+ case NANDSIM_STATUS_CTRL:
+ ret = nandsim_ctrl_status((struct sim_ctrl *)data);
+ break;
+ case NANDSIM_CREATE_CHIP:
+ ret = nandsim_create_chip((struct sim_chip *)data);
+ break;
+ case NANDSIM_DESTROY_CHIP:
+ ret = nandsim_destroy_chip((struct sim_ctrl_chip *)data);
+ break;
+ case NANDSIM_STATUS_CHIP:
+ ret = nandsim_chip_status((struct sim_chip *)data);
+ break;
+ case NANDSIM_MODIFY:
+ ret = nandsim_modify((struct sim_mod *)data);
+ break;
+ case NANDSIM_START_CTRL:
+ ret = nandsim_start_ctrl(*(int *)data);
+ break;
+ case NANDSIM_STOP_CTRL:
+ ret = nandsim_stop_ctrl(*(int *)data);
+ break;
+ case NANDSIM_INJECT_ERROR:
+ ret = nandsim_inject_error((struct sim_error *)data);
+ break;
+ case NANDSIM_SET_BLOCK_STATE:
+ ret = nandsim_set_block_state((struct sim_block_state *)data);
+ break;
+ case NANDSIM_GET_BLOCK_STATE:
+ ret = nandsim_get_block_state((struct sim_block_state *)data);
+ break;
+ case NANDSIM_PRINT_LOG:
+ nandsim_print_log((struct sim_log *)data);
+ break;
+ case NANDSIM_DUMP:
+ ret = nandsim_dump((struct sim_dump *)data);
+ break;
+ case NANDSIM_RESTORE:
+ ret = nandsim_restore((struct sim_dump *)data);
+ break;
+ case NANDSIM_FREEZE:
+ ret = nandsim_freeze((struct sim_ctrl_chip *)data);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+
+ return (ret);
+}
+
+static void
+nandsim_init_sim_param(struct sim_param *param)
+{
+
+ if (!param)
+ return;
+
+ nand_debug(NDBG_SIM,"log level:%d output %d", param->log_level,
+ param->log_output);
+ nandsim_log_level = param->log_level;
+ nandsim_log_output = param->log_output;
+}
+
+static int
+nandsim_create_ctrl(struct sim_ctrl *ctrl)
+{
+ struct sim_ctrl_conf *sim_ctrl;
+
+ nand_debug(NDBG_SIM,"create controller num:%d cs:%d",ctrl->num,
+ ctrl->num_cs);
+
+ if (ctrl->num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ sim_ctrl = &ctrls[ctrl->num];
+ if(sim_ctrl->created)
+ return (EEXIST);
+
+ sim_ctrl->num = ctrl->num;
+ sim_ctrl->num_cs = ctrl->num_cs;
+ sim_ctrl->ecc = ctrl->ecc;
+ memcpy(sim_ctrl->ecc_layout, ctrl->ecc_layout,
+ MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
+ strlcpy(sim_ctrl->filename, ctrl->filename,
+ FILENAME_SIZE);
+ sim_ctrl->created = 1;
+
+ return (0);
+}
+
+static int
+nandsim_destroy_ctrl(int ctrl_num)
+{
+
+ nand_debug(NDBG_SIM,"destroy controller num:%d", ctrl_num);
+
+ if (ctrl_num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ if (!ctrls[ctrl_num].created) {
+ return (ENODEV);
+ }
+
+ if (ctrls[ctrl_num].running) {
+ return (EBUSY);
+ }
+
+ memset(&ctrls[ctrl_num], 0, sizeof(ctrls[ctrl_num]));
+
+ return (0);
+}
+
+static int
+nandsim_ctrl_status(struct sim_ctrl *ctrl)
+{
+
+ nand_debug(NDBG_SIM,"status controller num:%d cs:%d",ctrl->num,
+ ctrl->num_cs);
+
+ if (ctrl->num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ ctrl->num_cs = ctrls[ctrl->num].num_cs;
+ ctrl->ecc = ctrls[ctrl->num].ecc;
+ memcpy(ctrl->ecc_layout, ctrls[ctrl->num].ecc_layout,
+ MAX_ECC_BYTES * sizeof(ctrl->ecc_layout[0]));
+ strlcpy(ctrl->filename, ctrls[ctrl->num].filename,
+ FILENAME_SIZE);
+ ctrl->running = ctrls[ctrl->num].running;
+ ctrl->created = ctrls[ctrl->num].created;
+
+ return (0);
+}
+
+static int
+nandsim_create_chip(struct sim_chip *chip)
+{
+ struct sim_chip *sim_chip;
+
+ nand_debug(NDBG_SIM,"create chip num:%d at ctrl:%d", chip->num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV ||
+ chip->num >= MAX_CTRL_CS) {
+ return (EINVAL);
+ }
+
+ if (ctrls[chip->ctrl_num].chips[chip->num]) {
+ return (EEXIST);
+ }
+
+ sim_chip = malloc(sizeof(*sim_chip), M_NANDSIM,
+ M_WAITOK);
+ if (sim_chip == NULL) {
+ return (ENOMEM);
+ }
+
+ memcpy(sim_chip, chip, sizeof(*sim_chip));
+ ctrls[chip->ctrl_num].chips[chip->num] = sim_chip;
+ sim_chip->created = 1;
+
+ return (0);
+}
+
+static int
+nandsim_destroy_chip(struct sim_ctrl_chip *chip)
+{
+ struct sim_ctrl_conf *ctrl_conf;
+
+ nand_debug(NDBG_SIM,"destroy chip num:%d at ctrl:%d", chip->chip_num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV ||
+ chip->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ ctrl_conf = &ctrls[chip->ctrl_num];
+
+ if (!ctrl_conf->created || !ctrl_conf->chips[chip->chip_num])
+ return (ENODEV);
+
+ if (ctrl_conf->running)
+ return (EBUSY);
+
+ free(ctrl_conf->chips[chip->chip_num], M_NANDSIM);
+ ctrl_conf->chips[chip->chip_num] = NULL;
+
+ return (0);
+}
+
+static int
+nandsim_chip_status(struct sim_chip *chip)
+{
+ struct sim_ctrl_conf *ctrl_conf;
+
+ nand_debug(NDBG_SIM,"status for chip num:%d at ctrl:%d", chip->num,
+ chip->ctrl_num);
+
+ if (chip->ctrl_num >= MAX_SIM_DEV &&
+ chip->num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ ctrl_conf = &ctrls[chip->ctrl_num];
+ if (!ctrl_conf->chips[chip->num])
+ chip->created = 0;
+ else
+ memcpy(chip, ctrl_conf->chips[chip->num], sizeof(*chip));
+
+ return (0);
+}
+
+static int
+nandsim_start_ctrl(int num)
+{
+ device_t nexus, ndev;
+ devclass_t nexus_devclass;
+ int ret = 0;
+
+ nand_debug(NDBG_SIM,"start ctlr num:%d", num);
+
+ if (num >= MAX_SIM_DEV)
+ return (EINVAL);
+
+ if (!ctrls[num].created)
+ return (ENODEV);
+
+ if (ctrls[num].running)
+ return (EBUSY);
+
+ /* We will add our device as a child of the nexus0 device */
+ if (!(nexus_devclass = devclass_find("nexus")) ||
+ !(nexus = devclass_get_device(nexus_devclass, 0)))
+ return (EFAULT);
+
+ /*
+ * Create a newbus device representing this frontend instance
+ *
+ * XXX powerpc nexus doesn't implement bus_add_child, so child
+ * must be added by device_add_child().
+ */
+#if defined(__powerpc__)
+ ndev = device_add_child(nexus, "nandsim", num);
+#else
+ ndev = BUS_ADD_CHILD(nexus, 0, "nandsim", num);
+#endif
+ if (!ndev)
+ return (EFAULT);
+
+ mtx_lock(&Giant);
+ ret = device_probe_and_attach(ndev);
+ mtx_unlock(&Giant);
+
+ if (ret == 0) {
+ ctrls[num].sim_ctrl_dev = ndev;
+ ctrls[num].running = 1;
+ }
+
+ return (ret);
+}
+
+static int
+nandsim_stop_ctrl(int num)
+{
+ device_t nexus;
+ devclass_t nexus_devclass;
+ int ret = 0;
+
+ nand_debug(NDBG_SIM,"stop controller num:%d", num);
+
+ if (num >= MAX_SIM_DEV) {
+ return (EINVAL);
+ }
+
+ if (!ctrls[num].created || !ctrls[num].running) {
+ return (ENODEV);
+ }
+
+ /* We will add our device as a child of the nexus0 device */
+ if (!(nexus_devclass = devclass_find("nexus")) ||
+ !(nexus = devclass_get_device(nexus_devclass, 0))) {
+ return (ENODEV);
+ }
+
+ mtx_lock(&Giant);
+ if (ctrls[num].sim_ctrl_dev) {
+ ret = device_delete_child(nexus, ctrls[num].sim_ctrl_dev);
+ ctrls[num].sim_ctrl_dev = NULL;
+ }
+ mtx_unlock(&Giant);
+
+ ctrls[num].running = 0;
+
+ return (ret);
+}
+
+static struct nandsim_chip *
+get_nandsim_chip(uint8_t ctrl_num, uint8_t chip_num)
+{
+ struct nandsim_softc *sc;
+
+ if (!ctrls[ctrl_num].sim_ctrl_dev)
+ return (NULL);
+
+ sc = device_get_softc(ctrls[ctrl_num].sim_ctrl_dev);
+ return (sc->chips[chip_num]);
+}
+
+static void
+nandsim_print_log(struct sim_log *sim_log)
+{
+ struct nandsim_softc *sc;
+ int len1, len2;
+
+ if (!ctrls[sim_log->ctrl_num].sim_ctrl_dev)
+ return;
+
+ sc = device_get_softc(ctrls[sim_log->ctrl_num].sim_ctrl_dev);
+ if (sc->log_buff) {
+ len1 = strlen(&sc->log_buff[sc->log_idx + 1]);
+ if (len1 >= sim_log->len)
+ len1 = sim_log->len;
+ copyout(&sc->log_buff[sc->log_idx + 1], sim_log->log, len1);
+ len2 = strlen(sc->log_buff);
+ if (len2 >= (sim_log->len - len1))
+ len2 = (sim_log->len - len1);
+ copyout(sc->log_buff, &sim_log->log[len1], len2);
+ sim_log->len = len1 + len2;
+ }
+}
+
+static int
+nandsim_inject_error(struct sim_error *error)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ struct onfi_params *param;
+ int page, page_size, block, offset;
+
+ nand_debug(NDBG_SIM,"inject error for chip %d at ctrl %d\n",
+ error->chip_num, error->ctrl_num);
+
+ if (error->ctrl_num >= MAX_SIM_DEV ||
+ error->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ if (!ctrls[error->ctrl_num].created || !ctrls[error->ctrl_num].running)
+ return (ENODEV);
+
+ chip = get_nandsim_chip(error->ctrl_num, error->chip_num);
+ param = &chip->params;
+ page_size = param->bytes_per_page + param->spare_bytes_per_page;
+ block = error->page_num / param->pages_per_block;
+ page = error->page_num % param->pages_per_block;
+
+ bs = get_bs(chip->swap, block, 1);
+ if (!bs)
+ return (EINVAL);
+
+ offset = (page * page_size) + error->column;
+ memset(&bs->blk_ptr[offset], error->pattern, error->len);
+
+ return (0);
+}
+
+static int
+nandsim_set_block_state(struct sim_block_state *bs)
+{
+ struct onfi_params *params;
+ struct nandsim_chip *chip;
+ int blocks;
+
+ nand_debug(NDBG_SIM,"set block state for %d:%d block %d\n",
+ bs->chip_num, bs->ctrl_num, bs->block_num);
+
+ if (bs->ctrl_num >= MAX_SIM_DEV ||
+ bs->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
+ params = &chip->params;
+ blocks = params->luns * params->blocks_per_lun;
+
+ if (bs->block_num > blocks)
+ return (EINVAL);
+
+ chip->blk_state[bs->block_num].is_bad = bs->state;
+
+ if (bs->wearout >= 0)
+ chip->blk_state[bs->block_num].wear_lev = bs->wearout;
+
+ return (0);
+}
+
+static int
+nandsim_get_block_state(struct sim_block_state *bs)
+{
+ struct onfi_params *params;
+ struct nandsim_chip *chip;
+ int blocks;
+
+ if (bs->ctrl_num >= MAX_SIM_DEV ||
+ bs->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ nand_debug(NDBG_SIM,"get block state for %d:%d block %d\n",
+ bs->chip_num, bs->ctrl_num, bs->block_num);
+
+ chip = get_nandsim_chip(bs->ctrl_num, bs->chip_num);
+ params = &chip->params;
+ blocks = params->luns * params->blocks_per_lun;
+
+ if (bs->block_num > blocks)
+ return (EINVAL);
+
+ bs->state = chip->blk_state[bs->block_num].is_bad;
+ bs->wearout = chip->blk_state[bs->block_num].wear_lev;
+
+ return (0);
+}
+
+static int
+nandsim_dump(struct sim_dump *dump)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ int blk_size;
+
+ nand_debug(NDBG_SIM,"dump chip %d %d\n", dump->ctrl_num, dump->chip_num);
+
+ if (dump->ctrl_num >= MAX_SIM_DEV ||
+ dump->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
+ blk_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ bs = get_bs(chip->swap, dump->block_num, 0);
+ if (!bs)
+ return (EINVAL);
+
+ if (dump->len > blk_size)
+ dump->len = blk_size;
+
+ copyout(bs->blk_ptr, dump->data, dump->len);
+
+ return (0);
+}
+
+static int
+nandsim_restore(struct sim_dump *dump)
+{
+ struct nandsim_chip *chip;
+ struct block_space *bs;
+ int blk_size;
+
+ nand_debug(NDBG_SIM,"restore chip %d %d\n", dump->ctrl_num,
+ dump->chip_num);
+
+ if (dump->ctrl_num >= MAX_SIM_DEV ||
+ dump->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(dump->ctrl_num, dump->chip_num);
+ blk_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ bs = get_bs(chip->swap, dump->block_num, 1);
+ if (!bs)
+ return (EINVAL);
+
+ if (dump->len > blk_size)
+ dump->len = blk_size;
+
+
+ copyin(dump->data, bs->blk_ptr, dump->len);
+
+ return (0);
+}
+
+static int
+nandsim_freeze(struct sim_ctrl_chip *ctrl_chip)
+{
+ struct nandsim_chip *chip;
+
+ if (ctrl_chip->ctrl_num >= MAX_SIM_DEV ||
+ ctrl_chip->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ chip = get_nandsim_chip(ctrl_chip->ctrl_num, ctrl_chip->chip_num);
+ nandsim_chip_freeze(chip);
+
+ return (0);
+}
+
+static int
+nandsim_modify(struct sim_mod *mod)
+{
+ struct sim_chip *sim_conf = NULL;
+ struct nandsim_chip *sim_chip = NULL;
+
+ nand_debug(NDBG_SIM,"modify ctlr %d chip %d", mod->ctrl_num,
+ mod->chip_num);
+
+ if (mod->field != SIM_MOD_LOG_LEVEL) {
+ if (mod->ctrl_num >= MAX_SIM_DEV ||
+ mod->chip_num >= MAX_CTRL_CS)
+ return (EINVAL);
+
+ sim_conf = ctrls[mod->ctrl_num].chips[mod->chip_num];
+ sim_chip = get_nandsim_chip(mod->ctrl_num, mod->chip_num);
+ }
+
+ switch (mod->field) {
+ case SIM_MOD_LOG_LEVEL:
+ nandsim_log_level = mod->new_value;
+ break;
+ case SIM_MOD_ERASE_TIME:
+ sim_conf->erase_time = sim_chip->erase_delay = mod->new_value;
+ break;
+ case SIM_MOD_PROG_TIME:
+ sim_conf->prog_time = sim_chip->prog_delay = mod->new_value;
+ break;
+ case SIM_MOD_READ_TIME:
+ sim_conf->read_time = sim_chip->read_delay = mod->new_value;
+ break;
+ case SIM_MOD_ERROR_RATIO:
+ sim_conf->error_ratio = mod->new_value;
+ sim_chip->error_ratio = mod->new_value;
+ break;
+ default:
+ break;
+ }
+
+ return (0);
+}
+static int
+nandsim_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ struct sim_ctrl_chip chip_ctrl;
+ int i, j;
+
+ switch (type) {
+ case MOD_LOAD:
+ nandsim_dev = make_dev(&nandsim_cdevsw, 0,
+ UID_ROOT, GID_WHEEL, 0666, "nandsim.ioctl");
+ break;
+ case MOD_UNLOAD:
+ for (i = 0; i < MAX_SIM_DEV; i++) {
+ nandsim_stop_ctrl(i);
+ chip_ctrl.ctrl_num = i;
+ for (j = 0; j < MAX_CTRL_CS; j++) {
+ chip_ctrl.chip_num = j;
+ nandsim_destroy_chip(&chip_ctrl);
+ }
+ nandsim_destroy_ctrl(i);
+ }
+ destroy_dev(nandsim_dev);
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+
+DEV_MODULE(nandsim, nandsim_modevent, NULL);
+MODULE_VERSION(nandsim, 1);
diff --git a/sys/dev/nand/nandsim.h b/sys/dev/nand/nandsim.h
new file mode 100644
index 0000000..fbbb6d4
--- /dev/null
+++ b/sys/dev/nand/nandsim.h
@@ -0,0 +1,175 @@
+/*-
+ * 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$
+ */
+
+#ifndef _NANDSIM_H_
+#define _NANDSIM_H_
+
+#include <sys/ioccom.h>
+#include <sys/types.h>
+
+#define MAX_SIM_DEV 4
+#define MAX_CTRL_CS 4
+#define MAX_ECC_BYTES 512
+#define MAX_BAD_BLOCKS 512
+#define DEV_MODEL_STR_SIZE 21
+#define MAN_STR_SIZE 13
+#define FILENAME_SIZE 20
+
+#define MAX_CHIPS (MAX_SIM_DEV*MAX_CTRL_CS)
+
+#define NANDSIM_OUTPUT_NONE 0x0
+#define NANDSIM_OUTPUT_CONSOLE 0x1
+#define NANDSIM_OUTPUT_RAM 0x2
+#define NANDSIM_OUTPUT_FILE 0x3
+
+struct sim_ctrl_chip {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+
+#define NANDSIM_BASE 'A'
+
+struct sim_param {
+ uint8_t log_level;
+ uint8_t log_output;
+};
+
+#define NANDSIM_SIM_PARAM _IOW(NANDSIM_BASE, 1, struct sim_param)
+
+struct sim_ctrl {
+ uint8_t running;
+ uint8_t created;
+ uint8_t num;
+ uint8_t num_cs;
+ uint8_t ecc;
+ char filename[FILENAME_SIZE];
+ uint16_t ecc_layout[MAX_ECC_BYTES];
+};
+#define NANDSIM_CREATE_CTRL _IOW(NANDSIM_BASE, 2, struct sim_ctrl)
+#define NANDSIM_DESTROY_CTRL _IOW(NANDSIM_BASE, 3, int)
+
+struct sim_chip {
+ uint8_t num;
+ uint8_t ctrl_num;
+ uint8_t created;
+ uint8_t device_id;
+ uint8_t manufact_id;
+ char device_model[DEV_MODEL_STR_SIZE];
+ char manufacturer[MAN_STR_SIZE];
+ uint8_t col_addr_cycles;
+ uint8_t row_addr_cycles;
+ uint8_t features;
+ uint8_t width;
+ uint32_t page_size;
+ uint32_t oob_size;
+ uint32_t pgs_per_blk;
+ uint32_t blks_per_lun;
+ uint32_t luns;
+
+ uint32_t prog_time;
+ uint32_t erase_time;
+ uint32_t read_time;
+ uint32_t ccs_time;
+
+ uint32_t error_ratio;
+ uint32_t wear_level;
+ uint32_t bad_block_map[MAX_BAD_BLOCKS];
+ uint8_t is_wp;
+};
+
+#define NANDSIM_CREATE_CHIP _IOW(NANDSIM_BASE, 3, struct sim_chip)
+
+struct sim_chip_destroy {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+#define NANDSIM_DESTROY_CHIP _IOW(NANDSIM_BASE, 4, struct sim_chip_destroy)
+
+#define NANDSIM_START_CTRL _IOW(NANDSIM_BASE, 5, int)
+#define NANDSIM_STOP_CTRL _IOW(NANDSIM_BASE, 6, int)
+#define NANDSIM_RESTART_CTRL _IOW(NANDSIM_BASE, 7, int)
+
+#define NANDSIM_STATUS_CTRL _IOWR(NANDSIM_BASE, 8, struct sim_ctrl)
+#define NANDSIM_STATUS_CHIP _IOWR(NANDSIM_BASE, 9, struct sim_chip)
+
+struct sim_mod {
+ uint8_t chip_num;
+ uint8_t ctrl_num;
+ uint32_t field;
+ uint32_t new_value;
+};
+#define SIM_MOD_LOG_LEVEL 0
+#define SIM_MOD_ERASE_TIME 1
+#define SIM_MOD_PROG_TIME 2
+#define SIM_MOD_READ_TIME 3
+#define SIM_MOD_CCS_TIME 4
+#define SIM_MOD_ERROR_RATIO 5
+
+#define NANDSIM_MODIFY _IOW(NANDSIM_BASE, 10, struct sim_mod)
+#define NANDSIM_FREEZE _IOW(NANDSIM_BASE, 11, struct sim_ctrl_chip)
+
+struct sim_error {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t page_num;
+ uint32_t column;
+ uint32_t len;
+ uint32_t pattern;
+};
+#define NANDSIM_INJECT_ERROR _IOW(NANDSIM_BASE, 20, struct sim_error)
+
+#define NANDSIM_GOOD_BLOCK 0
+#define NANDSIM_BAD_BLOCK 1
+struct sim_block_state {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t block_num;
+ int wearout;
+ uint8_t state;
+};
+#define NANDSIM_SET_BLOCK_STATE _IOW(NANDSIM_BASE, 21, struct sim_block_state)
+#define NANDSIM_GET_BLOCK_STATE _IOWR(NANDSIM_BASE, 22, struct sim_block_state)
+
+struct sim_log {
+ uint8_t ctrl_num;
+ char* log;
+ size_t len;
+};
+#define NANDSIM_PRINT_LOG _IOWR(NANDSIM_BASE, 23, struct sim_log)
+
+struct sim_dump {
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+ uint32_t block_num;
+ uint32_t len;
+ void* data;
+};
+#define NANDSIM_DUMP _IOWR(NANDSIM_BASE, 24, struct sim_dump)
+#define NANDSIM_RESTORE _IOWR(NANDSIM_BASE, 25, struct sim_dump)
+
+#endif /* _NANDSIM_H_ */
diff --git a/sys/dev/nand/nandsim_chip.c b/sys/dev/nand/nandsim_chip.c
new file mode 100644
index 0000000..f04ad80
--- /dev/null
+++ b/sys/dev/nand/nandsim_chip.c
@@ -0,0 +1,901 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/proc.h>
+#include <sys/sched.h>
+#include <sys/kthread.h>
+#include <sys/unistd.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_swap.h>
+
+MALLOC_DEFINE(M_NANDSIM, "NANDsim", "NANDsim dynamic data");
+
+#define NANDSIM_CHIP_LOCK(chip) mtx_lock(&(chip)->ns_lock)
+#define NANDSIM_CHIP_UNLOCK(chip) mtx_unlock(&(chip)->ns_lock)
+
+static nandsim_evh_t erase_evh;
+static nandsim_evh_t idle_evh;
+static nandsim_evh_t poweron_evh;
+static nandsim_evh_t reset_evh;
+static nandsim_evh_t read_evh;
+static nandsim_evh_t readid_evh;
+static nandsim_evh_t readparam_evh;
+static nandsim_evh_t write_evh;
+
+static void nandsim_loop(void *);
+static void nandsim_undefined(struct nandsim_chip *, uint8_t);
+static void nandsim_bad_address(struct nandsim_chip *, uint8_t *);
+static void nandsim_ignore_address(struct nandsim_chip *, uint8_t);
+static void nandsim_sm_error(struct nandsim_chip *);
+static void nandsim_start_handler(struct nandsim_chip *, nandsim_evh_t);
+
+static void nandsim_callout_eh(void *);
+static int nandsim_delay(struct nandsim_chip *, int);
+
+static int nandsim_bbm_init(struct nandsim_chip *, uint32_t, uint32_t *);
+static int nandsim_blk_state_init(struct nandsim_chip *, uint32_t, uint32_t);
+static void nandsim_blk_state_destroy(struct nandsim_chip *);
+static int nandchip_is_block_valid(struct nandsim_chip *, int);
+
+static void nandchip_set_status(struct nandsim_chip *, uint8_t);
+static void nandchip_clear_status(struct nandsim_chip *, uint8_t);
+
+struct proc *nandsim_proc;
+
+struct nandsim_chip *
+nandsim_chip_init(struct nandsim_softc* sc, uint8_t chip_num,
+ struct sim_chip *sim_chip)
+{
+ struct nandsim_chip *chip;
+ struct onfi_params *chip_param;
+ char swapfile[20];
+ uint32_t size;
+ int error;
+
+ chip = malloc(sizeof(*chip), M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip)
+ return (NULL);
+
+ mtx_init(&chip->ns_lock, "nandsim lock", NULL, MTX_DEF);
+ callout_init(&chip->ns_callout, CALLOUT_MPSAFE);
+ STAILQ_INIT(&chip->nandsim_events);
+
+ chip->chip_num = chip_num;
+ chip->ctrl_num = sim_chip->ctrl_num;
+ chip->sc = sc;
+
+ if (!sim_chip->is_wp)
+ nandchip_set_status(chip, NAND_STATUS_WP);
+
+ chip_param = &chip->params;
+
+ chip->id.dev_id = sim_chip->device_id;
+ chip->id.man_id = sim_chip->manufact_id;
+
+ chip->error_ratio = sim_chip->error_ratio;
+ chip->wear_level = sim_chip->wear_level;
+ chip->prog_delay = sim_chip->prog_time;
+ chip->erase_delay = sim_chip->erase_time;
+ chip->read_delay = sim_chip->read_time;
+
+ chip_param->t_prog = sim_chip->prog_time;
+ chip_param->t_bers = sim_chip->erase_time;
+ chip_param->t_r = sim_chip->read_time;
+ bcopy("onfi", &chip_param->signature, 4);
+
+ chip_param->manufacturer_id = sim_chip->manufact_id;
+ strncpy(chip_param->manufacturer_name, sim_chip->manufacturer, 12);
+ chip_param->manufacturer_name[11] = 0;
+ strncpy(chip_param->device_model, sim_chip->device_model, 20);
+ chip_param->device_model[19] = 0;
+
+ chip_param->bytes_per_page = sim_chip->page_size;
+ chip_param->spare_bytes_per_page = sim_chip->oob_size;
+ chip_param->pages_per_block = sim_chip->pgs_per_blk;
+ chip_param->blocks_per_lun = sim_chip->blks_per_lun;
+ chip_param->luns = sim_chip->luns;
+
+ init_chip_geom(&chip->cg, chip_param->luns, chip_param->blocks_per_lun,
+ chip_param->pages_per_block, chip_param->bytes_per_page,
+ chip_param->spare_bytes_per_page);
+
+ chip_param->address_cycles = sim_chip->row_addr_cycles |
+ (sim_chip->col_addr_cycles << 4);
+ chip_param->features = sim_chip->features;
+ if (sim_chip->width == 16)
+ chip_param->features |= ONFI_FEAT_16BIT;
+
+ size = chip_param->blocks_per_lun * chip_param->luns;
+
+ error = nandsim_blk_state_init(chip, size, sim_chip->wear_level);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ error = nandsim_bbm_init(chip, size, sim_chip->bad_block_map);
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ nandsim_start_handler(chip, poweron_evh);
+
+ nand_debug(NDBG_SIM,"Create thread for chip%d [%8p]", chip->chip_num,
+ chip);
+ /* Create chip thread */
+ error = kproc_kthread_add(nandsim_loop, chip, &nandsim_proc,
+ &chip->nandsim_td, RFSTOPPED | RFHIGHPID,
+ 0, "nandsim", "chip");
+ if (error) {
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ free(chip, M_NANDSIM);
+ return (NULL);
+ }
+
+ thread_lock(chip->nandsim_td);
+ sched_class(chip->nandsim_td, PRI_REALTIME);
+ sched_add(chip->nandsim_td, SRQ_BORING);
+ thread_unlock(chip->nandsim_td);
+
+ size = (chip_param->bytes_per_page +
+ chip_param->spare_bytes_per_page) *
+ chip_param->pages_per_block;
+
+ sprintf(swapfile, "chip%d%d.swp", chip->ctrl_num, chip->chip_num);
+ chip->swap = nandsim_swap_init(swapfile, chip_param->blocks_per_lun *
+ chip_param->luns, size);
+ if (!chip->swap)
+ nandsim_chip_destroy(chip);
+
+ /* Wait for new thread to enter main loop */
+ tsleep(chip->nandsim_td, PWAIT, "ns_chip", 1 * hz);
+
+ return (chip);
+}
+
+static int
+nandsim_blk_state_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t wear_lev)
+{
+ int i;
+
+ if (!chip || size == 0)
+ return (-1);
+
+ chip->blk_state = malloc(size * sizeof(struct nandsim_block_state),
+ M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!chip->blk_state) {
+ return (-1);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (wear_lev)
+ chip->blk_state[i].wear_lev = wear_lev;
+ else
+ chip->blk_state[i].wear_lev = -1;
+ }
+
+ return (0);
+}
+
+static void
+nandsim_blk_state_destroy(struct nandsim_chip *chip)
+{
+
+ if (chip && chip->blk_state)
+ free(chip->blk_state, M_NANDSIM);
+}
+
+static int
+nandsim_bbm_init(struct nandsim_chip *chip, uint32_t size,
+ uint32_t *sim_bbm)
+{
+ uint32_t index;
+ int i;
+
+ if ((chip == NULL) || (size == 0))
+ return (-1);
+
+ if (chip->blk_state == NULL)
+ return (-1);
+
+ if (sim_bbm == NULL)
+ return (0);
+
+ for (i = 0; i < MAX_BAD_BLOCKS; i++) {
+ index = sim_bbm[i];
+
+ if (index == 0xffffffff)
+ break;
+ else if (index > size)
+ return (-1);
+ else
+ chip->blk_state[index].is_bad = 1;
+ }
+
+ return (0);
+}
+
+void
+nandsim_chip_destroy(struct nandsim_chip *chip)
+{
+ struct nandsim_ev *ev;
+
+ ev = create_event(chip, NANDSIM_EV_EXIT, 0);
+ if (ev)
+ send_event(ev);
+}
+
+void
+nandsim_chip_freeze(struct nandsim_chip *chip)
+{
+
+ chip->flags |= NANDSIM_CHIP_FROZEN;
+}
+
+static void
+nandsim_loop(void *arg)
+{
+ struct nandsim_chip *chip = (struct nandsim_chip *)arg;
+ struct nandsim_ev *ev;
+
+ nand_debug(NDBG_SIM,"Start main loop for chip%d [%8p]", chip->chip_num,
+ chip);
+ for(;;) {
+ NANDSIM_CHIP_LOCK(chip);
+ if (!(chip->flags & NANDSIM_CHIP_ACTIVE)) {
+ chip->flags |= NANDSIM_CHIP_ACTIVE;
+ wakeup(chip->nandsim_td);
+ }
+
+ if (STAILQ_EMPTY(&chip->nandsim_events)) {
+ nand_debug(NDBG_SIM,"Chip%d [%8p] going sleep",
+ chip->chip_num, chip);
+ msleep(chip, &chip->ns_lock, PRIBIO, "nandev", 0);
+ }
+
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+ if (ev->type == NANDSIM_EV_EXIT) {
+ NANDSIM_CHIP_LOCK(chip);
+ destroy_event(ev);
+ wakeup(ev);
+ while (!STAILQ_EMPTY(&chip->nandsim_events)) {
+ ev = STAILQ_FIRST(&chip->nandsim_events);
+ STAILQ_REMOVE_HEAD(&chip->nandsim_events,
+ links);
+ destroy_event(ev);
+ wakeup(ev);
+ };
+ NANDSIM_CHIP_UNLOCK(chip);
+ nandsim_log(chip, NANDSIM_LOG_SM, "destroyed\n");
+ mtx_destroy(&chip->ns_lock);
+ nandsim_blk_state_destroy(chip);
+ nandsim_swap_destroy(chip->swap);
+ free(chip, M_NANDSIM);
+ nandsim_proc = NULL;
+
+ kthread_exit();
+ }
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip [%x] get event [%x]",
+ chip->chip_num, ev->type);
+ chip->ev_handler(chip, ev->type, ev->data);
+ }
+
+ wakeup(ev);
+ destroy_event(ev);
+ }
+
+}
+
+struct nandsim_ev *
+create_event(struct nandsim_chip *chip, uint8_t type, uint8_t data_size)
+{
+ struct nandsim_ev *ev;
+
+ ev = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ if (!ev) {
+ nand_debug(NDBG_SIM,"Cannot create event");
+ return (NULL);
+ }
+
+ if (data_size > 0)
+ ev->data = malloc(sizeof(*ev), M_NANDSIM, M_NOWAIT | M_ZERO);
+ ev->type = type;
+ ev->chip = chip;
+
+ return (ev);
+}
+
+void
+destroy_event(struct nandsim_ev *ev)
+{
+
+ if (ev->data)
+ free(ev->data, M_NANDSIM);
+ free(ev, M_NANDSIM);
+}
+
+int
+send_event(struct nandsim_ev *ev)
+{
+ struct nandsim_chip *chip = ev->chip;
+
+ if (!(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ nand_debug(NDBG_SIM,"Chip%d [%p] send event %x",
+ chip->chip_num, chip, ev->type);
+
+ NANDSIM_CHIP_LOCK(chip);
+ STAILQ_INSERT_TAIL(&chip->nandsim_events, ev, links);
+ NANDSIM_CHIP_UNLOCK(chip);
+
+ wakeup(chip);
+ if ((ev->type != NANDSIM_EV_TIMEOUT) && chip->nandsim_td &&
+ (curthread != chip->nandsim_td))
+ tsleep(ev, PWAIT, "ns_ev", 5 * hz);
+ }
+
+ return (0);
+}
+
+static void
+nandsim_callout_eh(void *arg)
+{
+ struct nandsim_ev *ev = (struct nandsim_ev *)arg;
+
+ send_event(ev);
+}
+
+static int
+nandsim_delay(struct nandsim_chip *chip, int timeout)
+{
+ struct nandsim_ev *ev;
+ struct timeval delay;
+ int tm;
+
+ nand_debug(NDBG_SIM,"Chip[%d] Set delay: %d", chip->chip_num, timeout);
+
+ ev = create_event(chip, NANDSIM_EV_TIMEOUT, 0);
+ if (!ev)
+ return (-1);
+
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ tm = (timeout/10000) * (hz / 100);
+ if (callout_reset(&chip->ns_callout, tm, nandsim_callout_eh, ev))
+ return (-1);
+
+ delay.tv_sec = chip->read_delay / 1000000;
+ delay.tv_usec = chip->read_delay % 1000000;
+ timevaladd(&chip->delay_tv, &delay);
+
+ return (0);
+}
+
+static void
+nandsim_start_handler(struct nandsim_chip *chip, nandsim_evh_t evh)
+{
+ struct nandsim_ev *ev;
+
+ chip->ev_handler = evh;
+
+ nand_debug(NDBG_SIM,"Start handler %p for chip%d [%p]", evh,
+ chip->chip_num, chip);
+ ev = create_event(chip, NANDSIM_EV_START, 0);
+ if (!ev)
+ nandsim_sm_error(chip);
+
+ send_event(ev);
+}
+
+static void
+nandchip_set_data(struct nandsim_chip *chip, uint8_t *data, uint32_t len,
+ uint32_t idx)
+{
+
+ nand_debug(NDBG_SIM,"Chip [%x] data %p [%x] at %x", chip->chip_num,
+ data, len, idx);
+ chip->data.data_ptr = data;
+ chip->data.size = len;
+ chip->data.index = idx;
+}
+
+static int
+nandchip_chip_space(struct nandsim_chip *chip, int32_t row, int32_t column,
+ size_t size, uint8_t writing)
+{
+ struct block_space *blk_space;
+ uint32_t lun, block, page, offset, block_size;
+ int err;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun, &block, &page);
+ if (err) {
+ nand_debug(NDBG_SIM,"cannot get address\n");
+ return (-1);
+ }
+
+ if (!nandchip_is_block_valid(chip, block)) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ blk_space = get_bs(chip->swap, block, writing);
+ if (!blk_space) {
+ nandchip_set_data(chip, NULL, 0, 0);
+ return (-1);
+ }
+
+ if (size > block_size)
+ size = block_size;
+
+ if (size == block_size) {
+ offset = 0;
+ column = 0;
+ } else
+ offset = page * (chip->cg.page_size + chip->cg.oob_size);
+
+ nandchip_set_data(chip, &blk_space->blk_ptr[offset], size, column);
+
+ return (0);
+}
+
+static int
+nandchip_get_addr_byte(struct nandsim_chip *chip, void *data, uint32_t *value)
+{
+ int ncycles = 0;
+ uint8_t byte;
+ uint8_t *buffer;
+
+ buffer = (uint8_t *)value;
+ byte = *((uint8_t *)data);
+
+ KASSERT((chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW ||
+ chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL),
+ ("unexpected state"));
+
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ ncycles = chip->params.address_cycles & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ ncycles = (chip->params.address_cycles >> 4) & 0xf;
+ buffer[chip->sm_addr_cycle++] = byte;
+ }
+
+ nand_debug(NDBG_SIM, "Chip [%x] read addr byte: %02x (%d of %d)\n",
+ chip->chip_num, byte, chip->sm_addr_cycle, ncycles);
+
+ if (chip->sm_addr_cycle == ncycles) {
+ chip->sm_addr_cycle = 0;
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+nandchip_is_block_valid(struct nandsim_chip *chip, int block_num)
+{
+
+ if (!chip || !chip->blk_state)
+ return (0);
+
+ if (chip->blk_state[block_num].wear_lev == 0 ||
+ chip->blk_state[block_num].is_bad)
+ return (0);
+
+ return (1);
+}
+
+static void
+nandchip_set_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status |= flags;
+}
+
+static void
+nandchip_clear_status(struct nandsim_chip *chip, uint8_t flags)
+{
+
+ chip->chip_status &= ~flags;
+}
+
+uint8_t
+nandchip_get_status(struct nandsim_chip *chip)
+{
+ return (chip->chip_status);
+}
+
+void
+nandsim_chip_timeout(struct nandsim_chip *chip)
+{
+ struct timeval tv;
+
+ getmicrotime(&tv);
+
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT &&
+ timevalcmp(&tv, &chip->delay_tv, >=)) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ }
+}
+void
+poweron_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START)
+ chip->sm_state = NANDSIM_STATE_IDLE;
+ else if (type == NANDSIM_EV_CMD) {
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_RESET:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ nandsim_start_handler(chip, reset_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+idle_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ uint8_t cmd;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in IDLE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else if (type == NANDSIM_EV_CMD) {
+ nandchip_clear_status(chip, NAND_STATUS_FAIL);
+ getmicrotime(&chip->delay_tv);
+ cmd = *(uint8_t *)data;
+ switch(cmd) {
+ case NAND_CMD_READ_ID:
+ nandsim_start_handler(chip, readid_evh);
+ break;
+ case NAND_CMD_READ_PARAMETER:
+ nandsim_start_handler(chip, readparam_evh);
+ break;
+ case NAND_CMD_READ:
+ nandsim_start_handler(chip, read_evh);
+ break;
+ case NAND_CMD_PROG:
+ nandsim_start_handler(chip, write_evh);
+ break;
+ case NAND_CMD_ERASE:
+ nandsim_start_handler(chip, erase_evh);
+ break;
+ default:
+ nandsim_undefined(chip, type);
+ break;
+ }
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readid_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READID state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+
+ addr = *((uint8_t *)data);
+
+ if (addr == 0x0)
+ nandchip_set_data(chip, (uint8_t *)&chip->id, 2, 0);
+ else if (addr == ONFI_SIG_ADDR)
+ nandchip_set_data(chip, (uint8_t *)&params->signature,
+ 4, 0);
+ else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+readparam_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ struct onfi_params *params;
+ uint8_t addr;
+
+ params = &chip->params;
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READPARAM state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_BYTE;
+ } else if (type == NANDSIM_EV_ADDR) {
+ addr = *((uint8_t *)data);
+
+ if (addr == 0) {
+ nandchip_set_data(chip, (uint8_t *)params,
+ sizeof(*params), 0);
+ } else
+ nandsim_bad_address(chip, &addr);
+
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+void
+read_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column = 0, row = 0;
+ uint32_t size;
+ uint8_t cmd;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in READ state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_READ_END) {
+ if (chip->read_delay != 0 &&
+ nandsim_delay(chip, chip->read_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_chip_space(chip, row, column, size, 0);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+void
+write_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t column, row;
+ uint32_t size;
+ uint8_t cmd;
+ int err;
+
+ size = chip->cg.page_size + chip->cg.oob_size;
+
+ switch(type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in WRITE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_COL;
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_COL) {
+ if (nandchip_get_addr_byte(chip, data, &column))
+ break;
+
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ } else if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, column, size, 1);
+ if (err == -1)
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_PROG_END) {
+ if (chip->prog_delay != 0 &&
+ nandsim_delay(chip, chip->prog_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandsim_start_handler(chip, idle_evh);
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+erase_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+ static uint32_t row, block_size;
+ uint32_t lun, block, page;
+ int err;
+ uint8_t cmd;
+
+ block_size = chip->cg.block_size +
+ (chip->cg.oob_size * chip->cg.pgs_per_blk);
+
+ switch (type) {
+ case NANDSIM_EV_START:
+ nandsim_log(chip, NANDSIM_LOG_SM, "in ERASE state\n");
+ chip->sm_state = NANDSIM_STATE_WAIT_ADDR_ROW;
+ break;
+ case NANDSIM_EV_CMD:
+ cmd = *(uint8_t *)data;
+ if (chip->sm_state == NANDSIM_STATE_WAIT_CMD &&
+ cmd == NAND_CMD_ERASE_END) {
+ if (chip->data.data_ptr != NULL &&
+ chip->data.size == block_size)
+ memset(chip->data.data_ptr, 0xff, block_size);
+ else
+ nand_debug(NDBG_SIM,"Bad block erase data\n");
+
+ err = nand_row_to_blkpg(&chip->cg, row, &lun,
+ &block, &page);
+ if (!err) {
+ if (chip->blk_state[block].wear_lev > 0)
+ chip->blk_state[block].wear_lev--;
+ }
+
+ if (chip->erase_delay != 0 &&
+ nandsim_delay(chip, chip->erase_delay) == 0)
+ nandchip_clear_status(chip, NAND_STATUS_RDY);
+ else {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ }
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ case NANDSIM_EV_ADDR:
+ if (chip->sm_state == NANDSIM_STATE_WAIT_ADDR_ROW) {
+ if (nandchip_get_addr_byte(chip, data, &row))
+ break;
+
+ err = nandchip_chip_space(chip, row, 0, block_size, 1);
+ if (err == -1) {
+ nandchip_set_status(chip, NAND_STATUS_FAIL);
+ }
+ chip->sm_state = NANDSIM_STATE_WAIT_CMD;
+ } else
+ nandsim_ignore_address(chip, *((uint8_t *)data));
+ break;
+ case NANDSIM_EV_TIMEOUT:
+ if (chip->sm_state == NANDSIM_STATE_TIMEOUT) {
+ nandchip_set_status(chip, NAND_STATUS_RDY);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+ break;
+ }
+}
+
+void
+reset_evh(struct nandsim_chip *chip, uint32_t type, void *data)
+{
+
+ if (type == NANDSIM_EV_START) {
+ nandsim_log(chip, NANDSIM_LOG_SM, "in RESET state\n");
+ chip->sm_state = NANDSIM_STATE_TIMEOUT;
+ nandchip_set_data(chip, NULL, 0, 0);
+ DELAY(500);
+ nandsim_start_handler(chip, idle_evh);
+ } else
+ nandsim_undefined(chip, type);
+}
+
+static void
+nandsim_undefined(struct nandsim_chip *chip, uint8_t type)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received ev %x in state %x\n",
+ type, chip->sm_state);
+ nandsim_start_handler(chip, idle_evh);
+}
+
+static void
+nandsim_bad_address(struct nandsim_chip *chip, uint8_t *addr)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR,
+ "ERR: Chip received out of range address"
+ "%02x%02x - %02x%02x%02x\n", addr[0], addr[1], addr[2],
+ addr[3], addr[4]);
+}
+
+static void
+nandsim_ignore_address(struct nandsim_chip *chip, uint8_t byte)
+{
+ nandsim_log(chip, NANDSIM_LOG_SM, "ignored address byte: %d\n", byte);
+}
+
+static void
+nandsim_sm_error(struct nandsim_chip *chip)
+{
+
+ nandsim_log(chip, NANDSIM_LOG_ERR, "ERR: State machine error."
+ "Restart required.\n");
+}
diff --git a/sys/dev/nand/nandsim_chip.h b/sys/dev/nand/nandsim_chip.h
new file mode 100644
index 0000000..a6fb234
--- /dev/null
+++ b/sys/dev/nand/nandsim_chip.h
@@ -0,0 +1,159 @@
+/*-
+ * 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$
+ */
+
+#ifndef _NANDSIM_CHIP_H
+#define _NANDSIM_CHIP_H
+
+#include <sys/malloc.h>
+#include <sys/callout.h>
+#include <dev/nand/nand.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_swap.h>
+
+MALLOC_DECLARE(M_NANDSIM);
+
+#define MAX_CS_NUM 4
+struct nandsim_chip;
+
+typedef void nandsim_evh_t(struct nandsim_chip *chip, uint32_t ev, void *data);
+
+enum addr_type {
+ ADDR_NONE,
+ ADDR_ID,
+ ADDR_ROW,
+ ADDR_ROWCOL
+};
+
+struct nandsim_softc {
+ struct nand_softc nand_dev;
+ device_t dev;
+
+ struct nandsim_chip *chips[MAX_CS_NUM];
+ struct nandsim_chip *active_chip;
+
+ uint8_t address_cycle;
+ enum addr_type address_type;
+ int log_idx;
+ char *log_buff;
+ struct alq *alq;
+};
+
+struct nandsim_ev {
+ STAILQ_ENTRY(nandsim_ev) links;
+ struct nandsim_chip *chip;
+ uint8_t type;
+ void *data;
+};
+
+struct nandsim_data {
+ uint8_t *data_ptr;
+ uint32_t index;
+ uint32_t size;
+};
+
+struct nandsim_block_state {
+ int32_t wear_lev;
+ uint8_t is_bad;
+};
+
+#define NANDSIM_CHIP_ACTIVE 0x1
+#define NANDSIM_CHIP_FROZEN 0x2
+#define NANDSIM_CHIP_GET_STATUS 0x4
+
+struct nandsim_chip {
+ struct nandsim_softc *sc;
+ struct thread *nandsim_td;
+
+ STAILQ_HEAD(, nandsim_ev) nandsim_events;
+ nandsim_evh_t *ev_handler;
+ struct mtx ns_lock;
+ struct callout ns_callout;
+
+ struct chip_geom cg;
+ struct nand_id id;
+ struct onfi_params params;
+ struct nandsim_data data;
+ struct nandsim_block_state *blk_state;
+
+ struct chip_swap *swap;
+
+ uint32_t error_ratio;
+ uint32_t wear_level;
+ uint32_t sm_state;
+ uint32_t sm_addr_cycle;
+
+ uint32_t erase_delay;
+ uint32_t prog_delay;
+ uint32_t read_delay;
+ struct timeval delay_tv;
+
+ uint8_t flags;
+ uint8_t chip_status;
+ uint8_t ctrl_num;
+ uint8_t chip_num;
+};
+
+struct sim_ctrl_conf {
+ uint8_t num;
+ uint8_t num_cs;
+ uint8_t ecc;
+ uint8_t running;
+ uint8_t created;
+ device_t sim_ctrl_dev;
+ struct sim_chip *chips[MAX_CTRL_CS];
+ uint16_t ecc_layout[MAX_ECC_BYTES];
+ char filename[FILENAME_SIZE];
+};
+
+#define NANDSIM_STATE_IDLE 0x0
+#define NANDSIM_STATE_WAIT_ADDR_BYTE 0x1
+#define NANDSIM_STATE_WAIT_CMD 0x2
+#define NANDSIM_STATE_TIMEOUT 0x3
+#define NANDSIM_STATE_WAIT_ADDR_ROW 0x4
+#define NANDSIM_STATE_WAIT_ADDR_COL 0x5
+
+#define NANDSIM_EV_START 0x1
+#define NANDSIM_EV_CMD 0x2
+#define NANDSIM_EV_ADDR 0x3
+#define NANDSIM_EV_TIMEOUT 0x4
+#define NANDSIM_EV_EXIT 0xff
+
+struct nandsim_chip *nandsim_chip_init(struct nandsim_softc *,
+ uint8_t, struct sim_chip *);
+void nandsim_chip_destroy(struct nandsim_chip *);
+void nandsim_chip_freeze(struct nandsim_chip *);
+void nandsim_chip_timeout(struct nandsim_chip *);
+int nandsim_chip_check_bad_block(struct nandsim_chip *, int);
+
+uint8_t nandchip_get_status(struct nandsim_chip *);
+
+void destroy_event(struct nandsim_ev *);
+int send_event(struct nandsim_ev *);
+struct nandsim_ev *create_event(struct nandsim_chip *, uint8_t, uint8_t);
+
+#endif /* _NANDSIM_CHIP_H */
diff --git a/sys/dev/nand/nandsim_ctrl.c b/sys/dev/nand/nandsim_ctrl.c
new file mode 100644
index 0000000..5f7b019
--- /dev/null
+++ b/sys/dev/nand/nandsim_ctrl.c
@@ -0,0 +1,396 @@
+/*-
+ * 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.
+ */
+
+/* Simulated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include <dev/nand/nandsim.h>
+#include <dev/nand/nandsim_log.h>
+#include <dev/nand/nandsim_chip.h>
+#include "nfc_if.h"
+
+#define ADDRESS_SIZE 5
+
+extern struct sim_ctrl_conf ctrls[MAX_SIM_DEV];
+
+static void byte_corrupt(struct nandsim_chip *, uint8_t *);
+
+static int nandsim_attach(device_t);
+static int nandsim_detach(device_t);
+static int nandsim_probe(device_t);
+
+static uint8_t nandsim_read_byte(device_t);
+static uint16_t nandsim_read_word(device_t);
+static int nandsim_select_cs(device_t, uint8_t);
+static void nandsim_write_byte(device_t, uint8_t);
+static void nandsim_write_word(device_t, uint16_t);
+static void nandsim_read_buf(device_t, void *, uint32_t);
+static void nandsim_write_buf(device_t, void *, uint32_t);
+static int nandsim_send_command(device_t, uint8_t);
+static int nandsim_send_address(device_t, uint8_t);
+
+static device_method_t nandsim_methods[] = {
+ DEVMETHOD(device_probe, nandsim_probe),
+ DEVMETHOD(device_attach, nandsim_attach),
+ DEVMETHOD(device_detach, nandsim_detach),
+
+ DEVMETHOD(nfc_select_cs, nandsim_select_cs),
+ DEVMETHOD(nfc_send_command, nandsim_send_command),
+ DEVMETHOD(nfc_send_address, nandsim_send_address),
+ DEVMETHOD(nfc_read_byte, nandsim_read_byte),
+ DEVMETHOD(nfc_read_word, nandsim_read_word),
+ DEVMETHOD(nfc_write_byte, nandsim_write_byte),
+ DEVMETHOD(nfc_read_buf, nandsim_read_buf),
+ DEVMETHOD(nfc_write_buf, nandsim_write_buf),
+
+ { 0, 0 },
+};
+
+static driver_t nandsim_driver = {
+ "nandsim",
+ nandsim_methods,
+ sizeof(struct nandsim_softc),
+};
+
+static devclass_t nandsim_devclass;
+DRIVER_MODULE(nandsim, nexus, nandsim_driver, nandsim_devclass, 0, 0);
+DRIVER_MODULE(nandbus, nandsim, nandbus_driver, nandbus_devclass, 0, 0);
+
+static int
+nandsim_probe(device_t dev)
+{
+
+ device_set_desc(dev, "NAND controller simulator");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+nandsim_attach(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct sim_ctrl_conf *params;
+ struct sim_chip *chip;
+ uint16_t *eccpos;
+ int i, err;
+
+ sc = device_get_softc(dev);
+ params = &ctrls[device_get_unit(dev)];
+
+ if (strlen(params->filename) == 0)
+ snprintf(params->filename, FILENAME_SIZE, "ctrl%d.log",
+ params->num);
+
+ nandsim_log_init(sc, params->filename);
+ for (i = 0; i < params->num_cs; i++) {
+ chip = params->chips[i];
+ if (chip && chip->device_id != 0) {
+ sc->chips[i] = nandsim_chip_init(sc, i, chip);
+ if (chip->features & ONFI_FEAT_16BIT)
+ sc->nand_dev.flags |= NAND_16_BIT;
+ }
+ }
+
+ if (params->ecc_layout[0] != 0xffff)
+ eccpos = params->ecc_layout;
+ else
+ eccpos = NULL;
+
+ nand_init(&sc->nand_dev, dev, params->ecc, 0, 0, eccpos, "nandsim");
+
+ err = nandbus_create(dev);
+
+ return (err);
+}
+
+static int
+nandsim_detach(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct sim_ctrl_conf *params;
+ int i;
+
+ sc = device_get_softc(dev);
+ params = &ctrls[device_get_unit(dev)];
+
+ for (i = 0; i < params->num_cs; i++)
+ if (sc->chips[i] != NULL)
+ nandsim_chip_destroy(sc->chips[i]);
+
+ nandsim_log_close(sc);
+
+ return (0);
+}
+
+static int
+nandsim_select_cs(device_t dev, uint8_t cs)
+{
+ struct nandsim_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (cs >= MAX_CS_NUM)
+ return (EINVAL);
+
+ sc->active_chip = sc->chips[cs];
+
+ if (sc->active_chip)
+ nandsim_log(sc->active_chip, NANDSIM_LOG_EV,
+ "Select cs %d\n", cs);
+
+ return (0);
+}
+
+static int
+nandsim_send_command(device_t dev, uint8_t command)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ struct nandsim_ev *ev;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip == NULL)
+ return (0);
+
+ nandsim_log(chip, NANDSIM_LOG_EV, "Send command %x\n", command);
+
+ switch (command) {
+ case NAND_CMD_READ_ID:
+ case NAND_CMD_READ_PARAMETER:
+ sc->address_type = ADDR_ID;
+ break;
+ case NAND_CMD_ERASE:
+ sc->address_type = ADDR_ROW;
+ break;
+ case NAND_CMD_READ:
+ case NAND_CMD_PROG:
+ sc->address_type = ADDR_ROWCOL;
+ break;
+ default:
+ sc->address_type = ADDR_NONE;
+ break;
+ }
+
+ if (command == NAND_CMD_STATUS)
+ chip->flags |= NANDSIM_CHIP_GET_STATUS;
+ else {
+ ev = create_event(chip, NANDSIM_EV_CMD, 1);
+ *(uint8_t *)ev->data = command;
+ send_event(ev);
+ }
+
+ return (0);
+}
+
+static int
+nandsim_send_address(device_t dev, uint8_t addr)
+{
+ struct nandsim_ev *ev;
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip == NULL)
+ return (0);
+
+ KASSERT((sc->address_type != ADDR_NONE), ("unexpected address"));
+ nandsim_log(chip, NANDSIM_LOG_EV, "Send addr %x\n", addr);
+
+ ev = create_event(chip, NANDSIM_EV_ADDR, 1);
+
+ *((uint8_t *)(ev->data)) = addr;
+
+ send_event(ev);
+ return (0);
+}
+
+static uint8_t
+nandsim_read_byte(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint8_t ret = 0xff;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if (chip->flags & NANDSIM_CHIP_GET_STATUS) {
+ nandsim_chip_timeout(chip);
+ ret = nandchip_get_status(chip);
+ chip->flags &= ~NANDSIM_CHIP_GET_STATUS;
+ } else if (chip->data.index < chip->data.size) {
+ ret = chip->data.data_ptr[chip->data.index++];
+ byte_corrupt(chip, &ret);
+ }
+ nandsim_log(chip, NANDSIM_LOG_DATA, "read %02x\n", ret);
+ }
+
+ return (ret);
+}
+
+static uint16_t
+nandsim_read_word(device_t dev)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint16_t *data_ptr;
+ uint16_t ret = 0xffff;
+ uint8_t *byte_ret = (uint8_t *)&ret;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if (chip->data.index < chip->data.size - 1) {
+ data_ptr =
+ (uint16_t *)&(chip->data.data_ptr[chip->data.index]);
+ ret = *data_ptr;
+ chip->data.index += 2;
+ byte_corrupt(chip, byte_ret);
+ byte_corrupt(chip, byte_ret + 1);
+ }
+ nandsim_log(chip, NANDSIM_LOG_DATA, "read %04x\n", ret);
+ }
+
+ return (ret);
+}
+
+static void
+nandsim_write_byte(device_t dev, uint8_t byte)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN) &&
+ (chip->data.index < chip->data.size)) {
+ byte_corrupt(chip, &byte);
+ chip->data.data_ptr[chip->data.index] &= byte;
+ chip->data.index++;
+ nandsim_log(chip, NANDSIM_LOG_DATA, "write %02x\n", byte);
+ }
+}
+
+static void
+nandsim_write_word(device_t dev, uint16_t word)
+{
+ struct nandsim_softc *sc;
+ struct nandsim_chip *chip;
+ uint16_t *data_ptr;
+ uint8_t *byte_ptr = (uint8_t *)&word;
+
+ sc = device_get_softc(dev);
+ chip = sc->active_chip;
+
+ if (chip && !(chip->flags & NANDSIM_CHIP_FROZEN)) {
+ if ((chip->data.index + 1) < chip->data.size) {
+ byte_corrupt(chip, byte_ptr);
+ byte_corrupt(chip, byte_ptr + 1);
+ data_ptr =
+ (uint16_t *)&(chip->data.data_ptr[chip->data.index]);
+ *data_ptr &= word;
+ chip->data.index += 2;
+ }
+
+ nandsim_log(chip, NANDSIM_LOG_DATA, "write %04x\n", word);
+ }
+}
+
+static void
+nandsim_read_buf(device_t dev, void *buf, uint32_t len)
+{
+ struct nandsim_softc *sc;
+ uint16_t *buf16 = (uint16_t *)buf;
+ uint8_t *buf8 = (uint8_t *)buf;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (sc->nand_dev.flags & NAND_16_BIT) {
+ for (i = 0; i < len / 2; i++)
+ buf16[i] = nandsim_read_word(dev);
+ } else {
+ for (i = 0; i < len; i++)
+ buf8[i] = nandsim_read_byte(dev);
+ }
+}
+
+static void
+nandsim_write_buf(device_t dev, void *buf, uint32_t len)
+{
+ struct nandsim_softc *sc;
+ uint16_t *buf16 = (uint16_t *)buf;
+ uint8_t *buf8 = (uint8_t *)buf;
+ int i;
+
+ sc = device_get_softc(dev);
+
+ if (sc->nand_dev.flags & NAND_16_BIT) {
+ for (i = 0; i < len / 2; i++)
+ nandsim_write_word(dev, buf16[i]);
+ } else {
+ for (i = 0; i < len; i++)
+ nandsim_write_byte(dev, buf8[i]);
+ }
+}
+
+static void
+byte_corrupt(struct nandsim_chip *chip, uint8_t *byte)
+{
+ uint32_t rand;
+ uint8_t bit;
+
+ rand = random();
+ if ((rand % 1000000) < chip->error_ratio) {
+ bit = rand % 8;
+ if (*byte & (1 << bit))
+ *byte &= ~(1 << bit);
+ else
+ *byte |= (1 << bit);
+ }
+}
diff --git a/sys/dev/nand/nandsim_log.c b/sys/dev/nand/nandsim_log.c
new file mode 100644
index 0000000..c51118f
--- /dev/null
+++ b/sys/dev/nand/nandsim_log.c
@@ -0,0 +1,186 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/alq.h>
+#include <sys/time.h>
+
+#include <machine/stdarg.h>
+
+#include <dev/nand/nandsim_log.h>
+
+int nandsim_log_level;
+int nandsim_log_output;
+int log_size = NANDSIM_RAM_LOG_SIZE;
+
+static int nandsim_entry_size = NANDSIM_ENTRY_SIZE;
+static int nandsim_entry_count = NANDSIM_ENTRY_COUNT;
+static int str_index = 0;
+static char string[NANDSIM_ENTRY_SIZE + 1] = {0};
+
+int
+nandsim_log_init(struct nandsim_softc *sc, char *filename)
+{
+ int error = 0;
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
+ error = alq_open(&sc->alq, filename,
+ curthread->td_ucred, 0644,
+ nandsim_entry_size, nandsim_entry_count);
+ } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
+ sc->log_buff = malloc(log_size, M_NANDSIM, M_WAITOK | M_ZERO);
+ if (!sc->log_buff)
+ error = ENOMEM;
+ }
+
+ return (error);
+}
+
+void
+nandsim_log_close(struct nandsim_softc *sc)
+{
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_FILE) {
+ memset(&string[str_index], 0, NANDSIM_ENTRY_SIZE - str_index);
+ alq_write(sc->alq, (void *) string, ALQ_NOWAIT);
+ str_index = 0;
+ string[0] = '\0';
+ alq_close(sc->alq);
+ } else if (nandsim_log_output == NANDSIM_OUTPUT_RAM) {
+ free(sc->log_buff, M_NANDSIM);
+ sc->log_buff = NULL;
+ }
+}
+
+void
+nandsim_log(struct nandsim_chip *chip, int level, const char *fmt, ...)
+{
+ char hdr[TIME_STR_SIZE];
+ char tmp[NANDSIM_ENTRY_SIZE];
+ struct nandsim_softc *sc;
+ struct timeval currtime;
+ va_list ap;
+ int hdr_len, len, rest;
+
+ if (nandsim_log_output == NANDSIM_OUTPUT_NONE)
+ return;
+
+ if (chip == NULL)
+ return;
+
+ sc = chip->sc;
+ if (!sc->alq && nandsim_log_output == NANDSIM_OUTPUT_FILE)
+ return;
+
+ if (level <= nandsim_log_level) {
+ microtime(&currtime);
+ hdr_len = sprintf(hdr, "%08jd.%08li [chip:%d, ctrl:%d]: ",
+ (intmax_t)currtime.tv_sec, currtime.tv_usec,
+ chip->chip_num, chip->ctrl_num);
+
+ switch(nandsim_log_output) {
+ case NANDSIM_OUTPUT_CONSOLE:
+ printf("%s", hdr);
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ break;
+ case NANDSIM_OUTPUT_RAM:
+ va_start(ap, fmt);
+ len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
+ tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
+ va_end(ap);
+
+ rest = log_size - sc->log_idx - 1;
+ if (rest >= hdr_len) {
+ bcopy(hdr, &sc->log_buff[sc->log_idx],
+ hdr_len);
+ sc->log_idx += hdr_len;
+ sc->log_buff[sc->log_idx] = 0;
+ } else {
+ bcopy(hdr, &sc->log_buff[sc->log_idx], rest);
+ bcopy(&hdr[rest], sc->log_buff,
+ hdr_len - rest);
+ sc->log_idx = hdr_len - rest;
+ sc->log_buff[sc->log_idx] = 0;
+ }
+
+ rest = log_size - sc->log_idx - 1;
+ if (rest >= len) {
+ bcopy(tmp, &sc->log_buff[sc->log_idx], len);
+ sc->log_idx += len;
+ sc->log_buff[sc->log_idx] = 0;
+ } else {
+ bcopy(tmp, &sc->log_buff[sc->log_idx], rest);
+ bcopy(&tmp[rest], sc->log_buff, len - rest);
+ sc->log_idx = len - rest;
+ sc->log_buff[sc->log_idx] = 0;
+ }
+
+ break;
+
+ case NANDSIM_OUTPUT_FILE:
+ va_start(ap, fmt);
+ len = vsnprintf(tmp, NANDSIM_ENTRY_SIZE - 1, fmt, ap);
+ tmp[NANDSIM_ENTRY_SIZE - 1] = 0;
+ va_end(ap);
+
+ rest = NANDSIM_ENTRY_SIZE - str_index;
+ if (rest >= hdr_len) {
+ strcat(string, hdr);
+ str_index += hdr_len;
+ } else {
+ strlcat(string, hdr, NANDSIM_ENTRY_SIZE + 1);
+ alq_write(sc->alq, (void *) string,
+ ALQ_NOWAIT);
+ strcpy(string, &hdr[rest]);
+ str_index = hdr_len - rest;
+ }
+ rest = NANDSIM_ENTRY_SIZE - str_index;
+ if (rest >= len) {
+ strcat(string, tmp);
+ str_index += len;
+ } else {
+ strlcat(string, tmp, NANDSIM_ENTRY_SIZE + 1);
+ alq_write(sc->alq, (void *) string,
+ ALQ_NOWAIT);
+ strcpy(string, &tmp[rest]);
+ str_index = len - rest;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/sys/dev/nand/nandsim_log.h b/sys/dev/nand/nandsim_log.h
new file mode 100644
index 0000000..8cf1082
--- /dev/null
+++ b/sys/dev/nand/nandsim_log.h
@@ -0,0 +1,52 @@
+/*-
+ * 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$
+ */
+
+#ifndef _NANDSIM_LOG_H
+#define _NANDSIM_LOG_H
+
+#include <dev/nand/nandsim_chip.h>
+
+#define NANDSIM_ENTRY_SIZE 128
+#define NANDSIM_ENTRY_COUNT 1024
+#define NANDSIM_RAM_LOG_SIZE 16384
+#define TIME_STR_SIZE 40
+
+#define NANDSIM_LOG_ERR 1
+#define NANDSIM_LOG_SM 5
+#define NANDSIM_LOG_EV 10
+#define NANDSIM_LOG_DATA 15
+
+extern int nandsim_log_level;
+extern int nandsim_log_output;
+
+int nandsim_log_init(struct nandsim_softc *, char *);
+void nandsim_log_close(struct nandsim_softc *);
+void nandsim_log(struct nandsim_chip *, int, const char *, ...);
+
+#endif /* _NANDSIM_LOG_H */
+
diff --git a/sys/dev/nand/nandsim_swap.c b/sys/dev/nand/nandsim_swap.c
new file mode 100644
index 0000000..cc4201d
--- /dev/null
+++ b/sys/dev/nand/nandsim_swap.c
@@ -0,0 +1,389 @@
+/*-
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/namei.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+
+#include <dev/nand/nandsim_chip.h>
+#include <dev/nand/nandsim_swap.h>
+
+static int init_block_state(struct chip_swap *);
+static void destroy_block_state(struct chip_swap *);
+
+static int create_buffers(struct chip_swap *);
+static void destroy_buffers(struct chip_swap *);
+
+static int swap_file_open(struct chip_swap *, const char *);
+static void swap_file_close(struct chip_swap *);
+static int swap_file_write(struct chip_swap *, struct block_state *);
+static int swap_file_read(struct chip_swap *, struct block_state *);
+
+#define CHIP_SWAP_CMODE 0600
+#define CHIP_SWAP_BLOCKSPACES 2
+
+static int
+init_block_state(struct chip_swap *swap)
+{
+ struct block_state *blk_state;
+ int i;
+
+ if (swap == NULL)
+ return (-1);
+
+ blk_state = malloc(swap->nof_blks * sizeof(struct block_state),
+ M_NANDSIM, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < swap->nof_blks; i++)
+ blk_state[i].offset = 0xffffffff;
+
+ swap->blk_state = blk_state;
+
+ return (0);
+}
+
+static void
+destroy_block_state(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ if (swap->blk_state != NULL)
+ free(swap->blk_state, M_NANDSIM);
+}
+
+static int
+create_buffers(struct chip_swap *swap)
+{
+ struct block_space *block_space;
+ void *block;
+ int i;
+
+ for (i = 0; i < CHIP_SWAP_BLOCKSPACES; i++) {
+ block_space = malloc(sizeof(*block_space), M_NANDSIM, M_WAITOK);
+ block = malloc(swap->blk_size, M_NANDSIM, M_WAITOK);
+ block_space->blk_ptr = block;
+ SLIST_INSERT_HEAD(&swap->free_bs, block_space, free_link);
+ nand_debug(NDBG_SIM,"created blk_space %p[%p]\n", block_space,
+ block);
+ }
+
+ if (i == 0)
+ return (-1);
+
+ return (0);
+}
+
+static void
+destroy_buffers(struct chip_swap *swap)
+{
+ struct block_space *blk_space;
+
+ if (swap == NULL)
+ return;
+
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ while (blk_space) {
+ SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
+ nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
+ blk_space, blk_space->blk_ptr);
+ free(blk_space->blk_ptr, M_NANDSIM);
+ free(blk_space, M_NANDSIM);
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ }
+
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ while (blk_space) {
+ STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
+ nand_debug(NDBG_SIM,"destroyed blk_space %p[%p]\n",
+ blk_space, blk_space->blk_ptr);
+ free(blk_space->blk_ptr, M_NANDSIM);
+ free(blk_space, M_NANDSIM);
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ }
+}
+
+static int
+swap_file_open(struct chip_swap *swap, const char *swap_file)
+{
+ struct nameidata nd;
+ int vfslocked, flags, error;
+
+ NDINIT(&nd, LOOKUP, NOFOLLOW | MPSAFE, UIO_SYSSPACE, swap_file,
+ curthread);
+
+ flags = FWRITE | FREAD | O_NOFOLLOW | O_CREAT | O_TRUNC;
+
+ error = vn_open(&nd, &flags, CHIP_SWAP_CMODE, NULL);
+ if (error) {
+ nand_debug(NDBG_SIM,"Cannot create swap file %s", swap_file);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+ return (error);
+ }
+
+ swap->swap_cred = crhold(curthread->td_ucred);
+ vfslocked = NDHASGIANT(&nd);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ /* We just unlock so we hold a reference */
+ VOP_UNLOCK(nd.ni_vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ swap->swap_vp = nd.ni_vp;
+
+ return (0);
+}
+
+static void
+swap_file_close(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ if (swap->swap_vp == NULL)
+ return;
+
+ vn_close(swap->swap_vp, FWRITE, swap->swap_cred, curthread);
+ crfree(swap->swap_cred);
+}
+
+static int
+swap_file_write(struct chip_swap *swap, struct block_state *blk_state)
+{
+ struct block_space *blk_space;
+ struct thread *td;
+ struct mount *mp;
+ struct vnode *vp;
+ struct uio auio;
+ struct iovec aiov;
+ int vfslocked;
+
+ if (swap == NULL || blk_state == NULL)
+ return (-1);
+
+ blk_space = blk_state->blk_sp;
+ if (blk_state->offset == -1) {
+ blk_state->offset = swap->swap_offset;
+ swap->swap_offset += swap->blk_size;
+ }
+
+ nand_debug(NDBG_SIM,"saving %p[%p] at %x\n",
+ blk_space, blk_space->blk_ptr, blk_state->offset);
+
+ bzero(&aiov, sizeof(aiov));
+ bzero(&auio, sizeof(auio));
+
+ aiov.iov_base = blk_space->blk_ptr;
+ aiov.iov_len = swap->blk_size;
+ td = curthread;
+ vp = swap->swap_vp;
+
+ auio.uio_iov = &aiov;
+ auio.uio_offset = blk_state->offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = swap->blk_size;
+ auio.uio_td = td;
+
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ vn_start_write(vp, &mp, V_WAIT);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ VOP_WRITE(vp, &auio, IO_UNIT, swap->swap_cred);
+ VOP_UNLOCK(vp, 0);
+ vn_finished_write(mp);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ return (0);
+}
+
+static int
+swap_file_read(struct chip_swap *swap, struct block_state *blk_state)
+{
+ struct block_space *blk_space;
+ struct thread *td;
+ struct vnode *vp;
+ struct uio auio;
+ struct iovec aiov;
+ int vfslocked;
+
+ if (swap == NULL || blk_state == NULL)
+ return (-1);
+
+ blk_space = blk_state->blk_sp;
+
+ nand_debug(NDBG_SIM,"restore %p[%p] at %x\n",
+ blk_space, blk_space->blk_ptr, blk_state->offset);
+
+ bzero(&aiov, sizeof(aiov));
+ bzero(&auio, sizeof(auio));
+
+ aiov.iov_base = blk_space->blk_ptr;
+ aiov.iov_len = swap->blk_size;
+ td = curthread;
+ vp = swap->swap_vp;
+
+ auio.uio_iov = &aiov;
+ auio.uio_offset = blk_state->offset;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_iovcnt = 1;
+ auio.uio_resid = swap->blk_size;
+ auio.uio_td = td;
+
+ vfslocked = VFS_LOCK_GIANT(vp->v_mount);
+ vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
+ VOP_READ(vp, &auio, 0, swap->swap_cred);
+ VOP_UNLOCK(vp, 0);
+ VFS_UNLOCK_GIANT(vfslocked);
+
+ return (0);
+}
+
+struct chip_swap *
+nandsim_swap_init(const char *swap_file, uint32_t nof_blks, uint32_t blk_size)
+{
+ struct chip_swap *swap;
+ int err = 0;
+
+ if ((swap_file == NULL) || (nof_blks == 0) || (blk_size == 0))
+ return (NULL);
+
+ swap = malloc(sizeof(*swap), M_NANDSIM, M_WAITOK | M_ZERO);
+
+ SLIST_INIT(&swap->free_bs);
+ STAILQ_INIT(&swap->used_bs);
+ swap->blk_size = blk_size;
+ swap->nof_blks = nof_blks;
+
+ err = init_block_state(swap);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ err = create_buffers(swap);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ err = swap_file_open(swap, swap_file);
+ if (err) {
+ nandsim_swap_destroy(swap);
+ return (NULL);
+ }
+
+ return (swap);
+}
+
+void
+nandsim_swap_destroy(struct chip_swap *swap)
+{
+
+ if (swap == NULL)
+ return;
+
+ destroy_block_state(swap);
+ destroy_buffers(swap);
+ swap_file_close(swap);
+ free(swap, M_NANDSIM);
+}
+
+struct block_space *
+get_bs(struct chip_swap *swap, uint32_t block, uint8_t writing)
+{
+ struct block_state *blk_state, *old_blk_state = NULL;
+ struct block_space *blk_space;
+
+ if (swap == NULL || (block >= swap->nof_blks))
+ return (NULL);
+
+ blk_state = &swap->blk_state[block];
+ nand_debug(NDBG_SIM,"blk_state %x\n", blk_state->status);
+
+ if (blk_state->status & BLOCK_ALLOCATED) {
+ blk_space = blk_state->blk_sp;
+ } else {
+ blk_space = SLIST_FIRST(&swap->free_bs);
+ if (blk_space) {
+ SLIST_REMOVE_HEAD(&swap->free_bs, free_link);
+ STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
+ used_link);
+ } else {
+ blk_space = STAILQ_FIRST(&swap->used_bs);
+ old_blk_state = blk_space->blk_state;
+ STAILQ_REMOVE_HEAD(&swap->used_bs, used_link);
+ STAILQ_INSERT_TAIL(&swap->used_bs, blk_space,
+ used_link);
+ if (old_blk_state->status & BLOCK_DIRTY) {
+ swap_file_write(swap, old_blk_state);
+ old_blk_state->status &= ~BLOCK_DIRTY;
+ old_blk_state->status |= BLOCK_SWAPPED;
+ }
+ }
+ }
+
+ if (blk_space == NULL)
+ return (NULL);
+
+ if (old_blk_state != NULL) {
+ old_blk_state->status &= ~BLOCK_ALLOCATED;
+ old_blk_state->blk_sp = NULL;
+ }
+
+ blk_state->blk_sp = blk_space;
+ blk_space->blk_state = blk_state;
+
+ if (!(blk_state->status & BLOCK_ALLOCATED)) {
+ if (blk_state->status & BLOCK_SWAPPED)
+ swap_file_read(swap, blk_state);
+ else
+ memset(blk_space->blk_ptr, 0xff, swap->blk_size);
+ blk_state->status |= BLOCK_ALLOCATED;
+ }
+
+ if (writing)
+ blk_state->status |= BLOCK_DIRTY;
+
+ nand_debug(NDBG_SIM,"get_bs returned %p[%p] state %x\n", blk_space,
+ blk_space->blk_ptr, blk_state->status);
+
+ return (blk_space);
+}
diff --git a/sys/dev/nand/nandsim_swap.h b/sys/dev/nand/nandsim_swap.h
new file mode 100644
index 0000000..4fbae2a
--- /dev/null
+++ b/sys/dev/nand/nandsim_swap.h
@@ -0,0 +1,64 @@
+/*-
+ * 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$
+ */
+
+#ifndef _NANDSIM_SWAP_CHIP_H_
+#define _NANDSIM_SWAP_CHIP_H_
+
+struct block_space {
+ SLIST_ENTRY(block_space) free_link;
+ STAILQ_ENTRY(block_space) used_link;
+ struct block_state *blk_state;
+ uint8_t *blk_ptr;
+};
+
+#define BLOCK_ALLOCATED 0x1
+#define BLOCK_SWAPPED 0x2
+#define BLOCK_DIRTY 0x4
+
+struct block_state {
+ struct block_space *blk_sp;
+ uint32_t offset;
+ uint8_t status;
+};
+
+struct chip_swap {
+ struct block_state *blk_state;
+ SLIST_HEAD(,block_space) free_bs;
+ STAILQ_HEAD(,block_space) used_bs;
+ struct ucred *swap_cred;
+ struct vnode *swap_vp;
+ uint32_t swap_offset;
+ uint32_t blk_size;
+ uint32_t nof_blks;
+};
+
+struct chip_swap *nandsim_swap_init(const char *, uint32_t, uint32_t);
+void nandsim_swap_destroy(struct chip_swap *);
+struct block_space *get_bs(struct chip_swap *, uint32_t, uint8_t);
+
+#endif /* _NANDSIM_SWAP_CHIP_H_ */
diff --git a/sys/dev/nand/nfc_if.m b/sys/dev/nand/nfc_if.m
new file mode 100644
index 0000000..a4e1099
--- /dev/null
+++ b/sys/dev/nand/nfc_if.m
@@ -0,0 +1,165 @@
+#-
+# 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$
+
+# NAND controller interface description
+#
+
+#include <sys/bus.h>
+#include <dev/nand/nand.h>
+
+INTERFACE nfc;
+
+CODE {
+ static int nfc_default_method(device_t dev)
+ {
+ return (0);
+ }
+
+ static int nfc_softecc_get(device_t dev, void *buf, int pagesize,
+ void *ecc, int *needwrite)
+ {
+ *needwrite = 1;
+ return (nand_softecc_get(dev, buf, pagesize, ecc));
+ }
+
+ static int nfc_softecc_correct(device_t dev, void *buf, int pagesize,
+ void *readecc, void *calcecc)
+ {
+ return (nand_softecc_correct(dev, buf, pagesize, readecc,
+ calcecc));
+ }
+};
+
+# Send command to a NAND chip
+#
+# Return values:
+# 0: Success
+#
+METHOD int send_command {
+ device_t dev;
+ uint8_t command;
+};
+
+# Send address to a NAND chip
+#
+# Return values:
+# 0: Success
+#
+METHOD int send_address {
+ device_t dev;
+ uint8_t address;
+};
+
+# Read byte
+#
+# Return values:
+# byte read
+#
+METHOD uint8_t read_byte {
+ device_t dev;
+};
+
+# Write byte
+#
+METHOD void write_byte {
+ device_t dev;
+ uint8_t byte;
+};
+
+# Read word
+#
+# Return values:
+# word read
+#
+METHOD uint16_t read_word {
+ device_t dev;
+};
+
+# Write word
+#
+METHOD void write_word {
+ device_t dev;
+ uint16_t word;
+};
+
+# Read buf
+#
+METHOD void read_buf {
+ device_t dev;
+ void *buf;
+ uint32_t len;
+};
+
+# Write buf
+#
+METHOD void write_buf {
+ device_t dev;
+ void *buf;
+ uint32_t len;
+};
+
+# Select CS
+#
+METHOD int select_cs {
+ device_t dev;
+ uint8_t cs;
+};
+
+# Read ready/busy signal
+#
+METHOD int read_rnb {
+ device_t dev;
+};
+
+# Start command
+#
+# Return values:
+# 0: Success
+#
+METHOD int start_command {
+ device_t dev;
+} DEFAULT nfc_default_method;
+
+# Generate ECC or get it from H/W
+#
+METHOD int get_ecc {
+ device_t dev;
+ void *buf;
+ int pagesize;
+ void *ecc;
+ int *needwrite;
+} DEFAULT nfc_softecc_get;
+
+# Correct ECC
+#
+METHOD int correct_ecc {
+ device_t dev;
+ void *buf;
+ int pagesize;
+ void *readecc;
+ void *calcecc;
+} DEFAULT nfc_softecc_correct;
diff --git a/sys/dev/nand/nfc_mv.c b/sys/dev/nand/nfc_mv.c
new file mode 100644
index 0000000..7f68e82
--- /dev/null
+++ b/sys/dev/nand/nfc_mv.c
@@ -0,0 +1,236 @@
+/*-
+ * 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.
+ */
+
+/* Integrated NAND controller driver */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/malloc.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/time.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <arm/mv/mvvar.h>
+#include <arm/mv/mvwin.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/nand/nand.h>
+#include <dev/nand/nandbus.h>
+#include "nfc_if.h"
+
+#define MV_NAND_DATA (0x00)
+#define MV_NAND_COMMAND (0x01)
+#define MV_NAND_ADDRESS (0x02)
+
+struct mv_nand_softc {
+ struct nand_softc nand_dev;
+ bus_space_handle_t sc_handle;
+ bus_space_tag_t sc_tag;
+ struct resource *res;
+ int rid;
+};
+
+static int mv_nand_attach(device_t);
+static int mv_nand_probe(device_t);
+static int mv_nand_send_command(device_t, uint8_t);
+static int mv_nand_send_address(device_t, uint8_t);
+static uint8_t mv_nand_read_byte(device_t);
+static void mv_nand_read_buf(device_t, void *, uint32_t);
+static void mv_nand_write_buf(device_t, void *, uint32_t);
+static int mv_nand_select_cs(device_t, uint8_t);
+static int mv_nand_read_rnb(device_t);
+
+static device_method_t mv_nand_methods[] = {
+ DEVMETHOD(device_probe, mv_nand_probe),
+ DEVMETHOD(device_attach, mv_nand_attach),
+
+ DEVMETHOD(nfc_send_command, mv_nand_send_command),
+ DEVMETHOD(nfc_send_address, mv_nand_send_address),
+ DEVMETHOD(nfc_read_byte, mv_nand_read_byte),
+ DEVMETHOD(nfc_read_buf, mv_nand_read_buf),
+ DEVMETHOD(nfc_write_buf, mv_nand_write_buf),
+ DEVMETHOD(nfc_select_cs, mv_nand_select_cs),
+ DEVMETHOD(nfc_read_rnb, mv_nand_read_rnb),
+
+ { 0, 0 },
+};
+
+static driver_t mv_nand_driver = {
+ "nand",
+ mv_nand_methods,
+ sizeof(struct mv_nand_softc),
+};
+
+static devclass_t mv_nand_devclass;
+DRIVER_MODULE(mv_nand, localbus, mv_nand_driver, mv_nand_devclass, 0, 0);
+
+static int
+mv_nand_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "mrvl,nfc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Marvell NAND controller");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mv_nand_attach(device_t dev)
+{
+ struct mv_nand_softc *sc;
+ int err;
+
+ sc = device_get_softc(dev);
+ sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->rid,
+ RF_ACTIVE);
+ if (sc->res == NULL) {
+ device_printf(dev, "could not allocate resources!\n");
+ return (ENXIO);
+ }
+
+ sc->sc_tag = rman_get_bustag(sc->res);
+ sc->sc_handle = rman_get_bushandle(sc->res);
+
+ nand_init(&sc->nand_dev, dev, NAND_ECC_SOFT, 0, 0, NULL, NULL);
+
+ err = nandbus_create(dev);
+
+ return (err);
+}
+
+static int
+mv_nand_send_command(device_t dev, uint8_t command)
+{
+ struct mv_nand_softc *sc;
+
+ nand_debug(NDBG_DRV,"mv_nand: send command %x", command);
+
+ sc = device_get_softc(dev);
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_COMMAND, command);
+ return (0);
+}
+
+static int
+mv_nand_send_address(device_t dev, uint8_t addr)
+{
+ struct mv_nand_softc *sc;
+
+ nand_debug(NDBG_DRV,"mv_nand: send address %x", addr);
+
+ sc = device_get_softc(dev);
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_ADDRESS, addr);
+ return (0);
+}
+
+static uint8_t
+mv_nand_read_byte(device_t dev)
+{
+ struct mv_nand_softc *sc;
+ uint8_t data;
+
+ sc = device_get_softc(dev);
+ data = bus_space_read_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA);
+
+ nand_debug(NDBG_DRV,"mv_nand: read %x", data);
+
+ return (data);
+}
+
+static void
+mv_nand_read_buf(device_t dev, void* buf, uint32_t len)
+{
+ struct mv_nand_softc *sc;
+ int i;
+ uint8_t *b = (uint8_t*)buf;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < len; i++) {
+ b[i] = bus_space_read_1(sc->sc_tag, sc->sc_handle,
+ MV_NAND_DATA);
+#ifdef NAND_DEBUG
+ if (!(i % 16))
+ printf("%s", i == 0 ? "mv_nand:\n" : "\n");
+ printf(" %x", b[i]);
+ if (i == len - 1)
+ printf("\n");
+#endif
+ }
+}
+
+static void
+mv_nand_write_buf(device_t dev, void* buf, uint32_t len)
+{
+ struct mv_nand_softc *sc;
+ int i;
+ uint8_t *b = (uint8_t*)buf;
+
+ sc = device_get_softc(dev);
+
+ for (i = 0; i < len; i++) {
+#ifdef NAND_DEBUG
+ if (!(i % 16))
+ printf("%s", i == 0 ? "mv_nand:\n" : "\n");
+ printf(" %x", b[i]);
+ if (i == len - 1)
+ printf("\n");
+#endif
+ bus_space_write_1(sc->sc_tag, sc->sc_handle, MV_NAND_DATA,
+ b[i]);
+ }
+}
+
+static int
+mv_nand_select_cs(device_t dev, uint8_t cs)
+{
+
+ if (cs > 0)
+ return (ENODEV);
+
+ return (0);
+}
+
+static int
+mv_nand_read_rnb(device_t dev)
+{
+
+ /* no-op */
+ return (0); /* ready */
+}
diff --git a/sys/fs/nandfs/bmap.c b/sys/fs/nandfs/bmap.c
new file mode 100644
index 0000000..84e4a9e
--- /dev/null
+++ b/sys/fs/nandfs/bmap.c
@@ -0,0 +1,621 @@
+/*-
+ * Copyright (c) 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 ``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 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+#include <sys/ktr.h>
+#include <sys/kdb.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include <machine/_inttypes.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+#include "bmap.h"
+
+static int bmap_getlbns(struct nandfs_node *, nandfs_lbn_t,
+ struct nandfs_indir *, int *);
+
+int
+bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk, nandfs_daddr_t *vblk)
+{
+ struct nandfs_inode *ip;
+ struct nandfs_indir a[NIADDR + 1], *ap;
+ nandfs_daddr_t daddr;
+ struct buf *bp;
+ int error;
+ int num, *nump;
+
+ DPRINTF(BMAP, ("%s: node %p lblk %jx enter\n", __func__, node, lblk));
+ ip = &node->nn_inode;
+
+ ap = a;
+ nump = &num;
+
+ error = bmap_getlbns(node, lblk, ap, nump);
+ if (error)
+ return (error);
+
+ if (num == 0) {
+ *vblk = ip->i_db[lblk];
+ return (0);
+ }
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx trying ip->i_ib[%x]\n", __func__,
+ node, lblk, ap->in_off));
+ daddr = ip->i_ib[ap->in_off];
+ for (bp = NULL, ++ap; --num; ap++) {
+ if (daddr == 0) {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with "
+ "vblk 0\n", __func__, node, lblk));
+ *vblk = 0;
+ return (0);
+ }
+ if (ap->in_lbn == lblk) {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx ap->in_lbn=%jx "
+ "returning address of indirect block (%jx)\n",
+ __func__, node, lblk, ap->in_lbn, daddr));
+ *vblk = daddr;
+ return (0);
+ }
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx reading block "
+ "ap->in_lbn=%jx\n", __func__, node, lblk, ap->in_lbn));
+
+ error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
+ brelse(bp);
+ }
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx returning with %jx\n", __func__,
+ node, lblk, daddr));
+ *vblk = daddr;
+
+ return (0);
+}
+
+int
+bmap_dirty_meta(struct nandfs_node *node, nandfs_lbn_t lblk, int force)
+{
+ struct nandfs_indir a[NIADDR+1], *ap;
+#ifdef DEBUG
+ nandfs_daddr_t daddr;
+#endif
+ struct buf *bp;
+ int error;
+ int num, *nump;
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx\n", __func__, node, lblk));
+
+ ap = a;
+ nump = &num;
+
+ error = bmap_getlbns(node, lblk, ap, nump);
+ if (error)
+ return (error);
+
+ /*
+ * Direct block, nothing to do
+ */
+ if (num == 0)
+ return (0);
+
+ DPRINTF(BMAP, ("%s: node %p reading blocks\n", __func__, node));
+
+ for (bp = NULL, ++ap; --num; ap++) {
+ error = nandfs_bread_meta(node, ap->in_lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+#ifdef DEBUG
+ daddr = ((nandfs_daddr_t *)bp->b_data)[ap->in_off];
+ MPASS(daddr != 0 || node->nn_ino == 3);
+#endif
+
+ error = nandfs_dirty_buf_meta(bp, force);
+ if (error)
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk,
+ nandfs_daddr_t vblk)
+{
+ struct nandfs_inode *ip;
+ struct nandfs_indir a[NIADDR+1], *ap;
+ struct buf *bp;
+ nandfs_daddr_t daddr;
+ int error;
+ int num, *nump, i;
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx\n", __func__, node, lblk,
+ vblk));
+
+ ip = &node->nn_inode;
+
+ ap = a;
+ nump = &num;
+
+ error = bmap_getlbns(node, lblk, ap, nump);
+ if (error)
+ return (error);
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx got num=%d\n", __func__,
+ node, lblk, vblk, num));
+
+ if (num == 0) {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx direct block\n", __func__,
+ node, lblk));
+ ip->i_db[lblk] = vblk;
+ return (0);
+ }
+
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block level %d\n",
+ __func__, node, lblk, ap->in_off));
+
+ if (num == 1) {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx indirect block: inserting "
+ "%jx as vblk for indirect block %d\n", __func__, node,
+ lblk, vblk, ap->in_off));
+ ip->i_ib[ap->in_off] = vblk;
+ return (0);
+ }
+
+ bp = NULL;
+ daddr = ip->i_ib[a[0].in_off];
+ for (i = 1; i < num; i++) {
+ if (bp)
+ brelse(bp);
+ if (daddr == 0) {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx create "
+ "block %jx %d\n", __func__, node, lblk, vblk,
+ a[i].in_lbn, a[i].in_off));
+ error = nandfs_bcreate_meta(node, a[i].in_lbn, NOCRED,
+ 0, &bp);
+ if (error)
+ return (error);
+ } else {
+ DPRINTF(BMAP, ("%s: node %p lblk=%jx vblk=%jx read "
+ "block %jx %d\n", __func__, node, daddr, vblk,
+ a[i].in_lbn, a[i].in_off));
+ error = nandfs_bread_meta(node, a[i].in_lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ }
+ daddr = ((nandfs_daddr_t *)bp->b_data)[a[i].in_off];
+ }
+ i--;
+
+ DPRINTF(BMAP,
+ ("%s: bmap node %p lblk=%jx vblk=%jx inserting vblk level %d at "
+ "offset %d at %jx\n", __func__, node, lblk, vblk, i, a[i].in_off,
+ daddr));
+
+ if (!bp) {
+ nandfs_error("%s: cannot find indirect block\n", __func__);
+ return (-1);
+ }
+ ((nandfs_daddr_t *)bp->b_data)[a[i].in_off] = vblk;
+
+ error = nandfs_dirty_buf_meta(bp, 0);
+ if (error) {
+ nandfs_warning("%s: dirty failed buf: %p\n", __func__, bp);
+ return (error);
+ }
+ DPRINTF(BMAP, ("%s: exiting node %p lblk=%jx vblk=%jx\n", __func__,
+ node, lblk, vblk));
+
+ return (error);
+}
+
+CTASSERT(NIADDR <= 3);
+#define SINGLE 0 /* index of single indirect block */
+#define DOUBLE 1 /* index of double indirect block */
+#define TRIPLE 2 /* index of triple indirect block */
+
+static __inline nandfs_lbn_t
+lbn_offset(struct nandfs_device *fsdev, int level)
+{
+ nandfs_lbn_t res;
+
+ for (res = 1; level > 0; level--)
+ res *= MNINDIR(fsdev);
+ return (res);
+}
+
+static nandfs_lbn_t
+blocks_inside(struct nandfs_device *fsdev, int level, struct nandfs_indir *nip)
+{
+ nandfs_lbn_t blocks;
+
+ for (blocks = 1; level >= SINGLE; level--, nip++) {
+ MPASS(nip->in_off >= 0 && nip->in_off < MNINDIR(fsdev));
+ blocks += nip->in_off * lbn_offset(fsdev, level);
+ }
+
+ return (blocks);
+}
+
+static int
+bmap_truncate_indirect(struct nandfs_node *node, int level, nandfs_lbn_t *left,
+ int *cleaned, struct nandfs_indir *ap, struct nandfs_indir *fp,
+ nandfs_daddr_t *copy)
+{
+ struct buf *bp;
+ nandfs_lbn_t i, lbn, nlbn, factor, tosub;
+ struct nandfs_device *fsdev;
+ int error, lcleaned, modified;
+
+ DPRINTF(BMAP, ("%s: node %p level %d left %jx\n", __func__,
+ node, level, *left));
+
+ fsdev = node->nn_nandfsdev;
+
+ MPASS(ap->in_off >= 0 && ap->in_off < MNINDIR(fsdev));
+
+ factor = lbn_offset(fsdev, level);
+ lbn = ap->in_lbn;
+
+ error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ bcopy(bp->b_data, copy, fsdev->nd_blocksize);
+ bqrelse(bp);
+
+ modified = 0;
+
+ i = ap->in_off;
+
+ if (ap != fp)
+ ap++;
+ for (nlbn = lbn + 1 - i * factor; i >= 0 && *left > 0; i--,
+ nlbn += factor) {
+ lcleaned = 0;
+
+ DPRINTF(BMAP,
+ ("%s: node %p i=%jx nlbn=%jx left=%jx ap=%p vblk %jx\n",
+ __func__, node, i, nlbn, *left, ap, copy[i]));
+
+ if (copy[i] == 0) {
+ tosub = blocks_inside(fsdev, level - 1, ap);
+ if (tosub > *left)
+ tosub = 0;
+
+ *left -= tosub;
+ } else {
+ if (level > SINGLE) {
+ if (ap == fp)
+ ap->in_lbn = nlbn;
+
+ error = bmap_truncate_indirect(node, level - 1,
+ left, &lcleaned, ap, fp,
+ copy + MNINDIR(fsdev));
+ if (error)
+ return (error);
+ } else {
+ error = nandfs_bdestroy(node, copy[i]);
+ if (error)
+ return (error);
+ lcleaned = 1;
+ *left -= 1;
+ }
+ }
+
+ if (lcleaned) {
+ if (level > SINGLE) {
+ error = nandfs_vblock_end(fsdev, copy[i]);
+ if (error)
+ return (error);
+ }
+ copy[i] = 0;
+ modified++;
+ }
+
+ ap = fp;
+ }
+
+ if (i == -1)
+ *cleaned = 1;
+
+ error = nandfs_bread_meta(node, lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ if (modified)
+ bcopy(copy, bp->b_data, fsdev->nd_blocksize);
+
+ error = nandfs_dirty_buf_meta(bp, 0);
+ if (error)
+ return (error);
+
+ return (error);
+}
+
+int
+bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t lastblk,
+ nandfs_lbn_t todo)
+{
+ struct nandfs_inode *ip;
+ struct nandfs_indir a[NIADDR + 1], f[NIADDR], *ap;
+ nandfs_daddr_t indir_lbn[NIADDR];
+ nandfs_daddr_t *copy;
+ int error, level;
+ nandfs_lbn_t left, tosub;
+ struct nandfs_device *fsdev;
+ int cleaned, i;
+ int num, *nump;
+
+ DPRINTF(BMAP, ("%s: node %p lastblk %jx truncating by %jx\n", __func__,
+ node, lastblk, todo));
+
+ ip = &node->nn_inode;
+ fsdev = node->nn_nandfsdev;
+
+ ap = a;
+ nump = &num;
+
+ error = bmap_getlbns(node, lastblk, ap, nump);
+ if (error)
+ return (error);
+
+ indir_lbn[SINGLE] = -NDADDR;
+ indir_lbn[DOUBLE] = indir_lbn[SINGLE] - MNINDIR(fsdev) - 1;
+ indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - MNINDIR(fsdev)
+ * MNINDIR(fsdev) - 1;
+
+ for (i = 0; i < NIADDR; i++) {
+ f[i].in_off = MNINDIR(fsdev) - 1;
+ f[i].in_lbn = 0xdeadbeef;
+ }
+
+ left = todo;
+
+#ifdef DEBUG
+ a[num].in_off = -1;
+#endif
+
+ ap++;
+ num -= 2;
+
+ if (num < 0)
+ goto direct;
+
+ copy = malloc(MNINDIR(fsdev) * sizeof(nandfs_daddr_t) * (num + 1),
+ M_NANDFSTEMP, M_WAITOK);
+
+ for (level = num; level >= SINGLE && left > 0; level--) {
+ cleaned = 0;
+
+ if (ip->i_ib[level] == 0) {
+ tosub = blocks_inside(fsdev, level, ap);
+ if (tosub > left)
+ left = 0;
+ else
+ left -= tosub;
+ } else {
+ if (ap == f)
+ ap->in_lbn = indir_lbn[level];
+ error = bmap_truncate_indirect(node, level, &left,
+ &cleaned, ap, f, copy);
+ if (error) {
+ nandfs_error("%s: error %d when truncate "
+ "at level %d\n", __func__, error, level);
+ return (error);
+ }
+ }
+
+ if (cleaned) {
+ nandfs_vblock_end(fsdev, ip->i_ib[level]);
+ ip->i_ib[level] = 0;
+ }
+
+ ap = f;
+ }
+
+ free(copy, M_NANDFSTEMP);
+
+direct:
+ if (num < 0)
+ i = lastblk;
+ else
+ i = NDADDR - 1;
+
+ for (; i >= 0 && left > 0; i--) {
+ if (ip->i_db[i] != 0) {
+ error = nandfs_bdestroy(node, ip->i_db[i]);
+ if (error) {
+ nandfs_error("%s: cannot destroy "
+ "block %jx, error %d\n", __func__,
+ (uintmax_t)ip->i_db[i], error);
+ return (error);
+ }
+ ip->i_db[i] = 0;
+ }
+
+ left--;
+ }
+
+ KASSERT(left == 0,
+ ("truncated wrong number of blocks (%jd should be 0)", left));
+
+ return (error);
+}
+
+nandfs_lbn_t
+get_maxfilesize(struct nandfs_device *fsdev)
+{
+ struct nandfs_indir f[NIADDR];
+ nandfs_lbn_t max;
+ int i;
+
+ max = NDADDR;
+
+ for (i = 0; i < NIADDR; i++) {
+ f[i].in_off = MNINDIR(fsdev) - 1;
+ max += blocks_inside(fsdev, i, f);
+ }
+
+ max *= fsdev->nd_blocksize;
+
+ return (max);
+}
+
+/*
+ * This is ufs_getlbns with minor modifications.
+ */
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ib and
+ * once with the offset into the page itself.
+ */
+static int
+bmap_getlbns(struct nandfs_node *node, nandfs_lbn_t bn, struct nandfs_indir *ap, int *nump)
+{
+ nandfs_daddr_t blockcnt;
+ nandfs_lbn_t metalbn, realbn;
+ struct nandfs_device *fsdev;
+ int i, numlevels, off;
+
+ fsdev = node->nn_nandfsdev;
+
+ DPRINTF(BMAP, ("%s: node %p bn=%jx mnindir=%zd enter\n", __func__,
+ node, bn, MNINDIR(fsdev)));
+
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+
+ if (bn < 0)
+ bn = -bn;
+
+ /* The first NDADDR blocks are direct blocks. */
+ if (bn < NDADDR)
+ return (0);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the previous level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+ for (blockcnt = 1, i = NIADDR, bn -= NDADDR;; i--, bn -= blockcnt) {
+ DPRINTF(BMAP, ("%s: blockcnt=%jd i=%d bn=%jd\n", __func__,
+ blockcnt, i, bn));
+ if (i == 0)
+ return (EFBIG);
+ blockcnt *= MNINDIR(fsdev);
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+
+ DPRINTF(BMAP, ("%s: initial: ap->in_lbn=%jx ap->in_off=%d\n", __func__,
+ metalbn, off));
+
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ blockcnt /= MNINDIR(fsdev);
+ off = (bn / blockcnt) % MNINDIR(fsdev);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+
+ DPRINTF(BMAP, ("%s: in_lbn=%jx in_off=%d\n", __func__,
+ ap->in_lbn, ap->in_off));
+ ++ap;
+
+ metalbn -= -1 + off * blockcnt;
+ }
+ if (nump)
+ *nump = numlevels;
+
+ DPRINTF(BMAP, ("%s: numlevels=%d\n", __func__, numlevels));
+
+ return (0);
+}
diff --git a/sys/fs/nandfs/bmap.h b/sys/fs/nandfs/bmap.h
new file mode 100644
index 0000000..c27c61c
--- /dev/null
+++ b/sys/fs/nandfs/bmap.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 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 ``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 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 _BMAP_H
+#define _BMAP_H
+
+#include "nandfs_fs.h"
+
+int bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *);
+int bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t);
+int bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t, nandfs_lbn_t);
+int bmap_dirty_meta(struct nandfs_node *, nandfs_lbn_t, int);
+
+nandfs_lbn_t get_maxfilesize(struct nandfs_device *);
+
+#endif /* _BMAP_H */
diff --git a/sys/fs/nandfs/nandfs.h b/sys/fs/nandfs/nandfs.h
new file mode 100644
index 0000000..beb4e16
--- /dev/null
+++ b/sys/fs/nandfs/nandfs.h
@@ -0,0 +1,310 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs.h,v 1.1 2009/07/18 16:31:42 reinoud
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FS_NANDFS_NANDFS_H_
+#define _FS_NANDFS_NANDFS_H_
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/condvar.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/mutex.h>
+
+#include <sys/disk.h>
+#include <sys/kthread.h>
+#include "nandfs_fs.h"
+
+MALLOC_DECLARE(M_NANDFSTEMP);
+
+/* Debug categories */
+#define NANDFS_DEBUG_VOLUMES 0x000001
+#define NANDFS_DEBUG_BLOCK 0x000004
+#define NANDFS_DEBUG_LOCKING 0x000008
+#define NANDFS_DEBUG_NODE 0x000010
+#define NANDFS_DEBUG_LOOKUP 0x000020
+#define NANDFS_DEBUG_READDIR 0x000040
+#define NANDFS_DEBUG_TRANSLATE 0x000080
+#define NANDFS_DEBUG_STRATEGY 0x000100
+#define NANDFS_DEBUG_READ 0x000200
+#define NANDFS_DEBUG_WRITE 0x000400
+#define NANDFS_DEBUG_IFILE 0x000800
+#define NANDFS_DEBUG_ATTR 0x001000
+#define NANDFS_DEBUG_EXTATTR 0x002000
+#define NANDFS_DEBUG_ALLOC 0x004000
+#define NANDFS_DEBUG_CPFILE 0x008000
+#define NANDFS_DEBUG_DIRHASH 0x010000
+#define NANDFS_DEBUG_NOTIMPL 0x020000
+#define NANDFS_DEBUG_SHEDULE 0x040000
+#define NANDFS_DEBUG_SEG 0x080000
+#define NANDFS_DEBUG_SYNC 0x100000
+#define NANDFS_DEBUG_PARANOIA 0x200000
+#define NANDFS_DEBUG_VNCALL 0x400000
+#define NANDFS_DEBUG_BUF 0x1000000
+#define NANDFS_DEBUG_BMAP 0x2000000
+#define NANDFS_DEBUG_DAT 0x4000000
+#define NANDFS_DEBUG_GENERIC 0x8000000
+#define NANDFS_DEBUG_CLEAN 0x10000000
+
+extern int nandfs_verbose;
+
+#define DPRINTF(name, arg) { \
+ if (nandfs_verbose & NANDFS_DEBUG_##name) {\
+ printf arg;\
+ };\
+ }
+#define DPRINTFIF(name, cond, arg) { \
+ if (nandfs_verbose & NANDFS_DEBUG_##name) { \
+ if (cond) printf arg;\
+ };\
+ }
+
+#define VFSTONANDFS(mp) ((struct nandfsmount *)((mp)->mnt_data))
+#define VTON(vp) ((struct nandfs_node *)(vp)->v_data)
+#define NTOV(xp) ((xp)->nn_vnode)
+
+int nandfs_init(struct vfsconf *);
+int nandfs_uninit(struct vfsconf *);
+
+extern struct vop_vector nandfs_vnodeops;
+extern struct vop_vector nandfs_system_vnodeops;
+
+struct nandfs_node;
+
+/* Structure and derivatives */
+struct nandfs_mdt {
+ uint32_t entries_per_block;
+ uint32_t entries_per_group;
+ uint32_t blocks_per_group;
+ uint32_t groups_per_desc_block; /* desc is super group */
+ uint32_t blocks_per_desc_block; /* desc is super group */
+};
+
+struct nandfs_segment {
+ LIST_ENTRY(nandfs_segment) seg_link;
+
+ struct nandfs_device *fsdev;
+
+ TAILQ_HEAD(, buf) segsum;
+ TAILQ_HEAD(, buf) data;
+
+ uint64_t seg_num;
+ uint64_t seg_next;
+ uint64_t start_block;
+ uint32_t num_blocks;
+
+ uint32_t nblocks;
+ uint32_t nbinfos;
+ uint32_t segsum_blocks;
+ uint32_t segsum_bytes;
+ uint32_t bytes_left;
+ char *current_off;
+};
+
+struct nandfs_seginfo {
+ LIST_HEAD( ,nandfs_segment) seg_list;
+ struct nandfs_segment *curseg;
+ struct nandfs_device *fsdev;
+ uint32_t blocks;
+ uint8_t reiterate;
+};
+
+#define NANDFS_FSSTOR_FAILED 1
+struct nandfs_fsarea {
+ int offset;
+ int flags;
+ int last_used;
+};
+
+extern int nandfs_cleaner_enable;
+extern int nandfs_cleaner_interval;
+extern int nandfs_cleaner_segments;
+
+struct nandfs_device {
+ struct vnode *nd_devvp;
+ struct g_consumer *nd_gconsumer;
+
+ struct thread *nd_syncer;
+ struct thread *nd_cleaner;
+ int nd_syncer_exit;
+ int nd_cleaner_exit;
+
+ int nd_is_nand;
+
+ struct nandfs_fsarea nd_fsarea[NANDFS_NFSAREAS];
+ int nd_last_fsarea;
+
+ STAILQ_HEAD(nandfs_mnts, nandfsmount) nd_mounts;
+ SLIST_ENTRY(nandfs_device) nd_next_device;
+
+ /* FS structures */
+ struct nandfs_fsdata nd_fsdata;
+ struct nandfs_super_block nd_super;
+ struct nandfs_segment_summary nd_last_segsum;
+ struct nandfs_super_root nd_super_root;
+ struct nandfs_node *nd_dat_node;
+ struct nandfs_node *nd_cp_node;
+ struct nandfs_node *nd_su_node;
+ struct nandfs_node *nd_gc_node;
+
+ struct nandfs_mdt nd_dat_mdt;
+ struct nandfs_mdt nd_ifile_mdt;
+
+ struct timespec nd_ts;
+
+ /* Synchronization */
+ struct mtx nd_mutex;
+ struct mtx nd_sync_mtx;
+ struct cv nd_sync_cv;
+ struct mtx nd_clean_mtx;
+ struct cv nd_clean_cv;
+ struct lock nd_seg_const;
+
+ struct nandfs_seginfo *nd_seginfo;
+
+ /* FS geometry */
+ uint64_t nd_devsize;
+ uint64_t nd_maxfilesize;
+ uint32_t nd_blocksize;
+ uint32_t nd_erasesize;
+
+ uint32_t nd_devblocksize;
+
+ /* Segment usage */
+ uint64_t nd_clean_segs;
+ uint64_t *nd_free_base;
+ uint64_t nd_free_count;
+ uint64_t nd_dirty_bufs;
+
+ /* Running values */
+ uint64_t nd_seg_sequence;
+ uint64_t nd_seg_num;
+ uint64_t nd_next_seg_num;
+ uint64_t nd_last_pseg;
+ uint64_t nd_last_cno;
+ uint64_t nd_last_ino;
+ uint64_t nd_fakevblk;
+
+ int nd_mount_state;
+ int nd_refcnt;
+ int nd_syncing;
+ int nd_cleaning;
+};
+
+extern SLIST_HEAD(_nandfs_devices, nandfs_device) nandfs_devices;
+
+#define NANDFS_FORCE_SYNCER 0x1
+#define NANDFS_UMOUNT 0x2
+
+#define SYNCER_UMOUNT 0x0
+#define SYNCER_VFS_SYNC 0x1
+#define SYNCER_BDFLUSH 0x2
+#define SYNCER_FFORCE 0x3
+#define SYNCER_FSYNC 0x4
+#define SYNCER_ROUPD 0x5
+
+static __inline int
+nandfs_writelockflags(struct nandfs_device *fsdev, int flags)
+{
+ int error = 0;
+
+ if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE)
+ error = lockmgr(&fsdev->nd_seg_const, flags | LK_SHARED, NULL);
+
+ return (error);
+}
+
+static __inline void
+nandfs_writeunlock(struct nandfs_device *fsdev)
+{
+
+ if (lockstatus(&fsdev->nd_seg_const) != LK_EXCLUSIVE)
+ lockmgr(&(fsdev)->nd_seg_const, LK_RELEASE, NULL);
+}
+
+#define NANDFS_WRITELOCKFLAGS(fsdev, flags) nandfs_writelockflags(fsdev, flags)
+
+#define NANDFS_WRITELOCK(fsdev) NANDFS_WRITELOCKFLAGS(fsdev, 0)
+
+#define NANDFS_WRITEUNLOCK(fsdev) nandfs_writeunlock(fsdev)
+
+#define NANDFS_WRITEASSERT(fsdev) lockmgr_assert(&(fsdev)->nd_seg_const, KA_LOCKED)
+
+/* Specific mountpoint; head or a checkpoint/snapshot */
+struct nandfsmount {
+ STAILQ_ENTRY(nandfsmount) nm_next_mount;
+
+ struct mount *nm_vfs_mountp;
+ struct nandfs_device *nm_nandfsdev;
+ struct nandfs_args nm_mount_args;
+ struct nandfs_node *nm_ifile_node;
+
+ uint8_t nm_flags;
+ int8_t nm_ronly;
+};
+
+struct nandfs_node {
+ struct vnode *nn_vnode;
+ struct nandfsmount *nn_nmp;
+ struct nandfs_device *nn_nandfsdev;
+ struct lockf *nn_lockf;
+
+ uint64_t nn_ino;
+ struct nandfs_inode nn_inode;
+
+ uint64_t nn_diroff;
+ uint32_t nn_flags;
+};
+
+#define IN_ACCESS 0x0001 /* Inode access time update request */
+#define IN_CHANGE 0x0002 /* Inode change time update request */
+#define IN_UPDATE 0x0004 /* Inode was written to; update mtime*/
+#define IN_MODIFIED 0x0008 /* node has been modified */
+#define IN_RENAME 0x0010 /* node is being renamed. */
+
+/* File permissions. */
+#define IEXEC 0000100 /* Executable. */
+#define IWRITE 0000200 /* Writeable. */
+#define IREAD 0000400 /* Readable. */
+#define ISVTX 0001000 /* Sticky bit. */
+#define ISGID 0002000 /* Set-gid. */
+#define ISUID 0004000 /* Set-uid. */
+
+#define PRINT_NODE_FLAGS \
+ "\10\1IN_ACCESS\2IN_CHANGE\3IN_UPDATE\4IN_MODIFIED\5IN_RENAME"
+
+#define NANDFS_GATHER(x) ((x)->b_flags |= B_00800000)
+#define NANDFS_UNGATHER(x) ((x)->b_flags &= ~B_00800000)
+#define NANDFS_ISGATHERED(x) ((x)->b_flags & B_00800000)
+
+#endif /* !_FS_NANDFS_NANDFS_H_ */
diff --git a/sys/fs/nandfs/nandfs_alloc.c b/sys/fs/nandfs/nandfs_alloc.c
new file mode 100644
index 0000000..3417266
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_alloc.c
@@ -0,0 +1,364 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+static void
+nandfs_get_desc_block_nr(struct nandfs_mdt *mdt, uint64_t desc,
+ uint64_t *desc_block)
+{
+
+ *desc_block = desc * mdt->blocks_per_desc_block;
+}
+
+static void
+nandfs_get_group_block_nr(struct nandfs_mdt *mdt, uint64_t group,
+ uint64_t *group_block)
+{
+ uint64_t desc, group_off;
+
+ desc = group / mdt->groups_per_desc_block;
+ group_off = group % mdt->groups_per_desc_block;
+ *group_block = desc * mdt->blocks_per_desc_block +
+ 1 + group_off * mdt->blocks_per_group;
+}
+
+static void
+init_desc_block(struct nandfs_mdt *mdt, uint8_t *block_data)
+{
+ struct nandfs_block_group_desc *desc;
+ uint32_t i;
+
+ desc = (struct nandfs_block_group_desc *) block_data;
+ for (i = 0; i < mdt->groups_per_desc_block; i++)
+ desc[i].bg_nfrees = mdt->entries_per_group;
+}
+
+int
+nandfs_find_free_entry(struct nandfs_mdt *mdt, struct nandfs_node *node,
+ struct nandfs_alloc_request *req)
+{
+ nandfs_daddr_t desc, group, maxgroup, maxdesc, pos = 0;
+ nandfs_daddr_t start_group, start_desc;
+ nandfs_daddr_t desc_block, group_block;
+ nandfs_daddr_t file_blocks;
+ struct nandfs_block_group_desc *descriptors;
+ struct buf *bp, *bp2;
+ uint32_t *mask, i, mcount, msize;
+ int error;
+
+ file_blocks = node->nn_inode.i_blocks;
+ maxgroup = 0x100000000ull / mdt->entries_per_group;
+ maxdesc = maxgroup / mdt->groups_per_desc_block;
+ start_group = req->entrynum / mdt->entries_per_group;
+ start_desc = start_group / mdt->groups_per_desc_block;
+
+ bp = bp2 = NULL;
+restart:
+ for (desc = start_desc; desc < maxdesc; desc++) {
+ nandfs_get_desc_block_nr(mdt, desc, &desc_block);
+
+ if (bp)
+ brelse(bp);
+ if (desc_block < file_blocks) {
+ error = nandfs_bread(node, desc_block, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ } else {
+ error = nandfs_bcreate(node, desc_block, NOCRED, 0,
+ &bp);
+ if (error)
+ return (error);
+ file_blocks++;
+ init_desc_block(mdt, bp->b_data);
+ }
+
+ descriptors = (struct nandfs_block_group_desc *) bp->b_data;
+ for (group = start_group; group < mdt->groups_per_desc_block;
+ group++) {
+ if (descriptors[group].bg_nfrees > 0) {
+ nandfs_get_group_block_nr(mdt, group,
+ &group_block);
+
+ if (bp2)
+ brelse(bp2);
+ if (group_block < file_blocks) {
+ error = nandfs_bread(node, group_block,
+ NOCRED, 0, &bp2);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ } else {
+ error = nandfs_bcreate(node,
+ group_block, NOCRED, 0, &bp2);
+ if (error)
+ return (error);
+ file_blocks++;
+ }
+ mask = (uint32_t *)bp2->b_data;
+ msize = (sizeof(uint32_t) * __CHAR_BIT);
+ mcount = mdt->entries_per_group / msize;
+ for (i = 0; i < mcount; i++) {
+ if (mask[i] == UINT32_MAX)
+ continue;
+
+ pos = ffs(~mask[i]) - 1;
+ pos += (msize * i);
+ pos += (group * mdt->entries_per_group);
+ pos += desc * group *
+ mdt->groups_per_desc_block *
+ mdt->entries_per_group;
+ goto found;
+ }
+ }
+ }
+ start_group = 0;
+ }
+
+ if (start_desc != 0) {
+ maxdesc = start_desc;
+ start_desc = 0;
+ req->entrynum = 0;
+ goto restart;
+ }
+
+ return (ENOENT);
+
+found:
+ req->entrynum = pos;
+ req->bp_desc = bp;
+ req->bp_bitmap = bp2;
+ DPRINTF(ALLOC, ("%s: desc: %p bitmap: %p entry: %#jx\n",
+ __func__, req->bp_desc, req->bp_bitmap, (uintmax_t)pos));
+
+ return (0);
+}
+
+int
+nandfs_find_entry(struct nandfs_mdt* mdt, struct nandfs_node *nnode,
+ struct nandfs_alloc_request *req)
+{
+ uint64_t dblock, bblock, eblock;
+ uint32_t offset;
+ int error;
+
+ nandfs_mdt_trans_blk(mdt, req->entrynum, &dblock, &bblock, &eblock,
+ &offset);
+
+ error = nandfs_bread(nnode, dblock, NOCRED, 0, &req->bp_desc);
+ if (error) {
+ brelse(req->bp_desc);
+ return (error);
+ }
+
+ error = nandfs_bread(nnode, bblock, NOCRED, 0, &req->bp_bitmap);
+ if (error) {
+ brelse(req->bp_desc);
+ brelse(req->bp_bitmap);
+ return (error);
+ }
+
+ error = nandfs_bread(nnode, eblock, NOCRED, 0, &req->bp_entry);
+ if (error) {
+ brelse(req->bp_desc);
+ brelse(req->bp_bitmap);
+ brelse(req->bp_entry);
+ return (error);
+ }
+
+ DPRINTF(ALLOC,
+ ("%s: desc_buf: %p bitmap_buf %p entry_buf %p offset %x\n",
+ __func__, req->bp_desc, req->bp_bitmap, req->bp_entry, offset));
+
+ return (0);
+}
+
+static __inline void
+nandfs_calc_idx_entry(struct nandfs_mdt* mdt, uint32_t entrynum,
+ uint64_t *group, uint64_t *bitmap_idx, uint64_t *bitmap_off)
+{
+
+ /* Find group_desc index */
+ entrynum = entrynum %
+ (mdt->entries_per_group * mdt->groups_per_desc_block);
+ *group = entrynum / mdt->entries_per_group;
+ /* Find bitmap index and bit offset */
+ entrynum = entrynum % mdt->entries_per_group;
+ *bitmap_idx = entrynum / (sizeof(uint32_t) * __CHAR_BIT);
+ *bitmap_off = entrynum % (sizeof(uint32_t) * __CHAR_BIT);
+}
+
+int
+nandfs_free_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req)
+{
+ struct nandfs_block_group_desc *descriptors;
+ uint64_t bitmap_idx, bitmap_off;
+ uint64_t group;
+ uint32_t *mask, maskrw;
+
+ nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx,
+ &bitmap_off);
+
+ DPRINTF(ALLOC, ("nandfs_free_entry: req->entrynum=%jx bitmap_idx=%jx"
+ " bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum,
+ (uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group));
+
+ /* Update counter of free entries for group */
+ descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data;
+ descriptors[group].bg_nfrees++;
+
+ /* Set bit to indicate that entry is taken */
+ mask = (uint32_t *)req->bp_bitmap->b_data;
+ maskrw = mask[bitmap_idx];
+ KASSERT(maskrw & (1 << bitmap_off), ("freeing unallocated vblock"));
+ maskrw &= ~(1 << bitmap_off);
+ mask[bitmap_idx] = maskrw;
+
+ /* Make descriptor, bitmap and entry buffer dirty */
+ if (nandfs_dirty_buf(req->bp_desc, 0) == 0) {
+ nandfs_dirty_buf(req->bp_bitmap, 1);
+ nandfs_dirty_buf(req->bp_entry, 1);
+ } else {
+ brelse(req->bp_bitmap);
+ brelse(req->bp_entry);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+nandfs_alloc_entry(struct nandfs_mdt* mdt, struct nandfs_alloc_request *req)
+{
+ struct nandfs_block_group_desc *descriptors;
+ uint64_t bitmap_idx, bitmap_off;
+ uint64_t group;
+ uint32_t *mask, maskrw;
+
+ nandfs_calc_idx_entry(mdt, req->entrynum, &group, &bitmap_idx,
+ &bitmap_off);
+
+ DPRINTF(ALLOC, ("nandfs_alloc_entry: req->entrynum=%jx bitmap_idx=%jx"
+ " bitmap_off=%jx group=%jx\n", (uintmax_t)req->entrynum,
+ (uintmax_t)bitmap_idx, (uintmax_t)bitmap_off, (uintmax_t)group));
+
+ /* Update counter of free entries for group */
+ descriptors = (struct nandfs_block_group_desc *) req->bp_desc->b_data;
+ descriptors[group].bg_nfrees--;
+
+ /* Clear bit to indicate that entry is free */
+ mask = (uint32_t *)req->bp_bitmap->b_data;
+ maskrw = mask[bitmap_idx];
+ maskrw |= 1 << bitmap_off;
+ mask[bitmap_idx] = maskrw;
+
+ /* Make descriptor, bitmap and entry buffer dirty */
+ if (nandfs_dirty_buf(req->bp_desc, 0) == 0) {
+ nandfs_dirty_buf(req->bp_bitmap, 1);
+ nandfs_dirty_buf(req->bp_entry, 1);
+ } else {
+ brelse(req->bp_bitmap);
+ brelse(req->bp_entry);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+nandfs_abort_entry(struct nandfs_alloc_request *req)
+{
+
+ brelse(req->bp_desc);
+ brelse(req->bp_bitmap);
+ brelse(req->bp_entry);
+}
+
+int
+nandfs_get_entry_block(struct nandfs_mdt *mdt, struct nandfs_node *node,
+ struct nandfs_alloc_request *req, uint32_t *entry, int create)
+{
+ struct buf *bp;
+ nandfs_lbn_t blocknr;
+ int error;
+
+ /* Find buffer number for given entry */
+ nandfs_mdt_trans(mdt, req->entrynum, &blocknr, entry);
+ DPRINTF(ALLOC, ("%s: ino %#jx entrynum:%#jx block:%#jx entry:%x\n",
+ __func__, (uintmax_t)node->nn_ino, (uintmax_t)req->entrynum,
+ (uintmax_t)blocknr, *entry));
+
+ /* Read entry block or create if 'create' parameter is not zero */
+ bp = NULL;
+
+ if (blocknr < node->nn_inode.i_blocks)
+ error = nandfs_bread(node, blocknr, NOCRED, 0, &bp);
+ else if (create)
+ error = nandfs_bcreate(node, blocknr, NOCRED, 0, &bp);
+ else
+ error = E2BIG;
+
+ if (error) {
+ DPRINTF(ALLOC, ("%s: ino %#jx block %#jx entry %x error %d\n",
+ __func__, (uintmax_t)node->nn_ino, (uintmax_t)blocknr,
+ *entry, error));
+ if (bp)
+ brelse(bp);
+ return (error);
+ }
+
+ MPASS(nandfs_vblk_get(bp) != 0 || node->nn_ino == NANDFS_DAT_INO);
+
+ req->bp_entry = bp;
+ return (0);
+}
diff --git a/sys/fs/nandfs/nandfs_bmap.c b/sys/fs/nandfs/nandfs_bmap.c
new file mode 100644
index 0000000..9f800b8
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_bmap.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+#include <sys/ktr.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include <machine/_inttypes.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+#include "bmap.h"
+
+nandfs_lbn_t
+nandfs_get_maxfilesize(struct nandfs_device *fsdev)
+{
+
+ return (get_maxfilesize(fsdev));
+}
+
+int
+nandfs_bmap_lookup(struct nandfs_node *node, nandfs_lbn_t lblk,
+ nandfs_daddr_t *vblk)
+{
+ int error = 0;
+
+ if (node->nn_ino == NANDFS_GC_INO && lblk >= 0)
+ *vblk = lblk;
+ else
+ error = bmap_lookup(node, lblk, vblk);
+
+ DPRINTF(TRANSLATE, ("%s: error %d ino %#jx lblocknr %#jx -> %#jx\n",
+ __func__, error, (uintmax_t)node->nn_ino, (uintmax_t)lblk,
+ (uintmax_t)*vblk));
+
+ if (error)
+ nandfs_error("%s: returned %d", __func__, error);
+
+ return (error);
+}
+
+int
+nandfs_bmap_insert_block(struct nandfs_node *node, nandfs_lbn_t lblk,
+ struct buf *bp)
+{
+ struct nandfs_device *fsdev;
+ nandfs_daddr_t vblk;
+ int error;
+
+ fsdev = node->nn_nandfsdev;
+
+ vblk = 0;
+ if (node->nn_ino != NANDFS_DAT_INO) {
+ error = nandfs_vblock_alloc(fsdev, &vblk);
+ if (error)
+ return (error);
+ }
+
+ nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED);
+ nandfs_vblk_set(bp, vblk);
+
+ error = bmap_insert_block(node, lblk, vblk);
+ if (error) {
+ nandfs_vblock_free(fsdev, vblk);
+ return (error);
+ }
+
+ return (0);
+}
+
+int
+nandfs_bmap_dirty_blocks(struct nandfs_node *node, struct buf *bp, int force)
+{
+ int error;
+
+ error = bmap_dirty_meta(node, bp->b_lblkno, force);
+ if (error)
+ nandfs_error("%s: cannot dirty buffer %p\n",
+ __func__, bp);
+
+ return (error);
+}
+
+static int
+nandfs_bmap_update_mapping(struct nandfs_node *node, nandfs_lbn_t lblk,
+ nandfs_daddr_t blknr)
+{
+ int error;
+
+ DPRINTF(BMAP,
+ ("%s: node: %p ino: %#jx lblk: %#jx vblk: %#jx\n",
+ __func__, node, (uintmax_t)node->nn_ino, (uintmax_t)lblk,
+ (uintmax_t)blknr));
+
+ error = bmap_insert_block(node, lblk, blknr);
+
+ return (error);
+}
+
+int
+nandfs_bmap_update_block(struct nandfs_node *node, struct buf *bp,
+ nandfs_lbn_t blknr)
+{
+ nandfs_lbn_t lblk;
+ int error;
+
+ lblk = bp->b_lblkno;
+ nandfs_vblk_set(bp, blknr);
+
+ DPRINTF(BMAP, ("%s: node: %p ino: %#jx bp: %p lblk: %#jx blk: %#jx\n",
+ __func__, node, (uintmax_t)node->nn_ino, bp,
+ (uintmax_t)lblk, (uintmax_t)blknr));
+
+ error = nandfs_bmap_update_mapping(node, lblk, blknr);
+ if (error) {
+ nandfs_error("%s: cannot update lblk:%jx to blk:%jx for "
+ "node:%p, error:%d\n", __func__, (uintmax_t)lblk,
+ (uintmax_t)blknr, node, error);
+ return (error);
+ }
+
+ return (error);
+}
+
+int
+nandfs_bmap_update_dat(struct nandfs_node *node, nandfs_daddr_t oldblk,
+ struct buf *bp)
+{
+ struct nandfs_device *fsdev;
+ nandfs_daddr_t vblk = 0;
+ int error;
+
+ if (node->nn_ino == NANDFS_DAT_INO)
+ return (0);
+
+ if (nandfs_buf_check(bp, NANDFS_VBLK_ASSIGNED)) {
+ nandfs_buf_clear(bp, NANDFS_VBLK_ASSIGNED);
+ return (0);
+ }
+
+ fsdev = node->nn_nandfsdev;
+
+ /* First alloc new virtual block.... */
+ error = nandfs_vblock_alloc(fsdev, &vblk);
+ if (error)
+ return (error);
+
+ error = nandfs_bmap_update_block(node, bp, vblk);
+ if (error)
+ return (error);
+
+ /* Then we can end up with old one */
+ nandfs_vblock_end(fsdev, oldblk);
+
+ DPRINTF(BMAP,
+ ("%s: ino %#jx block %#jx: update vblk %#jx to %#jx\n",
+ __func__, (uintmax_t)node->nn_ino, (uintmax_t)bp->b_lblkno,
+ (uintmax_t)oldblk, (uintmax_t)vblk));
+ return (error);
+}
+
+int
+nandfs_bmap_truncate_mapping(struct nandfs_node *node, nandfs_lbn_t oblk,
+ nandfs_lbn_t nblk)
+{
+ nandfs_lbn_t todo;
+ int error;
+
+ todo = oblk - nblk;
+
+ DPRINTF(BMAP, ("%s: node %p oblk %jx nblk %jx truncate by %jx\n",
+ __func__, node, oblk, nblk, todo));
+
+ error = bmap_truncate_mapping(node, oblk, todo);
+ if (error)
+ return (error);
+
+ return (error);
+}
diff --git a/sys/fs/nandfs/nandfs_buffer.c b/sys/fs/nandfs/nandfs_buffer.c
new file mode 100644
index 0000000..b0d72668
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_buffer.c
@@ -0,0 +1,83 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/buf.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/bio.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+struct buf *
+nandfs_geteblk(int size, int flags)
+{
+ struct buf *bp;
+
+ /*
+ * XXX
+ * Right now we can call geteblk with GB_NOWAIT_BD flag, which means
+ * it can return NULL. But we cannot afford to get NULL, hence this panic.
+ */
+ bp = geteblk(size, flags);
+ if (bp == NULL)
+ panic("geteblk returned NULL");
+
+ return (bp);
+}
+
+void
+nandfs_dirty_bufs_increment(struct nandfs_device *fsdev)
+{
+
+ mtx_lock(&fsdev->nd_mutex);
+ KASSERT(fsdev->nd_dirty_bufs >= 0, ("negative nd_dirty_bufs"));
+ fsdev->nd_dirty_bufs++;
+ mtx_unlock(&fsdev->nd_mutex);
+}
+
+void
+nandfs_dirty_bufs_decrement(struct nandfs_device *fsdev)
+{
+
+ mtx_lock(&fsdev->nd_mutex);
+ KASSERT(fsdev->nd_dirty_bufs > 0,
+ ("decrementing not-positive nd_dirty_bufs"));
+ fsdev->nd_dirty_bufs--;
+ mtx_unlock(&fsdev->nd_mutex);
+}
diff --git a/sys/fs/nandfs/nandfs_cleaner.c b/sys/fs/nandfs/nandfs_cleaner.c
new file mode 100644
index 0000000..9257c10
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_cleaner.c
@@ -0,0 +1,621 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/buf.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/bio.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+#define NANDFS_CLEANER_KILL 1
+
+static void nandfs_cleaner(struct nandfs_device *);
+static int nandfs_cleaner_clean_segments(struct nandfs_device *,
+ struct nandfs_vinfo *, uint32_t, struct nandfs_period *, uint32_t,
+ struct nandfs_bdesc *, uint32_t, uint64_t *, uint32_t);
+
+static int
+nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
+ uint64_t nmembs);
+
+static void
+nandfs_wakeup_wait_cleaner(struct nandfs_device *fsdev, int reason)
+{
+
+ mtx_lock(&fsdev->nd_clean_mtx);
+ if (reason == NANDFS_CLEANER_KILL)
+ fsdev->nd_cleaner_exit = 1;
+ if (fsdev->nd_cleaning == 0) {
+ fsdev->nd_cleaning = 1;
+ wakeup(&fsdev->nd_cleaning);
+ }
+ cv_wait(&fsdev->nd_clean_cv, &fsdev->nd_clean_mtx);
+ mtx_unlock(&fsdev->nd_clean_mtx);
+}
+
+int
+nandfs_start_cleaner(struct nandfs_device *fsdev)
+{
+ int error;
+
+ MPASS(fsdev->nd_cleaner == NULL);
+
+ fsdev->nd_cleaner_exit = 0;
+
+ error = kthread_add((void(*)(void *))nandfs_cleaner, fsdev, NULL,
+ &fsdev->nd_cleaner, 0, 0, "nandfs_cleaner");
+ if (error)
+ printf("nandfs: could not start cleaner: %d\n", error);
+
+ return (error);
+}
+
+int
+nandfs_stop_cleaner(struct nandfs_device *fsdev)
+{
+
+ MPASS(fsdev->nd_cleaner != NULL);
+ nandfs_wakeup_wait_cleaner(fsdev, NANDFS_CLEANER_KILL);
+ fsdev->nd_cleaner = NULL;
+
+ DPRINTF(CLEAN, ("cleaner stopped\n"));
+ return (0);
+}
+
+static int
+nandfs_cleaner_finished(struct nandfs_device *fsdev)
+{
+ int exit;
+
+ mtx_lock(&fsdev->nd_clean_mtx);
+ fsdev->nd_cleaning = 0;
+ if (!fsdev->nd_cleaner_exit) {
+ DPRINTF(CLEAN, ("%s: sleep\n", __func__));
+ msleep(&fsdev->nd_cleaning, &fsdev->nd_clean_mtx, PRIBIO, "-",
+ hz * nandfs_cleaner_interval);
+ }
+ exit = fsdev->nd_cleaner_exit;
+ cv_broadcast(&fsdev->nd_clean_cv);
+ mtx_unlock(&fsdev->nd_clean_mtx);
+ if (exit) {
+ DPRINTF(CLEAN, ("%s: no longer active\n", __func__));
+ return (1);
+ }
+
+ return (0);
+}
+
+static void
+print_suinfo(struct nandfs_suinfo *suinfo, int nsegs)
+{
+ int i;
+
+ for (i = 0; i < nsegs; i++) {
+ DPRINTF(CLEAN, ("%jx %jd %c%c%c %10u\n",
+ suinfo[i].nsi_num, suinfo[i].nsi_lastmod,
+ (suinfo[i].nsi_flags &
+ (NANDFS_SEGMENT_USAGE_ACTIVE) ? 'a' : '-'),
+ (suinfo[i].nsi_flags &
+ (NANDFS_SEGMENT_USAGE_DIRTY) ? 'd' : '-'),
+ (suinfo[i].nsi_flags &
+ (NANDFS_SEGMENT_USAGE_ERROR) ? 'e' : '-'),
+ suinfo[i].nsi_blocks));
+ }
+}
+
+static int
+nandfs_cleaner_vblock_is_alive(struct nandfs_device *fsdev,
+ struct nandfs_vinfo *vinfo, struct nandfs_cpinfo *cp, uint32_t ncps)
+{
+ int64_t idx, min, max;
+
+ if (vinfo->nvi_end >= fsdev->nd_last_cno)
+ return (1);
+
+ if (ncps == 0)
+ return (0);
+
+ if (vinfo->nvi_end < cp[0].nci_cno ||
+ vinfo->nvi_start > cp[ncps - 1].nci_cno)
+ return (0);
+
+ idx = min = 0;
+ max = ncps - 1;
+ while (min <= max) {
+ idx = (min + max) / 2;
+ if (vinfo->nvi_start == cp[idx].nci_cno)
+ return (1);
+ if (vinfo->nvi_start < cp[idx].nci_cno)
+ max = idx - 1;
+ else
+ min = idx + 1;
+ }
+
+ return (vinfo->nvi_end >= cp[idx].nci_cno);
+}
+
+static void
+nandfs_cleaner_vinfo_mark_alive(struct nandfs_device *fsdev,
+ struct nandfs_vinfo *vinfo, uint32_t nmembs, struct nandfs_cpinfo *cp,
+ uint32_t ncps)
+{
+ uint32_t i;
+
+ for (i = 0; i < nmembs; i++)
+ vinfo[i].nvi_alive =
+ nandfs_cleaner_vblock_is_alive(fsdev, &vinfo[i], cp, ncps);
+}
+
+static int
+nandfs_cleaner_bdesc_is_alive(struct nandfs_device *fsdev,
+ struct nandfs_bdesc *bdesc)
+{
+ int alive;
+
+ alive = bdesc->bd_oblocknr == bdesc->bd_blocknr;
+ if (!alive)
+ MPASS(abs(bdesc->bd_oblocknr - bdesc->bd_blocknr) > 2);
+
+ return (alive);
+}
+
+static void
+nandfs_cleaner_bdesc_mark_alive(struct nandfs_device *fsdev,
+ struct nandfs_bdesc *bdesc, uint32_t nmembs)
+{
+ uint32_t i;
+
+ for (i = 0; i < nmembs; i++)
+ bdesc[i].bd_alive = nandfs_cleaner_bdesc_is_alive(fsdev,
+ &bdesc[i]);
+}
+
+static void
+nandfs_cleaner_iterate_psegment(struct nandfs_device *fsdev,
+ struct nandfs_segment_summary *segsum, union nandfs_binfo *binfo,
+ nandfs_daddr_t blk, struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp)
+{
+ int i;
+
+ DPRINTF(CLEAN, ("%s nbinfos %x\n", __func__, segsum->ss_nbinfos));
+ for (i = 0; i < segsum->ss_nbinfos; i++) {
+ if (binfo[i].bi_v.bi_ino == NANDFS_DAT_INO) {
+ (*bdpp)->bd_oblocknr = blk + segsum->ss_nblocks -
+ segsum->ss_nbinfos + i;
+ /*
+ * XXX Hack
+ */
+ if (segsum->ss_flags & NANDFS_SS_SR)
+ (*bdpp)->bd_oblocknr--;
+ (*bdpp)->bd_level = binfo[i].bi_dat.bi_level;
+ (*bdpp)->bd_offset = binfo[i].bi_dat.bi_blkoff;
+ (*bdpp)++;
+ } else {
+ (*vipp)->nvi_ino = binfo[i].bi_v.bi_ino;
+ (*vipp)->nvi_vblocknr = binfo[i].bi_v.bi_vblocknr;
+ (*vipp)++;
+ }
+ }
+}
+
+static int
+nandfs_cleaner_iterate_segment(struct nandfs_device *fsdev, uint64_t segno,
+ struct nandfs_vinfo **vipp, struct nandfs_bdesc **bdpp, int *select)
+{
+ struct nandfs_segment_summary *segsum;
+ union nandfs_binfo *binfo;
+ struct buf *bp;
+ uint32_t nblocks;
+ nandfs_daddr_t curr, start, end;
+ int error = 0;
+
+ nandfs_get_segment_range(fsdev, segno, &start, &end);
+
+ DPRINTF(CLEAN, ("%s: segno %jx start %jx end %jx\n", __func__, segno,
+ start, end));
+
+ *select = 0;
+
+ for (curr = start; curr < end; curr += nblocks) {
+ error = nandfs_dev_bread(fsdev, curr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ nandfs_error("%s: couldn't load segment summary of %jx: %d\n",
+ __func__, segno, error);
+ return (error);
+ }
+
+ segsum = (struct nandfs_segment_summary *)bp->b_data;
+ binfo = (union nandfs_binfo *)(bp->b_data + segsum->ss_bytes);
+
+ if (!nandfs_segsum_valid(segsum)) {
+ brelse(bp);
+ nandfs_error("nandfs: invalid summary of segment %jx\n", segno);
+ return (error);
+ }
+
+ DPRINTF(CLEAN, ("%s: %jx magic %x bytes %x nblocks %x nbinfos "
+ "%x\n", __func__, segno, segsum->ss_magic, segsum->ss_bytes,
+ segsum->ss_nblocks, segsum->ss_nbinfos));
+
+ nandfs_cleaner_iterate_psegment(fsdev, segsum, binfo, curr,
+ vipp, bdpp);
+ nblocks = segsum->ss_nblocks;
+ brelse(bp);
+ }
+
+ if (error == 0)
+ *select = 1;
+
+ return (error);
+}
+
+static int
+nandfs_cleaner_choose_segment(struct nandfs_device *fsdev, uint64_t **segpp,
+ uint64_t nsegs, uint64_t *rseg)
+{
+ struct nandfs_suinfo *suinfo;
+ uint64_t i, ssegs;
+ int error;
+
+ suinfo = malloc(sizeof(*suinfo) * nsegs, M_NANDFSTEMP,
+ M_ZERO | M_WAITOK);
+
+ if (*rseg >= fsdev->nd_fsdata.f_nsegments)
+ *rseg = 0;
+
+retry:
+ error = nandfs_get_segment_info_filter(fsdev, suinfo, nsegs, *rseg,
+ &ssegs, NANDFS_SEGMENT_USAGE_DIRTY,
+ NANDFS_SEGMENT_USAGE_ACTIVE | NANDFS_SEGMENT_USAGE_ERROR |
+ NANDFS_SEGMENT_USAGE_GC);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ goto out;
+ }
+
+ if (ssegs == 0 && *rseg != 0) {
+ *rseg = 0;
+ goto retry;
+ }
+
+ print_suinfo(suinfo, ssegs);
+
+ for (i = 0; i < ssegs; i++) {
+ (**segpp) = suinfo[i].nsi_num;
+ (*segpp)++;
+ }
+
+ *rseg = suinfo[i - 1].nsi_num + 1;
+out:
+ free(suinfo, M_NANDFSTEMP);
+
+ return (error);
+}
+
+static int
+nandfs_cleaner_body(struct nandfs_device *fsdev, uint64_t *rseg)
+{
+ struct nandfs_vinfo *vinfo, *vip, *vipi;
+ struct nandfs_bdesc *bdesc, *bdp, *bdpi;
+ struct nandfs_cpstat cpstat;
+ struct nandfs_cpinfo *cpinfo = NULL;
+ uint64_t *segnums, *segp;
+ int select, selected;
+ int error = 0;
+ int nsegs;
+ int i;
+
+ nsegs = nandfs_cleaner_segments;
+
+ vip = vinfo = malloc(sizeof(*vinfo) *
+ fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
+ M_ZERO | M_WAITOK);
+ bdp = bdesc = malloc(sizeof(*bdesc) *
+ fsdev->nd_fsdata.f_blocks_per_segment * nsegs, M_NANDFSTEMP,
+ M_ZERO | M_WAITOK);
+ segp = segnums = malloc(sizeof(*segnums) * nsegs, M_NANDFSTEMP,
+ M_WAITOK);
+
+ error = nandfs_cleaner_choose_segment(fsdev, &segp, nsegs, rseg);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ goto out;
+ }
+
+ if (segnums == segp)
+ goto out;
+
+ selected = 0;
+ for (i = 0; i < segp - segnums; i++) {
+ error = nandfs_cleaner_iterate_segment(fsdev, segnums[i], &vip,
+ &bdp, &select);
+ if (error) {
+ /*
+ * XXX deselect (see below)?
+ */
+ goto out;
+ }
+ if (!select)
+ segnums[i] = NANDFS_NOSEGMENT;
+ else {
+ error = nandfs_markgc_segment(fsdev, segnums[i]);
+ if (error) {
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+ goto out;
+ }
+ selected++;
+ }
+ }
+
+ if (selected == 0) {
+ MPASS(vinfo == vip);
+ MPASS(bdesc == bdp);
+ goto out;
+ }
+
+ error = nandfs_get_cpstat(fsdev->nd_cp_node, &cpstat);
+ if (error) {
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+ goto out;
+ }
+
+ if (cpstat.ncp_nss != 0) {
+ cpinfo = malloc(sizeof(struct nandfs_cpinfo) * cpstat.ncp_nss,
+ M_NANDFSTEMP, M_WAITOK);
+ error = nandfs_get_cpinfo(fsdev->nd_cp_node, 1, NANDFS_SNAPSHOT,
+ cpinfo, cpstat.ncp_nss, NULL);
+ if (error) {
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+ goto out_locked;
+ }
+ }
+
+ NANDFS_WRITELOCK(fsdev);
+ DPRINTF(CLEAN, ("%s: got lock\n", __func__));
+
+ error = nandfs_get_dat_vinfo(fsdev, vinfo, vip - vinfo);
+ if (error) {
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+ goto out_locked;
+ }
+
+ nandfs_cleaner_vinfo_mark_alive(fsdev, vinfo, vip - vinfo, cpinfo,
+ cpstat.ncp_nss);
+
+ error = nandfs_get_dat_bdescs(fsdev, bdesc, bdp - bdesc);
+ if (error) {
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+ goto out_locked;
+ }
+
+ nandfs_cleaner_bdesc_mark_alive(fsdev, bdesc, bdp - bdesc);
+
+ DPRINTF(CLEAN, ("got:\n"));
+ for (vipi = vinfo; vipi < vip; vipi++) {
+ DPRINTF(CLEAN, ("v ino %jx vblocknr %jx start %jx end %jx "
+ "alive %d\n", vipi->nvi_ino, vipi->nvi_vblocknr,
+ vipi->nvi_start, vipi->nvi_end, vipi->nvi_alive));
+ }
+ for (bdpi = bdesc; bdpi < bdp; bdpi++) {
+ DPRINTF(CLEAN, ("b oblocknr %jx blocknr %jx offset %jx "
+ "alive %d\n", bdpi->bd_oblocknr, bdpi->bd_blocknr,
+ bdpi->bd_offset, bdpi->bd_alive));
+ }
+ DPRINTF(CLEAN, ("end list\n"));
+
+ error = nandfs_cleaner_clean_segments(fsdev, vinfo, vip - vinfo, NULL,
+ 0, bdesc, bdp - bdesc, segnums, segp - segnums);
+ if (error)
+ nandfs_error("%s:%d\n", __FILE__, __LINE__);
+
+out_locked:
+ NANDFS_WRITEUNLOCK(fsdev);
+out:
+ free(cpinfo, M_NANDFSTEMP);
+ free(segnums, M_NANDFSTEMP);
+ free(bdesc, M_NANDFSTEMP);
+ free(vinfo, M_NANDFSTEMP);
+
+ return (error);
+}
+
+static void
+nandfs_cleaner(struct nandfs_device *fsdev)
+{
+ uint64_t checked_seg = 0;
+ int error;
+
+ while (!nandfs_cleaner_finished(fsdev)) {
+ if (!nandfs_cleaner_enable || rebooting)
+ continue;
+
+ DPRINTF(CLEAN, ("%s: run started\n", __func__));
+
+ fsdev->nd_cleaning = 1;
+
+ error = nandfs_cleaner_body(fsdev, &checked_seg);
+
+ DPRINTF(CLEAN, ("%s: run finished error %d\n", __func__,
+ error));
+ }
+
+ DPRINTF(CLEAN, ("%s: exiting\n", __func__));
+ kthread_exit();
+}
+
+static int
+nandfs_cleaner_clean_segments(struct nandfs_device *nffsdev,
+ struct nandfs_vinfo *vinfo, uint32_t nvinfo,
+ struct nandfs_period *pd, uint32_t npd,
+ struct nandfs_bdesc *bdesc, uint32_t nbdesc,
+ uint64_t *segments, uint32_t nsegs)
+{
+ struct nandfs_node *gc;
+ struct buf *bp;
+ uint32_t i;
+ int error = 0;
+
+ gc = nffsdev->nd_gc_node;
+
+ DPRINTF(CLEAN, ("%s: enter\n", __func__));
+
+ VOP_LOCK(NTOV(gc), LK_EXCLUSIVE);
+ for (i = 0; i < nvinfo; i++) {
+ if (!vinfo[i].nvi_alive)
+ continue;
+ DPRINTF(CLEAN, ("%s: read vblknr:%#jx blk:%#jx\n",
+ __func__, (uintmax_t)vinfo[i].nvi_vblocknr,
+ (uintmax_t)vinfo[i].nvi_blocknr));
+ error = nandfs_bread(nffsdev->nd_gc_node, vinfo[i].nvi_blocknr,
+ NULL, 0, &bp);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ VOP_UNLOCK(NTOV(gc), 0);
+ goto out;
+ }
+ nandfs_vblk_set(bp, vinfo[i].nvi_vblocknr);
+ nandfs_buf_set(bp, NANDFS_VBLK_ASSIGNED);
+ nandfs_dirty_buf(bp, 1);
+ }
+ VOP_UNLOCK(NTOV(gc), 0);
+
+ /* Delete checkpoints */
+ for (i = 0; i < npd; i++) {
+ DPRINTF(CLEAN, ("delete checkpoint: %jx\n",
+ (uintmax_t)pd[i].p_start));
+ error = nandfs_delete_cp(nffsdev->nd_cp_node, pd[i].p_start,
+ pd[i].p_end);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ goto out;
+ }
+ }
+
+ /* Update vblocks */
+ for (i = 0; i < nvinfo; i++) {
+ if (vinfo[i].nvi_alive)
+ continue;
+ DPRINTF(CLEAN, ("freeing vblknr: %jx\n", vinfo[i].nvi_vblocknr));
+ error = nandfs_vblock_free(nffsdev, vinfo[i].nvi_vblocknr);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ goto out;
+ }
+ }
+
+ error = nandfs_process_bdesc(nffsdev, bdesc, nbdesc);
+ if (error) {
+ nandfs_error("%s:%d", __FILE__, __LINE__);
+ goto out;
+ }
+
+ /* Add segments to clean */
+ if (nffsdev->nd_free_count) {
+ nffsdev->nd_free_base = realloc(nffsdev->nd_free_base,
+ (nffsdev->nd_free_count + nsegs) * sizeof(uint64_t),
+ M_NANDFSTEMP, M_WAITOK | M_ZERO);
+ memcpy(&nffsdev->nd_free_base[nffsdev->nd_free_count], segments,
+ nsegs * sizeof(uint64_t));
+ nffsdev->nd_free_count += nsegs;
+ } else {
+ nffsdev->nd_free_base = malloc(nsegs * sizeof(uint64_t),
+ M_NANDFSTEMP, M_WAITOK|M_ZERO);
+ memcpy(nffsdev->nd_free_base, segments,
+ nsegs * sizeof(uint64_t));
+ nffsdev->nd_free_count = nsegs;
+ }
+
+out:
+
+ DPRINTF(CLEAN, ("%s: exit error %d\n", __func__, error));
+
+ return (error);
+}
+
+static int
+nandfs_process_bdesc(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
+ uint64_t nmembs)
+{
+ struct nandfs_node *dat_node;
+ struct buf *bp;
+ uint64_t i;
+ int error;
+
+ dat_node = nffsdev->nd_dat_node;
+
+ VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE);
+
+ for (i = 0; i < nmembs; i++) {
+ if (!bd[i].bd_alive)
+ continue;
+ DPRINTF(CLEAN, ("%s: idx %jx offset %jx\n",
+ __func__, i, bd[i].bd_offset));
+ if (bd[i].bd_level) {
+ error = nandfs_bread_meta(dat_node, bd[i].bd_offset,
+ NULL, 0, &bp);
+ if (error) {
+ nandfs_error("%s: cannot read dat node "
+ "level:%d\n", __func__, bd[i].bd_level);
+ brelse(bp);
+ VOP_UNLOCK(NTOV(dat_node), 0);
+ return (error);
+ }
+ nandfs_dirty_buf_meta(bp, 1);
+ nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1);
+ } else {
+ error = nandfs_bread(dat_node, bd[i].bd_offset, NULL,
+ 0, &bp);
+ if (error) {
+ nandfs_error("%s: cannot read dat node\n",
+ __func__);
+ brelse(bp);
+ VOP_UNLOCK(NTOV(dat_node), 0);
+ return (error);
+ }
+ nandfs_dirty_buf(bp, 1);
+ }
+ DPRINTF(CLEAN, ("%s: bp: %p\n", __func__, bp));
+ }
+
+ VOP_UNLOCK(NTOV(dat_node), 0);
+
+ return (0);
+}
diff --git a/sys/fs/nandfs/nandfs_cpfile.c b/sys/fs/nandfs/nandfs_cpfile.c
new file mode 100644
index 0000000..8814fc0
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_cpfile.c
@@ -0,0 +1,776 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+
+
+static int
+nandfs_checkpoint_size(struct nandfs_device *fsdev)
+{
+
+ return (fsdev->nd_fsdata.f_checkpoint_size);
+}
+
+static int
+nandfs_checkpoint_blk_offset(struct nandfs_device *fsdev, uint64_t cn,
+ uint64_t *blk, uint64_t *offset)
+{
+ uint64_t off;
+ uint16_t cp_size, cp_per_blk;
+
+ KASSERT((cn), ("checkpoing cannot be zero"));
+
+ cp_size = fsdev->nd_fsdata.f_checkpoint_size;
+ cp_per_blk = fsdev->nd_blocksize / cp_size;
+ off = roundup(sizeof(struct nandfs_cpfile_header), cp_size) / cp_size;
+ off += (cn - 1);
+
+ *blk = off / cp_per_blk;
+ *offset = (off % cp_per_blk) * cp_size;
+
+ return (0);
+}
+
+static int
+nandfs_checkpoint_blk_remaining(struct nandfs_device *fsdev, uint64_t cn,
+ uint64_t blk, uint64_t offset)
+{
+ uint16_t cp_size, cp_remaining;
+
+ cp_size = fsdev->nd_fsdata.f_checkpoint_size;
+ cp_remaining = (fsdev->nd_blocksize - offset) / cp_size;
+
+ return (cp_remaining);
+}
+
+int
+nandfs_get_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
+ uint64_t cn)
+{
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
+ return (-1);
+ }
+
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (-1);
+ }
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (-1);
+
+
+ nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
+
+ if (blk != 0) {
+ if (blk < cp_node->nn_inode.i_blocks)
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ else
+ error = nandfs_bcreate(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return (-1);
+ }
+
+ nandfs_dirty_buf(bp, 1);
+ }
+
+ DPRINTF(CPFILE, ("%s: cn:%#jx entry block:%#jx offset:%#jx\n",
+ __func__, (uintmax_t)cn, (uintmax_t)blk, (uintmax_t)offset));
+
+ return (0);
+}
+
+int
+nandfs_set_checkpoint(struct nandfs_device *fsdev, struct nandfs_node *cp_node,
+ uint64_t cn, struct nandfs_inode *ifile_inode, uint64_t nblocks)
+{
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ if (cn != fsdev->nd_last_cno && cn != (fsdev->nd_last_cno + 1)) {
+ nandfs_error("%s: trying to set invalid chekpoint %jx - %jx\n",
+ __func__, cn, fsdev->nd_last_cno);
+ return (-1);
+ }
+
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ cnh->ch_ncheckpoints++;
+
+ nandfs_checkpoint_blk_offset(fsdev, cn, &blk, &offset);
+
+ if(blk != 0) {
+ brelse(bp);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ }
+
+ cnp = (struct nandfs_checkpoint *)((uint8_t *)bp->b_data + offset);
+ cnp->cp_flags = 0;
+ cnp->cp_checkpoints_count = 1;
+ memset(&cnp->cp_snapshot_list, 0, sizeof(struct nandfs_snapshot_list));
+ cnp->cp_cno = cn;
+ cnp->cp_create = fsdev->nd_ts.tv_sec;
+ cnp->cp_nblk_inc = nblocks;
+ cnp->cp_blocks_count = 0;
+ memcpy (&cnp->cp_ifile_inode, ifile_inode, sizeof(cnp->cp_ifile_inode));
+
+ DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx nblk:%#jx\n",
+ __func__, (uintmax_t)cn, (uintmax_t)cnp->cp_create,
+ (uintmax_t)nblocks));
+
+ brelse(bp);
+ return (0);
+}
+
+static int
+nandfs_cp_mounted(struct nandfs_device *nandfsdev, uint64_t cno)
+{
+ struct nandfsmount *nmp;
+ int mounted = 0;
+
+ mtx_lock(&nandfsdev->nd_mutex);
+ /* No double-mounting of the same checkpoint */
+ STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
+ if (nmp->nm_mount_args.cpno == cno) {
+ mounted = 1;
+ break;
+ }
+ }
+ mtx_unlock(&nandfsdev->nd_mutex);
+
+ return (mounted);
+}
+
+static int
+nandfs_cp_set_snapshot(struct nandfs_node *cp_node, uint64_t cno)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_snapshot_list *list;
+ struct buf *bp;
+ uint64_t blk, prev_blk, offset;
+ uint64_t curr, prev;
+ int error;
+
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get snapshot data */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ if ((cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
+ brelse(bp);
+ return (EINVAL);
+ }
+
+ brelse(bp);
+ /* Get list from header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ prev = list->ssl_prev;
+ brelse(bp);
+ prev_blk = ~(0);
+ curr = 0;
+ while (prev > cno) {
+ curr = prev;
+ nandfs_checkpoint_blk_offset(fsdev, prev, &prev_blk, &offset);
+ error = nandfs_bread(cp_node, prev_blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ prev = list->ssl_prev;
+ brelse(bp);
+ }
+
+ if (curr == 0) {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ } else {
+ nandfs_checkpoint_blk_offset(fsdev, curr, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ }
+
+ list->ssl_prev = cno;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+
+ /* Update snapshot for cno */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ list->ssl_prev = prev;
+ list->ssl_next = curr;
+ cnp->cp_flags |= NANDFS_CHECKPOINT_SNAPSHOT;
+ nandfs_dirty_buf(bp, 1);
+
+ if (prev == 0) {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ } else {
+ /* Update snapshot list for prev */
+ nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ }
+ list->ssl_next = cno;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ cnh->ch_nsnapshots++;
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+static int
+nandfs_cp_clr_snapshot(struct nandfs_node *cp_node, uint64_t cno)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_snapshot_list *list;
+ struct buf *bp;
+ uint64_t blk, offset, snapshot_cnt;
+ uint64_t next, prev;
+ int error;
+
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get snapshot data */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ if (!(cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT)) {
+ brelse(bp);
+ return (EINVAL);
+ }
+
+ list = &cnp->cp_snapshot_list;
+ next = list->ssl_next;
+ prev = list->ssl_prev;
+ brelse(bp);
+
+ /* Get previous snapshot */
+ if (prev != 0) {
+ nandfs_checkpoint_blk_offset(fsdev, prev, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ } else {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ }
+
+ list->ssl_next = next;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ /* Get next snapshot */
+ if (next != 0) {
+ nandfs_checkpoint_blk_offset(fsdev, next, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ } else {
+ nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ list = &cnh->ch_snapshot_list;
+ }
+ list->ssl_prev = prev;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update snapshot list for cno */
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(cp_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ list = &cnp->cp_snapshot_list;
+ list->ssl_prev = 0;
+ list->ssl_next = 0;
+ cnp->cp_flags &= !NANDFS_CHECKPOINT_SNAPSHOT;
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ snapshot_cnt = cnh->ch_nsnapshots;
+ snapshot_cnt--;
+ cnh->ch_nsnapshots = snapshot_cnt;
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+int
+nandfs_chng_cpmode(struct nandfs_node *node, struct nandfs_cpmode *ncpm)
+{
+ struct nandfs_device *fsdev;
+ uint64_t cno = ncpm->ncpm_cno;
+ int mode = ncpm->ncpm_mode;
+ int ret;
+
+ fsdev = node->nn_nandfsdev;
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ switch (mode) {
+ case NANDFS_CHECKPOINT:
+ if (nandfs_cp_mounted(fsdev, cno)) {
+ ret = EBUSY;
+ } else
+ ret = nandfs_cp_clr_snapshot(node, cno);
+ break;
+ case NANDFS_SNAPSHOT:
+ ret = nandfs_cp_set_snapshot(node, cno);
+ break;
+ default:
+ ret = EINVAL;
+ break;
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (ret);
+}
+
+static void
+nandfs_cpinfo_fill(struct nandfs_checkpoint *cnp, struct nandfs_cpinfo *nci)
+{
+
+ nci->nci_flags = cnp->cp_flags;
+ nci->nci_pad = 0;
+ nci->nci_cno = cnp->cp_cno;
+ nci->nci_create = cnp->cp_create;
+ nci->nci_nblk_inc = cnp->cp_nblk_inc;
+ nci->nci_blocks_count = cnp->cp_blocks_count;
+ nci->nci_next = cnp->cp_snapshot_list.ssl_next;
+ DPRINTF(CPFILE, ("%s: cn:%#jx ctime:%#jx\n",
+ __func__, (uintmax_t)cnp->cp_cno,
+ (uintmax_t)cnp->cp_create));
+}
+
+static int
+nandfs_get_cpinfo_cp(struct nandfs_node *node, uint64_t cno,
+ struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
+{
+ struct nandfs_device *fsdev;
+ struct buf *bp;
+ uint64_t blk, offset, last_cno, i;
+ uint16_t remaining;
+ int error;
+#ifdef INVARIANTS
+ uint64_t testblk, testoffset;
+#endif
+
+ if (cno == 0) {
+ return (ENOENT);
+ }
+
+ if (mnmembs < 1) {
+ return (EINVAL);
+ }
+
+ fsdev = node->nn_nandfsdev;
+ last_cno = fsdev->nd_last_cno;
+ DPRINTF(CPFILE, ("%s: cno:%#jx mnmembs: %#jx last:%#jx\n", __func__,
+ (uintmax_t)cno, (uintmax_t)mnmembs,
+ (uintmax_t)fsdev->nd_last_cno));
+
+ /*
+ * do {
+ * get block
+ * read checkpoints until we hit last checkpoint, end of block or
+ * requested number
+ * } while (last read checkpoint <= last checkpoint on fs &&
+ * read checkpoints < request number);
+ */
+ *nmembs = i = 0;
+ do {
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ remaining = nandfs_checkpoint_blk_remaining(fsdev, cno,
+ blk, offset);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ while (cno <= last_cno && i < mnmembs && remaining) {
+#ifdef INVARIANTS
+ nandfs_checkpoint_blk_offset(fsdev, cno, &testblk,
+ &testoffset);
+ KASSERT(testblk == blk, ("testblk != blk"));
+ KASSERT(testoffset == offset, ("testoffset != offset"));
+#endif
+ DPRINTF(CPFILE, ("%s: cno %#jx\n", __func__,
+ (uintmax_t)cno));
+
+ nandfs_cpinfo_fill((struct nandfs_checkpoint *)
+ (bp->b_data + offset), nci);
+ offset += nandfs_checkpoint_size(fsdev);
+ i++;
+ nci++;
+ cno++;
+ (*nmembs)++;
+ remaining--;
+ }
+ brelse(bp);
+ } while (cno <= last_cno && i < mnmembs);
+
+ return (0);
+}
+
+static int
+nandfs_get_cpinfo_sp(struct nandfs_node *node, uint64_t cno,
+ struct nandfs_cpinfo *nci, uint32_t mnmembs, uint32_t *nmembs)
+{
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_cpfile_header *cnh;
+ struct nandfs_device *fsdev;
+ struct buf *bp = NULL;
+ uint64_t curr = 0;
+ uint64_t blk, offset, curr_cno;
+ uint32_t flag;
+ int i, error;
+
+ if (cno == 0 || cno == ~(0))
+ return (ENOENT);
+
+ fsdev = node->nn_nandfsdev;
+ curr_cno = cno;
+
+ if (nmembs)
+ *nmembs = 0;
+ if (curr_cno == 1) {
+ /* Get list from header */
+ error = nandfs_bread(node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ curr_cno = cnh->ch_snapshot_list.ssl_next;
+ brelse(bp);
+ bp = NULL;
+
+ /* No snapshots */
+ if (curr_cno == 0)
+ return (0);
+ }
+
+ for (i = 0; i < mnmembs; i++, nci++) {
+ nandfs_checkpoint_blk_offset(fsdev, curr_cno, &blk, &offset);
+ if (i == 0 || curr != blk) {
+ if (bp)
+ brelse(bp);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (ENOENT);
+ }
+ curr = blk;
+ }
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ flag = cnp->cp_flags;
+ if (!(flag & NANDFS_CHECKPOINT_SNAPSHOT) ||
+ (flag & NANDFS_CHECKPOINT_INVALID))
+ break;
+
+ nci->nci_flags = flag;
+ nci->nci_pad = 0;
+ nci->nci_cno = cnp->cp_cno;
+ nci->nci_create = cnp->cp_create;
+ nci->nci_nblk_inc = cnp->cp_nblk_inc;
+ nci->nci_blocks_count = cnp->cp_blocks_count;
+ nci->nci_next = cnp->cp_snapshot_list.ssl_next;
+ if (nmembs)
+ (*nmembs)++;
+
+ curr_cno = nci->nci_next;
+ if (!curr_cno)
+ break;
+ }
+
+ brelse(bp);
+
+ return (0);
+}
+
+int
+nandfs_get_cpinfo(struct nandfs_node *node, uint64_t cno, uint16_t flags,
+ struct nandfs_cpinfo *nci, uint32_t nmembs, uint32_t *nnmembs)
+{
+ int error;
+
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ switch (flags) {
+ case NANDFS_CHECKPOINT:
+ error = nandfs_get_cpinfo_cp(node, cno, nci, nmembs, nnmembs);
+ break;
+ case NANDFS_SNAPSHOT:
+ error = nandfs_get_cpinfo_sp(node, cno, nci, nmembs, nnmembs);
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (error);
+}
+
+int
+nandfs_get_cpinfo_ioctl(struct nandfs_node *node, struct nandfs_argv *nargv)
+{
+ struct nandfs_cpinfo *nci;
+ uint64_t cno = nargv->nv_index;
+ void *buf = (void *)((uintptr_t)nargv->nv_base);
+ uint16_t flags = nargv->nv_flags;
+ uint32_t nmembs = 0;
+ int error;
+
+ if (nargv->nv_nmembs > NANDFS_CPINFO_MAX)
+ return (EINVAL);
+
+ nci = malloc(sizeof(struct nandfs_cpinfo) * nargv->nv_nmembs,
+ M_NANDFSTEMP, M_WAITOK | M_ZERO);
+
+ error = nandfs_get_cpinfo(node, cno, flags, nci, nargv->nv_nmembs, &nmembs);
+
+ if (error == 0) {
+ nargv->nv_nmembs = nmembs;
+ error = copyout(nci, buf,
+ sizeof(struct nandfs_cpinfo) * nmembs);
+ }
+
+ free(nci, M_NANDFSTEMP);
+ return (error);
+}
+
+int
+nandfs_delete_cp(struct nandfs_node *node, uint64_t start, uint64_t end)
+{
+ struct nandfs_checkpoint *cnp;
+ struct nandfs_device *fsdev;
+ struct buf *bp;
+ uint64_t cno = start, blk, offset;
+ int error;
+
+ DPRINTF(CPFILE, ("%s: delete cno %jx-%jx\n", __func__, start, end));
+ VOP_LOCK(NTOV(node), LK_EXCLUSIVE);
+ fsdev = node->nn_nandfsdev;
+ for (cno = start; cno <= end; cno++) {
+ if (!cno)
+ continue;
+
+ nandfs_checkpoint_blk_offset(fsdev, cno, &blk, &offset);
+ error = nandfs_bread(node, blk, NOCRED, 0, &bp);
+ if (error) {
+ VOP_UNLOCK(NTOV(node), 0);
+ brelse(bp);
+ return (error);
+ }
+
+ cnp = (struct nandfs_checkpoint *)(bp->b_data + offset);
+ if (cnp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(node), 0);
+ return (0);
+ }
+
+ cnp->cp_flags |= NANDFS_CHECKPOINT_INVALID;
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+ }
+ VOP_UNLOCK(NTOV(node), 0);
+
+ return (0);
+}
+
+int
+nandfs_make_snap(struct nandfs_device *fsdev, uint64_t *cno)
+{
+ struct nandfs_cpmode cpm;
+ int error;
+
+ *cno = cpm.ncpm_cno = fsdev->nd_last_cno;
+ cpm.ncpm_mode = NANDFS_SNAPSHOT;
+ error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
+ return (error);
+}
+
+int
+nandfs_delete_snap(struct nandfs_device *fsdev, uint64_t cno)
+{
+ struct nandfs_cpmode cpm;
+ int error;
+
+ cpm.ncpm_cno = cno;
+ cpm.ncpm_mode = NANDFS_CHECKPOINT;
+ error = nandfs_chng_cpmode(fsdev->nd_cp_node, &cpm);
+ return (error);
+}
+
+int nandfs_get_cpstat(struct nandfs_node *cp_node, struct nandfs_cpstat *ncp)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_cpfile_header *cnh;
+ struct buf *bp;
+ int error;
+
+ VOP_LOCK(NTOV(cp_node), LK_EXCLUSIVE);
+ fsdev = cp_node->nn_nandfsdev;
+
+ /* Get header */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (error);
+ }
+ cnh = (struct nandfs_cpfile_header *) bp->b_data;
+ ncp->ncp_cno = fsdev->nd_last_cno;
+ ncp->ncp_ncps = cnh->ch_ncheckpoints;
+ ncp->ncp_nss = cnh->ch_nsnapshots;
+ DPRINTF(CPFILE, ("%s: cno:%#jx ncps:%#jx nss:%#jx\n",
+ __func__, ncp->ncp_cno, ncp->ncp_ncps, ncp->ncp_nss));
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+
+ return (0);
+}
diff --git a/sys/fs/nandfs/nandfs_dat.c b/sys/fs/nandfs/nandfs_dat.c
new file mode 100644
index 0000000..799113d
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_dat.c
@@ -0,0 +1,344 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+int
+nandfs_vblock_alloc(struct nandfs_device *nandfsdev, nandfs_daddr_t *vblock)
+{
+ struct nandfs_node *dat;
+ struct nandfs_mdt *mdt;
+ struct nandfs_alloc_request req;
+ struct nandfs_dat_entry *dat_entry;
+ uint64_t start;
+ uint32_t entry;
+ int locked, error;
+
+ dat = nandfsdev->nd_dat_node;
+ mdt = &nandfsdev->nd_dat_mdt;
+ start = nandfsdev->nd_last_cno + 1;
+
+ locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
+ if (!locked)
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ req.entrynum = 0;
+
+ /* Alloc vblock number */
+ error = nandfs_find_free_entry(mdt, dat, &req);
+ if (error) {
+ nandfs_error("%s: cannot find free vblk entry\n",
+ __func__);
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+ }
+
+ /* Read/create buffer */
+ error = nandfs_get_entry_block(mdt, dat, &req, &entry, 1);
+ if (error) {
+ nandfs_error("%s: cannot get free vblk entry\n",
+ __func__);
+ nandfs_abort_entry(&req);
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+ }
+
+ /* Fill out vblock data */
+ dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
+ dat_entry[entry].de_start = start;
+ dat_entry[entry].de_end = UINTMAX_MAX;
+ dat_entry[entry].de_blocknr = 0;
+
+ /* Commit allocation */
+ error = nandfs_alloc_entry(mdt, &req);
+ if (error) {
+ nandfs_error("%s: cannot get free vblk entry\n",
+ __func__);
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+ }
+
+ /* Return allocated vblock */
+ *vblock = req.entrynum;
+ DPRINTF(DAT, ("%s: allocated vblock %#jx\n",
+ __func__, (uintmax_t)*vblock));
+
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+}
+
+int
+nandfs_vblock_assign(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock,
+ nandfs_lbn_t block)
+{
+ struct nandfs_node *dat;
+ struct nandfs_mdt *mdt;
+ struct nandfs_alloc_request req;
+ struct nandfs_dat_entry *dat_entry;
+ uint32_t entry;
+ int locked, error;
+
+ dat = nandfsdev->nd_dat_node;
+ mdt = &nandfsdev->nd_dat_mdt;
+
+ locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
+ if (!locked)
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ req.entrynum = vblock;
+
+ error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0);
+ if (!error) {
+ dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
+ dat_entry[entry].de_blocknr = block;
+
+ DPRINTF(DAT, ("%s: assing vblock %jx->%jx\n",
+ __func__, (uintmax_t)vblock, (uintmax_t)block));
+
+ /*
+ * It is mostly called from syncer() so
+ * we want to force making buf dirty
+ */
+ error = nandfs_dirty_buf(req.bp_entry, 1);
+ }
+
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+
+ return (error);
+}
+
+int
+nandfs_vblock_end(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock)
+{
+ struct nandfs_node *dat;
+ struct nandfs_mdt *mdt;
+ struct nandfs_alloc_request req;
+ struct nandfs_dat_entry *dat_entry;
+ uint64_t end;
+ uint32_t entry;
+ int locked, error;
+
+ dat = nandfsdev->nd_dat_node;
+ mdt = &nandfsdev->nd_dat_mdt;
+ end = nandfsdev->nd_last_cno;
+
+ locked = NANDFS_VOP_ISLOCKED(NTOV(dat));
+ if (!locked)
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ req.entrynum = vblock;
+
+ error = nandfs_get_entry_block(mdt, dat, &req, &entry, 0);
+ if (!error) {
+ dat_entry = (struct nandfs_dat_entry *) req.bp_entry->b_data;
+ dat_entry[entry].de_end = end;
+ DPRINTF(DAT, ("%s: end vblock %#jx at checkpoint %#jx\n",
+ __func__, (uintmax_t)vblock, (uintmax_t)end));
+
+ /*
+ * It is mostly called from syncer() so
+ * we want to force making buf dirty
+ */
+ error = nandfs_dirty_buf(req.bp_entry, 1);
+ }
+
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat), 0);
+
+ return (error);
+}
+
+int
+nandfs_vblock_free(struct nandfs_device *nandfsdev, nandfs_daddr_t vblock)
+{
+ struct nandfs_node *dat;
+ struct nandfs_mdt *mdt;
+ struct nandfs_alloc_request req;
+ int error;
+
+ dat = nandfsdev->nd_dat_node;
+ mdt = &nandfsdev->nd_dat_mdt;
+
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ req.entrynum = vblock;
+
+ error = nandfs_find_entry(mdt, dat, &req);
+ if (!error) {
+ DPRINTF(DAT, ("%s: vblk %#jx\n", __func__, (uintmax_t)vblock));
+ nandfs_free_entry(mdt, &req);
+ }
+
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+}
+
+int
+nandfs_get_dat_vinfo_ioctl(struct nandfs_device *nandfsdev, struct nandfs_argv *nargv)
+{
+ struct nandfs_vinfo *vinfo;
+ size_t size;
+ int error;
+
+ if (nargv->nv_nmembs > NANDFS_VINFO_MAX)
+ return (EINVAL);
+
+ size = sizeof(struct nandfs_vinfo) * nargv->nv_nmembs;
+ vinfo = malloc(size, M_NANDFSTEMP, M_WAITOK|M_ZERO);
+
+ error = copyin((void *)(uintptr_t)nargv->nv_base, vinfo, size);
+ if (error) {
+ free(vinfo, M_NANDFSTEMP);
+ return (error);
+ }
+
+ error = nandfs_get_dat_vinfo(nandfsdev, vinfo, nargv->nv_nmembs);
+ if (error == 0)
+ error = copyout(vinfo, (void *)(uintptr_t)nargv->nv_base, size);
+ free(vinfo, M_NANDFSTEMP);
+ return (error);
+}
+
+int
+nandfs_get_dat_vinfo(struct nandfs_device *nandfsdev, struct nandfs_vinfo *vinfo,
+ uint32_t nmembs)
+{
+ struct nandfs_node *dat;
+ struct nandfs_mdt *mdt;
+ struct nandfs_alloc_request req;
+ struct nandfs_dat_entry *dat_entry;
+ uint32_t i, idx;
+ int error = 0;
+
+ dat = nandfsdev->nd_dat_node;
+ mdt = &nandfsdev->nd_dat_mdt;
+
+ DPRINTF(DAT, ("%s: nmembs %#x\n", __func__, nmembs));
+
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+
+ for (i = 0; i < nmembs; i++) {
+ req.entrynum = vinfo[i].nvi_vblocknr;
+
+ error = nandfs_get_entry_block(mdt, dat,&req, &idx, 0);
+ if (error)
+ break;
+
+ dat_entry = ((struct nandfs_dat_entry *) req.bp_entry->b_data);
+ vinfo[i].nvi_start = dat_entry[idx].de_start;
+ vinfo[i].nvi_end = dat_entry[idx].de_end;
+ vinfo[i].nvi_blocknr = dat_entry[idx].de_blocknr;
+
+ DPRINTF(DAT, ("%s: vinfo: %jx[%jx-%jx]->%jx\n",
+ __func__, vinfo[i].nvi_vblocknr, vinfo[i].nvi_start,
+ vinfo[i].nvi_end, vinfo[i].nvi_blocknr));
+
+ brelse(req.bp_entry);
+ }
+
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+}
+
+int
+nandfs_get_dat_bdescs_ioctl(struct nandfs_device *nffsdev,
+ struct nandfs_argv *nargv)
+{
+ struct nandfs_bdesc *bd;
+ size_t size;
+ int error;
+
+ size = nargv->nv_nmembs * sizeof(struct nandfs_bdesc);
+ bd = malloc(size, M_NANDFSTEMP, M_WAITOK);
+ error = copyin((void *)(uintptr_t)nargv->nv_base, bd, size);
+ if (error) {
+ free(bd, M_NANDFSTEMP);
+ return (error);
+ }
+
+ error = nandfs_get_dat_bdescs(nffsdev, bd, nargv->nv_nmembs);
+
+ if (error == 0)
+ error = copyout(bd, (void *)(uintptr_t)nargv->nv_base, size);
+
+ free(bd, M_NANDFSTEMP);
+ return (error);
+}
+
+int
+nandfs_get_dat_bdescs(struct nandfs_device *nffsdev, struct nandfs_bdesc *bd,
+ uint32_t nmembs)
+{
+ struct nandfs_node *dat_node;
+ uint64_t map;
+ uint32_t i;
+ int error = 0;
+
+ dat_node = nffsdev->nd_dat_node;
+
+ VOP_LOCK(NTOV(dat_node), LK_EXCLUSIVE);
+
+ for (i = 0; i < nmembs; i++) {
+ DPRINTF(CLEAN,
+ ("%s: bd ino:%#jx oblk:%#jx blocknr:%#jx off:%#jx\n",
+ __func__, (uintmax_t)bd[i].bd_ino,
+ (uintmax_t)bd[i].bd_oblocknr, (uintmax_t)bd[i].bd_blocknr,
+ (uintmax_t)bd[i].bd_offset));
+
+ error = nandfs_bmap_lookup(dat_node, bd[i].bd_offset, &map);
+ if (error)
+ break;
+ bd[i].bd_blocknr = map;
+ }
+
+ VOP_UNLOCK(NTOV(dat_node), 0);
+ return (error);
+}
diff --git a/sys/fs/nandfs/nandfs_dir.c b/sys/fs/nandfs/nandfs_dir.c
new file mode 100644
index 0000000..e279510
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_dir.c
@@ -0,0 +1,314 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/kernel.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+
+int
+nandfs_add_dirent(struct vnode *dvp, uint64_t ino, char *nameptr, long namelen,
+ uint8_t type)
+{
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfs_dir_entry *dirent, *pdirent;
+ uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
+ uint64_t filesize = dir_node->nn_inode.i_size;
+ uint64_t inode_blks = dir_node->nn_inode.i_blocks;
+ uint32_t off, rest;
+ uint8_t *pos;
+ struct buf *bp;
+ int error;
+
+ pdirent = NULL;
+ bp = NULL;
+ if (inode_blks) {
+ error = nandfs_bread(dir_node, inode_blks - 1, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ pos = bp->b_data;
+ off = 0;
+ while (off < blocksize) {
+ pdirent = (struct nandfs_dir_entry *) (pos + off);
+ if (!pdirent->rec_len) {
+ pdirent = NULL;
+ break;
+ }
+ off += pdirent->rec_len;
+ }
+
+ if (pdirent)
+ rest = pdirent->rec_len -
+ NANDFS_DIR_REC_LEN(pdirent->name_len);
+ else
+ rest = blocksize;
+
+ if (rest < NANDFS_DIR_REC_LEN(namelen)) {
+ /* Do not update pdirent as new block is created */
+ pdirent = NULL;
+ brelse(bp);
+ /* Set to NULL to create new */
+ bp = NULL;
+ filesize += rest;
+ }
+ }
+
+ /* If no bp found create new */
+ if (!bp) {
+ error = nandfs_bcreate(dir_node, inode_blks, NOCRED, 0, &bp);
+ if (error)
+ return (error);
+ off = 0;
+ pos = bp->b_data;
+ }
+
+ /* Modify pdirent if exists */
+ if (pdirent) {
+ DPRINTF(LOOKUP, ("modify pdirent %p\n", pdirent));
+ /* modify last de */
+ off -= pdirent->rec_len;
+ pdirent->rec_len =
+ NANDFS_DIR_REC_LEN(pdirent->name_len);
+ off += pdirent->rec_len;
+ }
+
+ /* Create new dirent */
+ dirent = (struct nandfs_dir_entry *) (pos + off);
+ dirent->rec_len = blocksize - off;
+ dirent->inode = ino;
+ dirent->name_len = namelen;
+ memset(dirent->name, 0, NANDFS_DIR_NAME_LEN(namelen));
+ memcpy(dirent->name, nameptr, namelen);
+ dirent->file_type = type;
+
+ filesize += NANDFS_DIR_REC_LEN(dirent->name_len);
+
+ DPRINTF(LOOKUP, ("create dir_entry '%.*s' at %p with size %x "
+ "new filesize: %jx\n",
+ (int)namelen, dirent->name, dirent, dirent->rec_len,
+ (uintmax_t)filesize));
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ dir_node->nn_inode.i_size = filesize;
+ dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ vnode_pager_setsize(dvp, filesize);
+
+ return (0);
+}
+
+int
+nandfs_remove_dirent(struct vnode *dvp, struct nandfs_node *node,
+ struct componentname *cnp)
+{
+ struct nandfs_node *dir_node;
+ struct nandfs_dir_entry *dirent, *pdirent;
+ struct buf *bp;
+ uint64_t filesize, blocknr, ino, offset;
+ uint32_t blocksize, limit, off;
+ uint16_t newsize;
+ uint8_t *pos;
+ int error, found;
+
+ dir_node = VTON(dvp);
+ filesize = dir_node->nn_inode.i_size;
+ if (!filesize)
+ return (0);
+
+ if (node) {
+ offset = node->nn_diroff;
+ ino = node->nn_ino;
+ } else {
+ offset = dir_node->nn_diroff;
+ ino = NANDFS_WHT_INO;
+ }
+
+ dirent = pdirent = NULL;
+ blocksize = dir_node->nn_nandfsdev->nd_blocksize;
+ blocknr = offset / blocksize;
+
+ DPRINTF(LOOKUP, ("rm direntry dvp %p node %p ino %#jx at off %#jx\n",
+ dvp, node, (uintmax_t)ino, (uintmax_t)offset));
+
+ error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ pos = bp->b_data;
+ off = 0;
+ found = 0;
+ limit = offset % blocksize;
+ pdirent = (struct nandfs_dir_entry *) bp->b_data;
+ while (off <= limit) {
+ dirent = (struct nandfs_dir_entry *) (pos + off);
+
+ if ((off == limit) &&
+ (dirent->inode == ino)) {
+ found = 1;
+ break;
+ }
+ if (dirent->inode != 0)
+ pdirent = dirent;
+ off += dirent->rec_len;
+ }
+
+ if (!found) {
+ nandfs_error("cannot find entry to remove");
+ brelse(bp);
+ return (error);
+ }
+ DPRINTF(LOOKUP,
+ ("rm dirent ino %#jx at %#x with size %#x\n",
+ (uintmax_t)dirent->inode, off, dirent->rec_len));
+
+ newsize = (uintptr_t)dirent - (uintptr_t)pdirent;
+ newsize += dirent->rec_len;
+ pdirent->rec_len = newsize;
+ dirent->inode = 0;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ dir_node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ /* If last one modify filesize */
+ if ((offset + NANDFS_DIR_REC_LEN(dirent->name_len)) == filesize) {
+ filesize = blocknr * blocksize +
+ ((uintptr_t)pdirent - (uintptr_t)pos) +
+ NANDFS_DIR_REC_LEN(pdirent->name_len);
+ dir_node->nn_inode.i_size = filesize;
+ }
+
+ return (0);
+}
+
+int
+nandfs_update_parent_dir(struct vnode *dvp, uint64_t newparent)
+{
+ struct nandfs_dir_entry *dirent;
+ struct nandfs_node *dir_node;
+ struct buf *bp;
+ int error;
+
+ dir_node = VTON(dvp);
+ error = nandfs_bread(dir_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ dirent = (struct nandfs_dir_entry *)bp->b_data;
+ dirent->inode = newparent;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+int
+nandfs_update_dirent(struct vnode *dvp, struct nandfs_node *fnode,
+ struct nandfs_node *tnode)
+{
+ struct nandfs_node *dir_node;
+ struct nandfs_dir_entry *dirent;
+ struct buf *bp;
+ uint64_t file_size, blocknr;
+ uint32_t blocksize, off;
+ uint8_t *pos;
+ int error;
+
+ dir_node = VTON(dvp);
+ file_size = dir_node->nn_inode.i_size;
+ if (!file_size)
+ return (0);
+
+ DPRINTF(LOOKUP,
+ ("chg direntry dvp %p ino %#jx to in %#jx at off %#jx\n",
+ dvp, (uintmax_t)tnode->nn_ino, (uintmax_t)fnode->nn_ino,
+ (uintmax_t)tnode->nn_diroff));
+
+ blocksize = dir_node->nn_nandfsdev->nd_blocksize;
+ blocknr = tnode->nn_diroff / blocksize;
+ off = tnode->nn_diroff % blocksize;
+ error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ pos = bp->b_data;
+ dirent = (struct nandfs_dir_entry *) (pos + off);
+ KASSERT((dirent->inode == tnode->nn_ino),
+ ("direntry mismatch"));
+
+ dirent->inode = fnode->nn_ino;
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+
+ return (0);
+}
+
+int
+nandfs_init_dir(struct vnode *dvp, uint64_t ino, uint64_t parent_ino)
+{
+
+ if (nandfs_add_dirent(dvp, parent_ino, "..", 2, DT_DIR) ||
+ nandfs_add_dirent(dvp, ino, ".", 1, DT_DIR)) {
+ nandfs_error("%s: cannot initialize dir ino:%jd(pino:%jd)\n",
+ __func__, ino, parent_ino);
+ return (-1);
+ }
+ return (0);
+}
diff --git a/sys/fs/nandfs/nandfs_fs.h b/sys/fs/nandfs/nandfs_fs.h
new file mode 100644
index 0000000..b72be40
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_fs.h
@@ -0,0 +1,565 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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.
+ *
+ * Original definitions written by Koji Sato <koji@osrg.net>
+ * and Ryusuke Konishi <ryusuke@osrg.net>
+ * From: NetBSD: nandfs_fs.h,v 1.1 2009/07/18 16:31:42 reinoud
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NANDFS_FS_H
+#define _NANDFS_FS_H
+
+#include <sys/uuid.h>
+
+#define MNINDIR(fsdev) ((fsdev)->nd_blocksize / sizeof(nandfs_daddr_t))
+
+/*
+ * Inode structure. There are a few dedicated inode numbers that are
+ * defined here first.
+ */
+#define NANDFS_WHT_INO 1 /* Whiteout ino */
+#define NANDFS_ROOT_INO 2 /* Root file inode */
+#define NANDFS_DAT_INO 3 /* DAT file */
+#define NANDFS_CPFILE_INO 4 /* checkpoint file */
+#define NANDFS_SUFILE_INO 5 /* segment usage file */
+#define NANDFS_IFILE_INO 6 /* ifile */
+#define NANDFS_GC_INO 7 /* Cleanerd node */
+#define NANDFS_ATIME_INO 8 /* Atime file (reserved) */
+#define NANDFS_XATTR_INO 9 /* Xattribute file (reserved) */
+#define NANDFS_SKETCH_INO 10 /* Sketch file (obsolete) */
+#define NANDFS_USER_INO 11 /* First user's file inode number */
+
+#define NANDFS_SYS_NODE(ino) \
+ (((ino) >= NANDFS_DAT_INO) && ((ino) <= NANDFS_GC_INO))
+
+#define NDADDR 12 /* Direct addresses in inode. */
+#define NIADDR 3 /* Indirect addresses in inode. */
+
+typedef int64_t nandfs_daddr_t;
+typedef int64_t nandfs_lbn_t;
+
+struct nandfs_inode {
+ uint64_t i_blocks; /* 0: size in device blocks */
+ uint64_t i_size; /* 8: size in bytes */
+ uint64_t i_ctime; /* 16: creation time in seconds */
+ uint64_t i_mtime; /* 24: modification time in seconds part*/
+ uint32_t i_ctime_nsec; /* 32: creation time nanoseconds part */
+ uint32_t i_mtime_nsec; /* 36: modification time in nanoseconds */
+ uint32_t i_uid; /* 40: user id */
+ uint32_t i_gid; /* 44: group id */
+ uint16_t i_mode; /* 48: file mode */
+ uint16_t i_links_count; /* 50: number of references to the inode*/
+ uint32_t i_flags; /* 52: NANDFS_*_FL flags */
+ nandfs_daddr_t i_special; /* 56: special */
+ nandfs_daddr_t i_db[NDADDR]; /* 64: Direct disk blocks. */
+ nandfs_daddr_t i_ib[NIADDR]; /* 160: Indirect disk blocks. */
+ uint64_t i_xattr; /* 184: reserved for extended attributes*/
+ uint32_t i_generation; /* 192: file generation for NFS */
+ uint32_t i_pad[15]; /* 196: make it 64 bits aligned */
+};
+
+#ifdef _KERNEL
+CTASSERT(sizeof(struct nandfs_inode) == 256);
+#endif
+
+/*
+ * Each checkpoint/snapshot has a super root.
+ *
+ * The super root holds the inodes of the three system files: `dat', `cp' and
+ * 'su' files. All other FS state is defined by those.
+ *
+ * It is CRC checksum'ed and time stamped.
+ */
+
+struct nandfs_super_root {
+ uint32_t sr_sum; /* check-sum */
+ uint16_t sr_bytes; /* byte count of this structure */
+ uint16_t sr_flags; /* reserved for flags */
+ uint64_t sr_nongc_ctime; /* timestamp, not for cleaner(?) */
+ struct nandfs_inode sr_dat; /* DAT, virt->phys translation inode */
+ struct nandfs_inode sr_cpfile; /* CP, checkpoints inode */
+ struct nandfs_inode sr_sufile; /* SU, segment usage inode */
+};
+
+#define NANDFS_SR_MDT_OFFSET(inode_size, i) \
+ ((uint32_t)&((struct nandfs_super_root *)0)->sr_dat + \
+ (inode_size) * (i))
+
+#define NANDFS_SR_DAT_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 0)
+#define NANDFS_SR_CPFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 1)
+#define NANDFS_SR_SUFILE_OFFSET(inode_size) NANDFS_SR_MDT_OFFSET(inode_size, 2)
+#define NANDFS_SR_BYTES (sizeof(struct nandfs_super_root))
+
+/*
+ * The superblock describes the basic structure and mount history. It also
+ * records some sizes of structures found on the disc for sanity checks.
+ *
+ * The superblock is stored at two places: NANDFS_SB_OFFSET_BYTES and
+ * NANDFS_SB2_OFFSET_BYTES.
+ */
+
+/* File system states stored on media in superblock's sbp->s_state */
+#define NANDFS_VALID_FS 0x0001 /* cleanly unmounted and all is ok */
+#define NANDFS_ERROR_FS 0x0002 /* there were errors detected, fsck */
+#define NANDFS_RESIZE_FS 0x0004 /* resize required, XXX unknown flag*/
+#define NANDFS_MOUNT_STATE_BITS "\20\1VALID_FS\2ERROR_FS\3RESIZE_FS"
+
+/*
+ * Brief description of control structures:
+ *
+ * NANDFS_NFSAREAS first blocks contain fsdata and some amount of super blocks.
+ * Simple round-robin policy is used in order to choose which block will
+ * contain new super block.
+ *
+ * Simple case with 2 blocks:
+ * 1: fsdata sblock1 [sblock3 [sblock5 ..]]
+ * 2: fsdata sblock2 [sblock4 [sblock6 ..]]
+ */
+struct nandfs_fsdata {
+ uint16_t f_magic;
+ uint16_t f_bytes;
+
+ uint32_t f_sum; /* checksum of fsdata */
+ uint32_t f_rev_level; /* major disk format revision */
+
+ uint64_t f_ctime; /* creation time (execution time
+ of newfs) */
+ /* Block size represented as: blocksize = 1 << (f_log_block_size + 10) */
+ uint32_t f_log_block_size;
+
+ uint16_t f_inode_size; /* size of an inode */
+ uint16_t f_dat_entry_size; /* size of a dat entry */
+ uint16_t f_checkpoint_size; /* size of a checkpoint */
+ uint16_t f_segment_usage_size; /* size of a segment usage */
+
+ uint16_t f_sbbytes; /* byte count of CRC calculation
+ for super blocks. s_reserved
+ is excluded! */
+
+ uint16_t f_errors; /* behaviour on detecting errors */
+
+ uint32_t f_erasesize;
+ uint64_t f_nsegments; /* number of segm. in filesystem */
+ nandfs_daddr_t f_first_data_block; /* 1st seg disk block number */
+ uint32_t f_blocks_per_segment; /* number of blocks per segment */
+ uint32_t f_r_segments_percentage; /* reserved segments percentage */
+
+ struct uuid f_uuid; /* 128-bit uuid for volume */
+ char f_volume_name[16]; /* volume name */
+ uint32_t f_pad[104];
+} __packed;
+
+#ifdef _KERNEL
+CTASSERT(sizeof(struct nandfs_fsdata) == 512);
+#endif
+
+struct nandfs_super_block {
+ uint16_t s_magic; /* magic value for identification */
+
+ uint32_t s_sum; /* check sum of super block */
+
+ uint64_t s_last_cno; /* last checkpoint number */
+ uint64_t s_last_pseg; /* addr part. segm. written last */
+ uint64_t s_last_seq; /* seq.number of seg written last */
+ uint64_t s_free_blocks_count; /* free blocks count */
+
+ uint64_t s_mtime; /* mount time */
+ uint64_t s_wtime; /* write time */
+ uint16_t s_state; /* file system state */
+
+ char s_last_mounted[64]; /* directory where last mounted */
+
+ uint32_t s_c_interval; /* commit interval of segment */
+ uint32_t s_c_block_max; /* threshold of data amount for
+ the segment construction */
+ uint32_t s_reserved[32]; /* padding to end of the block */
+} __packed;
+
+#ifdef _KERNEL
+CTASSERT(sizeof(struct nandfs_super_block) == 256);
+#endif
+
+#define NANDFS_FSDATA_MAGIC 0xf8da
+#define NANDFS_SUPER_MAGIC 0x8008
+
+#define NANDFS_NFSAREAS 4
+#define NANDFS_DATA_OFFSET_BYTES(esize) (NANDFS_NFSAREAS * (esize))
+
+#define NANDFS_SBLOCK_OFFSET_BYTES (sizeof(struct nandfs_fsdata))
+
+#define NANDFS_DEF_BLOCKSIZE 4096
+#define NANDFS_MIN_BLOCKSIZE 512
+
+#define NANDFS_DEF_ERASESIZE (2 << 16)
+
+#define NANDFS_MIN_SEGSIZE NANDFS_DEF_ERASESIZE
+
+#define NANDFS_CURRENT_REV 9 /* current major revision */
+
+#define NANDFS_FSDATA_CRC_BYTES offsetof(struct nandfs_fsdata, f_pad)
+/* Bytes count of super_block for CRC-calculation */
+#define NANDFS_SB_BYTES offsetof(struct nandfs_super_block, s_reserved)
+
+/* Maximal count of links to a file */
+#define NANDFS_LINK_MAX 32000
+
+/*
+ * Structure of a directory entry.
+ *
+ * Note that they can't span blocks; the rec_len fills out.
+ */
+
+#define NANDFS_NAME_LEN 255
+struct nandfs_dir_entry {
+ uint64_t inode; /* inode number */
+ uint16_t rec_len; /* directory entry length */
+ uint8_t name_len; /* name length */
+ uint8_t file_type;
+ char name[NANDFS_NAME_LEN]; /* file name */
+ char pad;
+};
+
+/*
+ * NANDFS_DIR_PAD defines the directory entries boundaries
+ *
+ * NOTE: It must be a multiple of 8
+ */
+#define NANDFS_DIR_PAD 8
+#define NANDFS_DIR_ROUND (NANDFS_DIR_PAD - 1)
+#define NANDFS_DIR_NAME_OFFSET (offsetof(struct nandfs_dir_entry, name))
+#define NANDFS_DIR_REC_LEN(name_len) \
+ (((name_len) + NANDFS_DIR_NAME_OFFSET + NANDFS_DIR_ROUND) \
+ & ~NANDFS_DIR_ROUND)
+#define NANDFS_DIR_NAME_LEN(name_len) \
+ (NANDFS_DIR_REC_LEN(name_len) - NANDFS_DIR_NAME_OFFSET)
+
+/*
+ * NiLFS/NANDFS devides the disc into fixed length segments. Each segment is
+ * filled with one or more partial segments of variable lengths.
+ *
+ * Each partial segment has a segment summary header followed by updates of
+ * files and optionally a super root.
+ */
+
+/*
+ * Virtual to physical block translation information. For data blocks it maps
+ * logical block number bi_blkoff to virtual block nr bi_vblocknr. For non
+ * datablocks it is the virtual block number assigned to an indirect block
+ * and has no bi_blkoff. The physical block number is the next
+ * available data block in the partial segment after all the binfo's.
+ */
+struct nandfs_binfo_v {
+ uint64_t bi_ino; /* file's inode */
+ uint64_t bi_vblocknr; /* assigned virtual block number */
+ uint64_t bi_blkoff; /* for file's logical block number */
+};
+
+/*
+ * DAT allocation. For data blocks just the logical block number that maps on
+ * the next available data block in the partial segment after the binfo's.
+ */
+struct nandfs_binfo_dat {
+ uint64_t bi_ino;
+ uint64_t bi_blkoff; /* DAT file's logical block number */
+ uint8_t bi_level; /* whether this is meta block */
+ uint8_t bi_pad[7];
+};
+
+#ifdef _KERNEL
+CTASSERT(sizeof(struct nandfs_binfo_v) == sizeof(struct nandfs_binfo_dat));
+#endif
+
+/* Convenience union for both types of binfo's */
+union nandfs_binfo {
+ struct nandfs_binfo_v bi_v;
+ struct nandfs_binfo_dat bi_dat;
+};
+
+/* Indirect buffers path */
+struct nandfs_indir {
+ nandfs_daddr_t in_lbn;
+ int in_off;
+};
+
+/* The (partial) segment summary */
+struct nandfs_segment_summary {
+ uint32_t ss_datasum; /* CRC of complete data block */
+ uint32_t ss_sumsum; /* CRC of segment summary only */
+ uint32_t ss_magic; /* magic to identify segment summary */
+ uint16_t ss_bytes; /* size of segment summary structure */
+ uint16_t ss_flags; /* NANDFS_SS_* flags */
+ uint64_t ss_seq; /* sequence number of this segm. sum */
+ uint64_t ss_create; /* creation timestamp in seconds */
+ uint64_t ss_next; /* blocknumber of next segment */
+ uint32_t ss_nblocks; /* number of blocks used by summary */
+ uint32_t ss_nbinfos; /* number of binfo structures */
+ uint32_t ss_sumbytes; /* total size of segment summary */
+ uint32_t ss_pad;
+ /* stream of binfo structures */
+};
+
+#define NANDFS_SEGSUM_MAGIC 0x8e680011 /* segment summary magic number */
+
+/* Segment summary flags */
+#define NANDFS_SS_LOGBGN 0x0001 /* begins a logical segment */
+#define NANDFS_SS_LOGEND 0x0002 /* ends a logical segment */
+#define NANDFS_SS_SR 0x0004 /* has super root */
+#define NANDFS_SS_SYNDT 0x0008 /* includes data only updates */
+#define NANDFS_SS_GC 0x0010 /* segment written for cleaner operation */
+#define NANDFS_SS_FLAG_BITS "\20\1LOGBGN\2LOGEND\3SR\4SYNDT\5GC"
+
+/* Segment summary constrains */
+#define NANDFS_SEG_MIN_BLOCKS 16 /* minimum number of blocks in a
+ full segment */
+#define NANDFS_PSEG_MIN_BLOCKS 2 /* minimum number of blocks in a
+ partial segment */
+#define NANDFS_MIN_NRSVSEGS 8 /* minimum number of reserved
+ segments */
+
+/*
+ * Structure of DAT/inode file.
+ *
+ * A DAT file is devided into groups. The maximum number of groups is the
+ * number of block group descriptors that fit into one block; this descriptor
+ * only gives the number of free entries in the associated group.
+ *
+ * Each group has a block sized bitmap indicating if an entry is taken or
+ * empty. Each bit stands for a DAT entry.
+ *
+ * The inode file has exactly the same format only the entries are inode
+ * entries.
+ */
+
+struct nandfs_block_group_desc {
+ uint32_t bg_nfrees; /* num. free entries in block group */
+};
+
+/* DAT entry in a super root's DAT file */
+struct nandfs_dat_entry {
+ uint64_t de_blocknr; /* block number */
+ uint64_t de_start; /* valid from checkpoint */
+ uint64_t de_end; /* valid till checkpoint */
+ uint64_t de_rsv; /* reserved for future use */
+};
+
+/*
+ * Structure of CP file.
+ *
+ * A snapshot is just a checkpoint only it's protected against removal by the
+ * cleaner. The snapshots are kept on a double linked list of checkpoints.
+ */
+struct nandfs_snapshot_list {
+ uint64_t ssl_next; /* checkpoint nr. forward */
+ uint64_t ssl_prev; /* checkpoint nr. back */
+};
+
+/* Checkpoint entry structure */
+struct nandfs_checkpoint {
+ uint32_t cp_flags; /* NANDFS_CHECKPOINT_* flags */
+ uint32_t cp_checkpoints_count; /* ZERO, not used anymore? */
+ struct nandfs_snapshot_list cp_snapshot_list; /* list of snapshots */
+ uint64_t cp_cno; /* checkpoint number */
+ uint64_t cp_create; /* creation timestamp */
+ uint64_t cp_nblk_inc; /* number of blocks incremented */
+ uint64_t cp_blocks_count; /* reserved (might be deleted) */
+ struct nandfs_inode cp_ifile_inode; /* inode file inode */
+};
+
+/* Checkpoint flags */
+#define NANDFS_CHECKPOINT_SNAPSHOT 1
+#define NANDFS_CHECKPOINT_INVALID 2
+#define NANDFS_CHECKPOINT_SKETCH 4
+#define NANDFS_CHECKPOINT_MINOR 8
+#define NANDFS_CHECKPOINT_BITS "\20\1SNAPSHOT\2INVALID\3SKETCH\4MINOR"
+
+/* Header of the checkpoint file */
+struct nandfs_cpfile_header {
+ uint64_t ch_ncheckpoints; /* number of checkpoints */
+ uint64_t ch_nsnapshots; /* number of snapshots */
+ struct nandfs_snapshot_list ch_snapshot_list; /* snapshot list */
+};
+
+#define NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET \
+ ((sizeof(struct nandfs_cpfile_header) + \
+ sizeof(struct nandfs_checkpoint) - 1) / \
+ sizeof(struct nandfs_checkpoint))
+
+
+#define NANDFS_NOSEGMENT 0xffffffff
+
+/*
+ * Structure of SU file.
+ *
+ * The segment usage file sums up how each of the segments are used. They are
+ * indexed by their segment number.
+ */
+
+/* Segment usage entry */
+struct nandfs_segment_usage {
+ uint64_t su_lastmod; /* last modified timestamp */
+ uint32_t su_nblocks; /* number of blocks in segment */
+ uint32_t su_flags; /* NANDFS_SEGMENT_USAGE_* flags */
+};
+
+/* Segment usage flag */
+#define NANDFS_SEGMENT_USAGE_ACTIVE 1
+#define NANDFS_SEGMENT_USAGE_DIRTY 2
+#define NANDFS_SEGMENT_USAGE_ERROR 4
+#define NANDFS_SEGMENT_USAGE_GC 8
+#define NANDFS_SEGMENT_USAGE_BITS "\20\1ACTIVE\2DIRTY\3ERROR"
+
+/* Header of the segment usage file */
+struct nandfs_sufile_header {
+ uint64_t sh_ncleansegs; /* number of segments marked clean */
+ uint64_t sh_ndirtysegs; /* number of segments marked dirty */
+ uint64_t sh_last_alloc; /* last allocated segment number */
+};
+
+#define NANDFS_SUFILE_FIRST_SEGMENT_USAGE_OFFSET \
+ ((sizeof(struct nandfs_sufile_header) + \
+ sizeof(struct nandfs_segment_usage) - 1) / \
+ sizeof(struct nandfs_segment_usage))
+
+struct nandfs_seg_stat {
+ uint64_t nss_nsegs;
+ uint64_t nss_ncleansegs;
+ uint64_t nss_ndirtysegs;
+ uint64_t nss_ctime;
+ uint64_t nss_nongc_ctime;
+ uint64_t nss_prot_seq;
+};
+
+enum {
+ NANDFS_CHECKPOINT,
+ NANDFS_SNAPSHOT
+};
+
+#define NANDFS_CPINFO_MAX 512
+
+struct nandfs_cpinfo {
+ uint32_t nci_flags;
+ uint32_t nci_pad;
+ uint64_t nci_cno;
+ uint64_t nci_create;
+ uint64_t nci_nblk_inc;
+ uint64_t nci_blocks_count;
+ uint64_t nci_next;
+};
+
+#define NANDFS_SEGMENTS_MAX 512
+
+struct nandfs_suinfo {
+ uint64_t nsi_num;
+ uint64_t nsi_lastmod;
+ uint32_t nsi_blocks;
+ uint32_t nsi_flags;
+};
+
+#define NANDFS_VINFO_MAX 512
+
+struct nandfs_vinfo {
+ uint64_t nvi_ino;
+ uint64_t nvi_vblocknr;
+ uint64_t nvi_start;
+ uint64_t nvi_end;
+ uint64_t nvi_blocknr;
+ int nvi_alive;
+};
+
+struct nandfs_cpmode {
+ uint64_t ncpm_cno;
+ uint32_t ncpm_mode;
+ uint32_t ncpm_pad;
+};
+
+struct nandfs_argv {
+ uint64_t nv_base;
+ uint32_t nv_nmembs;
+ uint16_t nv_size;
+ uint16_t nv_flags;
+ uint64_t nv_index;
+};
+
+struct nandfs_cpstat {
+ uint64_t ncp_cno;
+ uint64_t ncp_ncps;
+ uint64_t ncp_nss;
+};
+
+struct nandfs_period {
+ uint64_t p_start;
+ uint64_t p_end;
+};
+
+struct nandfs_vdesc {
+ uint64_t vd_ino;
+ uint64_t vd_cno;
+ uint64_t vd_vblocknr;
+ struct nandfs_period vd_period;
+ uint64_t vd_blocknr;
+ uint64_t vd_offset;
+ uint32_t vd_flags;
+ uint32_t vd_pad;
+};
+
+struct nandfs_bdesc {
+ uint64_t bd_ino;
+ uint64_t bd_oblocknr;
+ uint64_t bd_blocknr;
+ uint64_t bd_offset;
+ uint32_t bd_level;
+ uint32_t bd_alive;
+};
+
+#ifndef _KERNEL
+#ifndef MNAMELEN
+#define MNAMELEN 88
+#endif
+#endif
+
+struct nandfs_fsinfo {
+ struct nandfs_fsdata fs_fsdata;
+ struct nandfs_super_block fs_super;
+ char fs_dev[MNAMELEN];
+};
+
+#define NANDFS_MAX_MOUNTS 65535
+
+#define NANDFS_IOCTL_GET_SUSTAT _IOR('N', 100, struct nandfs_seg_stat)
+#define NANDFS_IOCTL_CHANGE_CPMODE _IOWR('N', 101, struct nandfs_cpmode)
+#define NANDFS_IOCTL_GET_CPINFO _IOWR('N', 102, struct nandfs_argv)
+#define NANDFS_IOCTL_DELETE_CP _IOWR('N', 103, uint64_t[2])
+#define NANDFS_IOCTL_GET_CPSTAT _IOR('N', 104, struct nandfs_cpstat)
+#define NANDFS_IOCTL_GET_SUINFO _IOWR('N', 105, struct nandfs_argv)
+#define NANDFS_IOCTL_GET_VINFO _IOWR('N', 106, struct nandfs_argv)
+#define NANDFS_IOCTL_GET_BDESCS _IOWR('N', 107, struct nandfs_argv)
+#define NANDFS_IOCTL_GET_FSINFO _IOR('N', 108, struct nandfs_fsinfo)
+#define NANDFS_IOCTL_MAKE_SNAP _IOWR('N', 109, uint64_t)
+#define NANDFS_IOCTL_DELETE_SNAP _IOWR('N', 110, uint64_t)
+#define NANDFS_IOCTL_SYNC _IOWR('N', 111, uint64_t)
+
+#endif /* _NANDFS_FS_H */
diff --git a/sys/fs/nandfs/nandfs_ifile.c b/sys/fs/nandfs/nandfs_ifile.c
new file mode 100644
index 0000000..7e4db87
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_ifile.c
@@ -0,0 +1,213 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+int
+nandfs_node_create(struct nandfsmount *nmp, struct nandfs_node **node,
+ uint16_t mode)
+{
+ struct nandfs_alloc_request req;
+ struct nandfs_device *nandfsdev;
+ struct nandfs_mdt *mdt;
+ struct nandfs_node *ifile;
+ struct nandfs_inode *inode;
+ struct vnode *vp;
+ uint32_t entry;
+ int error = 0;
+
+ nandfsdev = nmp->nm_nandfsdev;
+ mdt = &nandfsdev->nd_ifile_mdt;
+ ifile = nmp->nm_ifile_node;
+ vp = NTOV(ifile);
+
+ VOP_LOCK(vp, LK_EXCLUSIVE);
+ /* Allocate new inode in ifile */
+ req.entrynum = nandfsdev->nd_last_ino + 1;
+ error = nandfs_find_free_entry(mdt, ifile, &req);
+ if (error) {
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ error = nandfs_get_entry_block(mdt, ifile, &req, &entry, 1);
+ if (error) {
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ /* Inode initialization */
+ inode = ((struct nandfs_inode *) req.bp_entry->b_data) + entry;
+ nandfs_inode_init(inode, mode);
+
+ error = nandfs_alloc_entry(mdt, &req);
+ if (error) {
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ VOP_UNLOCK(vp, 0);
+
+ nandfsdev->nd_last_ino = req.entrynum;
+ error = nandfs_get_node(nmp, req.entrynum, node);
+ DPRINTF(IFILE, ("%s: node: %p ino: %#jx\n",
+ __func__, node, (uintmax_t)((*node)->nn_ino)));
+
+ return (error);
+}
+
+int
+nandfs_node_destroy(struct nandfs_node *node)
+{
+ struct nandfs_alloc_request req;
+ struct nandfsmount *nmp;
+ struct nandfs_mdt *mdt;
+ struct nandfs_node *ifile;
+ struct vnode *vp;
+ int error = 0;
+
+ nmp = node->nn_nmp;
+ req.entrynum = node->nn_ino;
+ mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
+ ifile = nmp->nm_ifile_node;
+ vp = NTOV(ifile);
+
+ DPRINTF(IFILE, ("%s: destroy node: %p ino: %#jx\n",
+ __func__, node, (uintmax_t)node->nn_ino));
+ VOP_LOCK(vp, LK_EXCLUSIVE);
+
+ error = nandfs_find_entry(mdt, ifile, &req);
+ if (error) {
+ nandfs_error("%s: finding entry error:%d node %p(%jx)",
+ __func__, error, node, node->nn_ino);
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ nandfs_inode_destroy(&node->nn_inode);
+
+ error = nandfs_free_entry(mdt, &req);
+ if (error) {
+ nandfs_error("%s: freing entry error:%d node %p(%jx)",
+ __func__, error, node, node->nn_ino);
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ VOP_UNLOCK(vp, 0);
+ DPRINTF(IFILE, ("%s: freed node %p ino %#jx\n",
+ __func__, node, (uintmax_t)node->nn_ino));
+ return (error);
+}
+
+int
+nandfs_node_update(struct nandfs_node *node)
+{
+ struct nandfs_alloc_request req;
+ struct nandfsmount *nmp;
+ struct nandfs_mdt *mdt;
+ struct nandfs_node *ifile;
+ struct nandfs_inode *inode;
+ uint32_t index;
+ int error = 0;
+
+ nmp = node->nn_nmp;
+ ifile = nmp->nm_ifile_node;
+ ASSERT_VOP_LOCKED(NTOV(ifile), __func__);
+
+ req.entrynum = node->nn_ino;
+ mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
+
+ DPRINTF(IFILE, ("%s: node:%p ino:%#jx\n",
+ __func__, &node->nn_inode, (uintmax_t)node->nn_ino));
+
+ error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0);
+ if (error) {
+ printf("nandfs_get_entry_block returned with ERROR=%d\n",
+ error);
+ return (error);
+ }
+
+ inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index;
+ memcpy(inode, &node->nn_inode, sizeof(*inode));
+ error = nandfs_dirty_buf(req.bp_entry, 0);
+
+ return (error);
+}
+
+int
+nandfs_get_node_entry(struct nandfsmount *nmp, struct nandfs_inode **inode,
+ uint64_t ino, struct buf **bp)
+{
+ struct nandfs_alloc_request req;
+ struct nandfs_mdt *mdt;
+ struct nandfs_node *ifile;
+ struct vnode *vp;
+ uint32_t index;
+ int error = 0;
+
+ req.entrynum = ino;
+ mdt = &nmp->nm_nandfsdev->nd_ifile_mdt;
+ ifile = nmp->nm_ifile_node;
+ vp = NTOV(ifile);
+
+ VOP_LOCK(vp, LK_EXCLUSIVE);
+ error = nandfs_get_entry_block(mdt, ifile, &req, &index, 0);
+ if (error) {
+ VOP_UNLOCK(vp, 0);
+ return (error);
+ }
+
+ *inode = ((struct nandfs_inode *) req.bp_entry->b_data) + index;
+ *bp = req.bp_entry;
+ VOP_UNLOCK(vp, 0);
+ return (0);
+}
+
diff --git a/sys/fs/nandfs/nandfs_mount.h b/sys/fs/nandfs/nandfs_mount.h
new file mode 100644
index 0000000..f733e22
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_mount.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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: NetBSD: nilfs_mount.h,v 1.1 2009/07/18 16:31:42 reinoud
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FS_NANDFS_NANDFS_MOUNT_H_
+#define _FS_NANDFS_NANDFS_MOUNT_H_
+
+/*
+ * Arguments to mount NANDFS filingsystem.
+ */
+
+struct nandfs_args {
+ char *fspec; /* mount specifier */
+ int64_t cpno; /* checkpoint number */
+};
+
+#endif /* !_FS_NANDFS_NANDFS_MOUNT_H_ */
+
diff --git a/sys/fs/nandfs/nandfs_segment.c b/sys/fs/nandfs/nandfs_segment.c
new file mode 100644
index 0000000..836bead
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_segment.c
@@ -0,0 +1,1329 @@
+/*-
+ * 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 "opt_ddb.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/libkern.h>
+
+#include <ddb/ddb.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+static int
+nandfs_new_segment(struct nandfs_device *fsdev)
+{
+ int error = 0;
+ uint64_t new;
+
+ error = nandfs_alloc_segment(fsdev, &new);
+ if (!error) {
+ fsdev->nd_seg_num = fsdev->nd_next_seg_num;
+ fsdev->nd_next_seg_num = new;
+ }
+ DPRINTF(SYNC, ("%s: new segment %jx next %jx error %d\n",
+ __func__, (uintmax_t)fsdev->nd_seg_num, (uintmax_t)new, error));
+ if (error)
+ nandfs_error("%s: cannot create segment error %d\n",
+ __func__, error);
+
+ return (error);
+}
+
+static int
+create_segment(struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_segment *seg;
+ struct nandfs_device *fsdev;
+ struct nandfs_segment *prev;
+ struct buf *bp;
+ uint64_t start_block, curr;
+ uint32_t blks_per_seg, nblocks;
+ int error;
+
+ fsdev = seginfo->fsdev;
+ prev = seginfo->curseg;
+ blks_per_seg = fsdev->nd_fsdata.f_blocks_per_segment;
+ nblocks = fsdev->nd_last_segsum.ss_nblocks;
+
+ if (!prev) {
+ vfs_timestamp(&fsdev->nd_ts);
+ /* Touch current segment */
+ error = nandfs_touch_segment(fsdev, fsdev->nd_seg_num);
+ if (error) {
+ nandfs_error("%s: cannot preallocate segment %jx\n",
+ __func__, fsdev->nd_seg_num);
+ return (error);
+ }
+ error = nandfs_touch_segment(fsdev, 0);
+ if (error) {
+ nandfs_error("%s: cannot dirty block with segment 0\n",
+ __func__);
+ return (error);
+ }
+ start_block = fsdev->nd_last_pseg + (uint64_t)nblocks;
+ /*
+ * XXX Hack
+ */
+ if (blks_per_seg - (start_block % blks_per_seg) - 1 == 0)
+ start_block++;
+ curr = nandfs_get_segnum_of_block(fsdev, start_block);
+ /* Allocate new segment if last one is full */
+ if (fsdev->nd_seg_num != curr) {
+ error = nandfs_new_segment(fsdev);
+ if (error) {
+ nandfs_error("%s: cannot create new segment\n",
+ __func__);
+ return (error);
+ }
+ /*
+ * XXX Hack
+ */
+ nandfs_get_segment_range(fsdev, fsdev->nd_seg_num, &start_block, NULL);
+ }
+ } else {
+ nandfs_get_segment_range(fsdev, fsdev->nd_next_seg_num,
+ &start_block, NULL);
+
+ /* Touch current segment and allocate and touch new one */
+ error = nandfs_new_segment(fsdev);
+ if (error) {
+ nandfs_error("%s: cannot create next segment\n",
+ __func__);
+ return (error);
+ }
+
+ /* Reiterate in case new buf is dirty */
+ seginfo->reiterate = 1;
+ }
+
+ /* Allocate and initialize nandfs_segment structure */
+ seg = malloc(sizeof(*seg), M_DEVBUF, M_WAITOK|M_ZERO);
+ TAILQ_INIT(&seg->segsum);
+ TAILQ_INIT(&seg->data);
+ seg->fsdev = fsdev;
+ seg->start_block = start_block;
+ seg->num_blocks = blks_per_seg - (start_block % blks_per_seg) - 1;
+ seg->seg_num = fsdev->nd_seg_num;
+ seg->seg_next = fsdev->nd_next_seg_num;
+ seg->segsum_blocks = 1;
+ seg->bytes_left = fsdev->nd_blocksize -
+ sizeof(struct nandfs_segment_summary);
+ seg->segsum_bytes = sizeof(struct nandfs_segment_summary);
+
+ /* Allocate buffer for segment summary */
+ bp = getblk(fsdev->nd_devvp, nandfs_block_to_dblock(fsdev,
+ seg->start_block), fsdev->nd_blocksize, 0, 0, 0);
+ bzero(bp->b_data, seginfo->fsdev->nd_blocksize);
+ bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj;
+ bp->b_flags |= B_MANAGED;
+
+ /* Add buffer to segment */
+ TAILQ_INSERT_TAIL(&seg->segsum, bp, b_cluster.cluster_entry);
+ seg->current_off = bp->b_data + sizeof(struct nandfs_segment_summary);
+
+ DPRINTF(SYNC, ("%s: seg %p : initial settings: start %#jx size :%#x\n",
+ __func__, seg, (uintmax_t)seg->start_block, seg->num_blocks));
+ DPRINTF(SYNC, ("%s: seg->seg_num %#jx cno %#jx next %#jx\n", __func__,
+ (uintmax_t)seg->seg_num, (uintmax_t)(fsdev->nd_last_cno + 1),
+ (uintmax_t)seg->seg_next));
+
+ if (!prev)
+ LIST_INSERT_HEAD(&seginfo->seg_list, seg, seg_link);
+ else
+ LIST_INSERT_AFTER(prev, seg, seg_link);
+
+ seginfo->curseg = seg;
+
+ return (0);
+}
+
+static int
+delete_segment(struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_segment *seg, *tseg;
+ struct buf *bp, *tbp;
+
+ LIST_FOREACH_SAFE(seg, &seginfo->seg_list, seg_link, tseg) {
+ TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry,
+ tbp) {
+ TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry);
+ bp->b_flags &= ~B_MANAGED;
+ brelse(bp);
+ };
+
+ LIST_REMOVE(seg, seg_link);
+ free(seg, M_DEVBUF);
+ }
+
+ return (0);
+}
+
+static int
+create_seginfo(struct nandfs_device *fsdev, struct nandfs_seginfo **seginfo)
+{
+ struct nandfs_seginfo *info;
+
+ info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK);
+
+ LIST_INIT(&info->seg_list);
+ info->fsdev = fsdev;
+ info->curseg = NULL;
+ info->blocks = 0;
+ *seginfo = info;
+ fsdev->nd_seginfo = info;
+ return (0);
+}
+
+static int
+delete_seginfo(struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_device *nffsdev;
+
+ nffsdev = seginfo->fsdev;
+ delete_segment(seginfo);
+ nffsdev->nd_seginfo = NULL;
+ free(seginfo, M_DEVBUF);
+
+ return (0);
+}
+
+static int
+nandfs_create_superroot_block(struct nandfs_seginfo *seginfo,
+ struct buf **newbp)
+{
+ struct buf *bp;
+ int error;
+
+ bp = nandfs_geteblk(seginfo->fsdev->nd_blocksize, GB_NOWAIT_BD);
+
+ bzero(bp->b_data, seginfo->fsdev->nd_blocksize);
+ bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj;
+ bp->b_flags |= B_MANAGED;
+
+ if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) {
+ error = create_segment(seginfo);
+ if (error) {
+ brelse(bp);
+ nandfs_error("%s: no segment for superroot\n",
+ __func__);
+ return (error);
+ }
+ }
+
+ TAILQ_INSERT_TAIL(&seginfo->curseg->data, bp, b_cluster.cluster_entry);
+
+ seginfo->curseg->nblocks++;
+ seginfo->curseg->num_blocks--;
+ seginfo->blocks++;
+
+ *newbp = bp;
+ return (0);
+}
+
+static int
+nandfs_add_superroot(struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_super_root *sr;
+ struct buf *bp = NULL;
+ uint64_t crc_skip;
+ uint32_t crc_calc;
+ int error;
+
+ fsdev = seginfo->fsdev;
+
+ error = nandfs_create_superroot_block(seginfo, &bp);
+ if (error) {
+ nandfs_error("%s: cannot add superroot\n", __func__);
+ return (error);
+ }
+
+ sr = (struct nandfs_super_root *)bp->b_data;
+ /* Save superroot CRC */
+ sr->sr_bytes = NANDFS_SR_BYTES;
+ sr->sr_flags = 0;
+ sr->sr_nongc_ctime = 0;
+
+ memcpy(&sr->sr_dat, &fsdev->nd_dat_node->nn_inode,
+ sizeof(struct nandfs_inode));
+ memcpy(&sr->sr_cpfile, &fsdev->nd_cp_node->nn_inode,
+ sizeof(struct nandfs_inode));
+ memcpy(&sr->sr_sufile, &fsdev->nd_su_node->nn_inode,
+ sizeof(struct nandfs_inode));
+
+ crc_skip = sizeof(sr->sr_sum);
+ crc_calc = crc32((uint8_t *)sr + crc_skip, NANDFS_SR_BYTES - crc_skip);
+
+ sr->sr_sum = crc_calc;
+
+ bp->b_flags |= B_MANAGED;
+ bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj;
+
+ bp->b_flags &= ~B_INVAL;
+ nandfs_dirty_bufs_increment(fsdev);
+ DPRINTF(SYNC, ("%s: bp:%p\n", __func__, bp));
+
+ return (0);
+}
+
+static int
+nandfs_add_segsum_block(struct nandfs_seginfo *seginfo, struct buf **newbp)
+{
+ struct nandfs_device *fsdev;
+ nandfs_daddr_t blk;
+ struct buf *bp;
+ int error;
+
+ if (!(seginfo->curseg) || seginfo->curseg->num_blocks <= 1) {
+ error = create_segment(seginfo);
+ if (error) {
+ nandfs_error("%s: error:%d when creating segment\n",
+ __func__, error);
+ return (error);
+ }
+ *newbp = TAILQ_FIRST(&seginfo->curseg->segsum);
+ return (0);
+ }
+
+ fsdev = seginfo->fsdev;
+ blk = nandfs_block_to_dblock(fsdev, seginfo->curseg->start_block +
+ seginfo->curseg->segsum_blocks);
+
+ bp = getblk(fsdev->nd_devvp, blk, fsdev->nd_blocksize, 0, 0, 0);
+
+ bzero(bp->b_data, seginfo->fsdev->nd_blocksize);
+ bp->b_bufobj = &seginfo->fsdev->nd_devvp->v_bufobj;
+ bp->b_flags |= B_MANAGED;
+
+ TAILQ_INSERT_TAIL(&seginfo->curseg->segsum, bp,
+ b_cluster.cluster_entry);
+ seginfo->curseg->num_blocks--;
+
+ seginfo->curseg->segsum_blocks++;
+ seginfo->curseg->bytes_left = seginfo->fsdev->nd_blocksize;
+ seginfo->curseg->current_off = bp->b_data;
+ seginfo->blocks++;
+
+ *newbp = bp;
+
+ DPRINTF(SYNC, ("%s: bp %p\n", __func__, bp));
+
+ return (0);
+}
+
+static int
+nandfs_add_blocks(struct nandfs_seginfo *seginfo, struct nandfs_node *node,
+ struct buf *bp)
+{
+ union nandfs_binfo *binfo;
+ struct buf *seg_bp;
+ int error;
+
+ if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) {
+ error = create_segment(seginfo);
+ if (error) {
+ nandfs_error("%s: error:%d when creating segment\n",
+ __func__, error);
+ return (error);
+ }
+ }
+
+ if (seginfo->curseg->bytes_left < sizeof(union nandfs_binfo)) {
+ error = nandfs_add_segsum_block(seginfo, &seg_bp);
+ if (error) {
+ nandfs_error("%s: error:%d when adding segsum\n",
+ __func__, error);
+ return (error);
+ }
+ }
+ binfo = (union nandfs_binfo *)seginfo->curseg->current_off;
+
+ if (node->nn_ino != NANDFS_DAT_INO) {
+ binfo->bi_v.bi_blkoff = bp->b_lblkno;
+ binfo->bi_v.bi_ino = node->nn_ino;
+ } else {
+ binfo->bi_dat.bi_blkoff = bp->b_lblkno;
+ binfo->bi_dat.bi_ino = node->nn_ino;
+ if (NANDFS_IS_INDIRECT(bp))
+ binfo->bi_dat.bi_level = 1;
+ else
+ binfo->bi_dat.bi_level = 0;
+ }
+ binfo++;
+
+ seginfo->curseg->bytes_left -= sizeof(union nandfs_binfo);
+ seginfo->curseg->segsum_bytes += sizeof(union nandfs_binfo);
+ seginfo->curseg->current_off = (char *)binfo;
+
+ TAILQ_INSERT_TAIL(&seginfo->curseg->data, bp, b_cluster.cluster_entry);
+
+ seginfo->curseg->nbinfos++;
+ seginfo->curseg->nblocks++;
+ seginfo->curseg->num_blocks--;
+ seginfo->blocks++;
+
+ DPRINTF(SYNC, ("%s: bp (%p) number %x (left %x)\n",
+ __func__, bp, seginfo->curseg->nblocks,
+ seginfo->curseg->num_blocks));
+ return (0);
+}
+
+static int
+nandfs_iterate_dirty_buf(struct vnode *vp, struct nandfs_seginfo *seginfo,
+ uint8_t hold)
+{
+ struct buf *bp, *tbd;
+ struct bufobj *bo;
+ struct nandfs_node *node;
+ int error;
+
+ node = VTON(vp);
+ bo = &vp->v_bufobj;
+
+ ASSERT_VOP_ELOCKED(vp, __func__);
+
+ /* Iterate dirty data bufs */
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, tbd) {
+ DPRINTF(SYNC, ("%s: vp (%p): bp (%p) with lblkno %jx ino %jx "
+ "add buf\n", __func__, vp, bp, bp->b_lblkno, node->nn_ino));
+
+ if (!(NANDFS_ISGATHERED(bp))) {
+ error = nandfs_bmap_update_dat(node,
+ nandfs_vblk_get(bp), bp);
+ if (error)
+ return (error);
+ NANDFS_GATHER(bp);
+ nandfs_add_blocks(seginfo, node, bp);
+ }
+ }
+
+ return (0);
+}
+
+static int
+nandfs_iterate_system_vnode(struct nandfs_node *node,
+ struct nandfs_seginfo *seginfo)
+{
+ struct vnode *vp;
+ int nblocks;
+ uint8_t hold = 0;
+
+ if (node->nn_ino != NANDFS_IFILE_INO)
+ hold = 1;
+
+ vp = NTOV(node);
+
+ nblocks = vp->v_bufobj.bo_dirty.bv_cnt;
+ DPRINTF(SYNC, ("%s: vp (%p): nblocks %x ino %jx\n",
+ __func__, vp, nblocks, node->nn_ino));
+
+ if (nblocks)
+ nandfs_iterate_dirty_buf(vp, seginfo, hold);
+
+ return (0);
+}
+
+static int
+nandfs_iterate_dirty_vnodes(struct mount *mp, struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_node *nandfs_node;
+ struct vnode *vp, *mvp;
+ struct thread *td;
+ int error, lockreq, update;
+
+ td = curthread;
+ lockreq = LK_EXCLUSIVE | LK_INTERLOCK | LK_RETRY;
+
+ MNT_ILOCK(mp);
+
+ MNT_VNODE_FOREACH(vp, mp, mvp) {
+ update = 0;
+
+ if (mp->mnt_syncer == vp)
+ continue;
+ if (VOP_ISLOCKED(vp))
+ continue;
+
+ VI_LOCK(vp);
+ MNT_IUNLOCK(mp);
+ if (vp->v_iflag & VI_DOOMED) {
+ VI_UNLOCK(vp);
+ MNT_ILOCK(mp);
+ continue;
+ }
+
+ if ((error = vget(vp, lockreq, td)) != 0) {
+ MNT_ILOCK(mp);
+ continue;
+ }
+
+ if (vp->v_iflag & VI_DOOMED) {
+ vput(vp);
+ MNT_ILOCK(mp);
+ continue;
+ }
+
+ nandfs_node = VTON(vp);
+ if (nandfs_node->nn_flags & IN_MODIFIED) {
+ nandfs_node->nn_flags &= ~(IN_MODIFIED);
+ update = 1;
+ }
+
+ if (vp->v_bufobj.bo_dirty.bv_cnt) {
+ error = nandfs_iterate_dirty_buf(vp, seginfo, 0);
+ if (error) {
+ nandfs_error("%s: cannot iterate vnode:%p "
+ "err:%d\n", __func__, vp, error);
+ vput(vp);
+ return (error);
+ }
+ update = 1;
+ } else
+ vput(vp);
+
+ if (update)
+ nandfs_node_update(nandfs_node);
+
+ MNT_ILOCK(mp);
+ }
+
+ MNT_IUNLOCK(mp);
+
+ return (0);
+}
+
+static int
+nandfs_update_phys_block(struct nandfs_device *fsdev, struct buf *bp,
+ uint64_t phys_blknr, union nandfs_binfo *binfo)
+{
+ struct nandfs_node *node, *dat;
+ struct vnode *vp;
+ uint64_t new_blknr;
+ int error;
+
+ vp = bp->b_vp;
+ node = VTON(vp);
+ new_blknr = nandfs_vblk_get(bp);
+ dat = fsdev->nd_dat_node;
+
+ DPRINTF(BMAP, ("%s: ino %#jx lblk %#jx: vblk %#jx -> %#jx\n",
+ __func__, (uintmax_t)node->nn_ino, (uintmax_t)bp->b_lblkno,
+ (uintmax_t)new_blknr, (uintmax_t)phys_blknr));
+
+ if (node->nn_ino != NANDFS_DAT_INO) {
+ KASSERT((new_blknr != 0), ("vblk for bp %p is 0", bp));
+
+ nandfs_vblock_assign(fsdev, new_blknr, phys_blknr);
+ binfo->bi_v.bi_vblocknr = new_blknr;
+ binfo->bi_v.bi_blkoff = bp->b_lblkno;
+ binfo->bi_v.bi_ino = node->nn_ino;
+ } else {
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ error = nandfs_bmap_update_block(node, bp, phys_blknr);
+ if (error) {
+ nandfs_error("%s: error updating block:%jx for bp:%p\n",
+ __func__, (uintmax_t)phys_blknr, bp);
+ VOP_UNLOCK(NTOV(dat), 0);
+ return (error);
+ }
+ VOP_UNLOCK(NTOV(dat), 0);
+ binfo->bi_dat.bi_blkoff = bp->b_lblkno;
+ binfo->bi_dat.bi_ino = node->nn_ino;
+ if (NANDFS_IS_INDIRECT(bp))
+ binfo->bi_dat.bi_level = 1;
+ else
+ binfo->bi_dat.bi_level = 0;
+ }
+
+ return (0);
+}
+
+#define NBINFO(off) ((off) + sizeof(union nandfs_binfo))
+static int
+nandfs_segment_assign_pblk(struct nandfs_segment *nfsseg)
+{
+ struct nandfs_device *fsdev;
+ union nandfs_binfo *binfo;
+ struct buf *bp, *seg_bp;
+ uint64_t blocknr;
+ uint32_t curr_off, blocksize;
+ int error;
+
+ fsdev = nfsseg->fsdev;
+ blocksize = fsdev->nd_blocksize;
+
+ blocknr = nfsseg->start_block + nfsseg->segsum_blocks;
+ seg_bp = TAILQ_FIRST(&nfsseg->segsum);
+ DPRINTF(SYNC, ("%s: seg:%p segsum bp:%p data:%p\n",
+ __func__, nfsseg, seg_bp, seg_bp->b_data));
+
+ binfo = (union nandfs_binfo *)(seg_bp->b_data +
+ sizeof(struct nandfs_segment_summary));
+ curr_off = sizeof(struct nandfs_segment_summary);
+
+ TAILQ_FOREACH(bp, &nfsseg->data, b_cluster.cluster_entry) {
+ KASSERT((bp->b_vp), ("bp %p has not vp", bp));
+
+ DPRINTF(BMAP, ("\n\n%s: assign buf %p for ino %#jx next %p\n",
+ __func__, bp, (uintmax_t)VTON(bp->b_vp)->nn_ino,
+ TAILQ_NEXT(bp, b_cluster.cluster_entry)));
+
+ if (NBINFO(curr_off) > blocksize) {
+ seg_bp = TAILQ_NEXT(seg_bp, b_cluster.cluster_entry);
+ binfo = (union nandfs_binfo *)seg_bp->b_data;
+ curr_off = 0;
+ DPRINTF(SYNC, ("%s: next segsum %p data %p\n",
+ __func__, seg_bp, seg_bp->b_data));
+ }
+
+ error = nandfs_update_phys_block(fsdev, bp, blocknr, binfo);
+ if (error) {
+ nandfs_error("%s: err:%d when updatinng phys block:%jx"
+ " for bp:%p and binfo:%p\n", __func__, error,
+ (uintmax_t)blocknr, bp, binfo);
+ return (error);
+ }
+ binfo++;
+ curr_off = NBINFO(curr_off);
+
+ blocknr++;
+ }
+
+ return (0);
+}
+
+static int
+nandfs_seginfo_assign_pblk(struct nandfs_seginfo *seginfo)
+{
+ struct nandfs_segment *nfsseg;
+ int error = 0;
+
+ LIST_FOREACH(nfsseg, &seginfo->seg_list, seg_link) {
+ error = nandfs_segment_assign_pblk(nfsseg);
+ if (error)
+ break;
+ }
+
+ return (error);
+}
+
+static struct nandfs_segment_summary *
+nandfs_fill_segsum(struct nandfs_segment *seg, int has_sr)
+{
+ struct nandfs_segment_summary *ss;
+ struct nandfs_device *fsdev;
+ struct buf *bp;
+ uint32_t rest, segsum_size, blocksize, crc_calc;
+ uint16_t flags;
+ uint8_t *crc_area, crc_skip;
+
+ DPRINTF(SYNC, ("%s: seg %#jx nblocks %#x sumbytes %#x\n",
+ __func__, (uintmax_t) seg->seg_num,
+ seg->nblocks + seg->segsum_blocks,
+ seg->segsum_bytes));
+
+ fsdev = seg->fsdev;
+
+ flags = NANDFS_SS_LOGBGN | NANDFS_SS_LOGEND;
+ if (has_sr)
+ flags |= NANDFS_SS_SR;
+
+ bp = TAILQ_FIRST(&seg->segsum);
+ ss = (struct nandfs_segment_summary *) bp->b_data;
+ ss->ss_magic = NANDFS_SEGSUM_MAGIC;
+ ss->ss_bytes = sizeof(struct nandfs_segment_summary);
+ ss->ss_flags = flags;
+ ss->ss_seq = ++(fsdev->nd_seg_sequence);
+ ss->ss_create = fsdev->nd_ts.tv_sec;
+ nandfs_get_segment_range(fsdev, seg->seg_next, &ss->ss_next, NULL);
+ ss->ss_nblocks = seg->nblocks + seg->segsum_blocks;
+ ss->ss_nbinfos = seg->nbinfos;
+ ss->ss_sumbytes = seg->segsum_bytes;
+
+ crc_skip = sizeof(ss->ss_datasum) + sizeof(ss->ss_sumsum);
+ blocksize = seg->fsdev->nd_blocksize;
+
+ segsum_size = seg->segsum_bytes - crc_skip;
+ rest = min(seg->segsum_bytes, blocksize) - crc_skip;
+ crc_area = (uint8_t *)ss + crc_skip;
+ crc_calc = ~0U;
+ while (segsum_size > 0) {
+ crc_calc = crc32_raw(crc_area, rest, crc_calc);
+ segsum_size -= rest;
+ if (!segsum_size)
+ break;
+ bp = TAILQ_NEXT(bp, b_cluster.cluster_entry);
+ crc_area = (uint8_t *)bp->b_data;
+ rest = segsum_size <= blocksize ? segsum_size : blocksize;
+ }
+ ss->ss_sumsum = crc_calc ^ ~0U;
+
+ return (ss);
+
+}
+
+static int
+nandfs_save_buf(struct buf *bp, uint64_t blocknr, struct nandfs_device *fsdev)
+{
+ struct bufobj *bo;
+ int error;
+
+ bo = &fsdev->nd_devvp->v_bufobj;
+
+ bp->b_blkno = nandfs_block_to_dblock(fsdev, blocknr);
+ bp->b_iooffset = dbtob(bp->b_blkno);
+
+ KASSERT(bp->b_bufobj != NULL, ("no bufobj for %p", bp));
+ if (bp->b_bufobj != bo) {
+ BO_LOCK(bp->b_bufobj);
+ BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK,
+ BO_MTX(bp->b_bufobj));
+ KASSERT(BUF_ISLOCKED(bp), ("Problem with locking buffer"));
+ }
+
+ DPRINTF(SYNC, ("%s: buf: %p offset %#jx blk %#jx size %#x\n",
+ __func__, bp, (uintmax_t)bp->b_offset, (uintmax_t)blocknr,
+ fsdev->nd_blocksize));
+
+ NANDFS_UNGATHER(bp);
+ nandfs_buf_clear(bp, 0xffffffff);
+ bp->b_flags &= ~(B_ASYNC|B_INVAL|B_MANAGED);
+ error = bwrite(bp);
+ if (error) {
+ nandfs_error("%s: error:%d when writing buffer:%p\n",
+ __func__, error, bp);
+ return (error);
+ }
+ return (error);
+}
+
+static void
+nandfs_clean_buf(struct nandfs_device *fsdev, struct buf *bp)
+{
+
+ DPRINTF(SYNC, ("%s: buf: %p\n", __func__, bp));
+
+ NANDFS_UNGATHER(bp);
+ nandfs_buf_clear(bp, 0xffffffff);
+ bp->b_flags &= ~(B_ASYNC|B_INVAL|B_MANAGED);
+ nandfs_undirty_buf_fsdev(fsdev, bp);
+}
+
+static void
+nandfs_clean_segblocks(struct nandfs_segment *seg, uint8_t unlock)
+{
+ struct nandfs_device *fsdev = seg->fsdev;
+ struct nandfs_segment *next_seg;
+ struct buf *bp, *tbp, *next_bp;
+ struct vnode *vp, *next_vp;
+
+ VOP_LOCK(fsdev->nd_devvp, LK_EXCLUSIVE);
+ TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry, tbp) {
+ TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry);
+ nandfs_clean_buf(fsdev, bp);
+ };
+
+ TAILQ_FOREACH_SAFE(bp, &seg->data, b_cluster.cluster_entry, tbp) {
+ TAILQ_REMOVE(&seg->data, bp, b_cluster.cluster_entry);
+
+ /*
+ * If bp is not super-root and vnode is not currently
+ * locked lock it.
+ */
+ vp = bp->b_vp;
+ next_vp = NULL;
+ next_bp = TAILQ_NEXT(bp, b_cluster.cluster_entry);
+ if (!next_bp) {
+ next_seg = LIST_NEXT(seg, seg_link);
+ if (next_seg)
+ next_bp = TAILQ_FIRST(&next_seg->data);
+ }
+
+ if (next_bp)
+ next_vp = next_bp->b_vp;
+
+ nandfs_clean_buf(fsdev, bp);
+
+ if (unlock && vp != NULL && next_vp != vp &&
+ !NANDFS_SYS_NODE(VTON(vp)->nn_ino))
+ vput(vp);
+
+ nandfs_dirty_bufs_decrement(fsdev);
+ }
+
+ VOP_UNLOCK(fsdev->nd_devvp, 0);
+}
+
+static int
+nandfs_save_segblocks(struct nandfs_segment *seg, uint8_t unlock)
+{
+ struct nandfs_device *fsdev = seg->fsdev;
+ struct nandfs_segment *next_seg;
+ struct buf *bp, *tbp, *next_bp;
+ struct vnode *vp, *next_vp;
+ uint64_t blocknr;
+ uint32_t i = 0;
+ int error = 0;
+
+ VOP_LOCK(fsdev->nd_devvp, LK_EXCLUSIVE);
+ TAILQ_FOREACH_SAFE(bp, &seg->segsum, b_cluster.cluster_entry, tbp) {
+ TAILQ_REMOVE(&seg->segsum, bp, b_cluster.cluster_entry);
+ blocknr = seg->start_block + i;
+ error = nandfs_save_buf(bp, blocknr, fsdev);
+ if (error) {
+ nandfs_error("%s: error saving buf: %p blocknr:%jx\n",
+ __func__, bp, (uintmax_t)blocknr);
+ goto out;
+ }
+ i++;
+ };
+
+ i = 0;
+ TAILQ_FOREACH_SAFE(bp, &seg->data, b_cluster.cluster_entry, tbp) {
+ TAILQ_REMOVE(&seg->data, bp, b_cluster.cluster_entry);
+
+ blocknr = seg->start_block + seg->segsum_blocks + i;
+ /*
+ * If bp is not super-root and vnode is not currently
+ * locked lock it.
+ */
+ vp = bp->b_vp;
+ next_vp = NULL;
+ next_bp = TAILQ_NEXT(bp, b_cluster.cluster_entry);
+ if (!next_bp) {
+ next_seg = LIST_NEXT(seg, seg_link);
+ if (next_seg)
+ next_bp = TAILQ_FIRST(&next_seg->data);
+ }
+
+ if (next_bp)
+ next_vp = next_bp->b_vp;
+
+ error = nandfs_save_buf(bp, blocknr, fsdev);
+ if (error) {
+ nandfs_error("%s: error saving buf: %p blknr: %jx\n",
+ __func__, bp, (uintmax_t)blocknr);
+ if (unlock && vp != NULL && next_vp != vp &&
+ !NANDFS_SYS_NODE(VTON(vp)->nn_ino))
+ vput(vp);
+ goto out;
+ }
+
+ if (unlock && vp != NULL && next_vp != vp &&
+ !NANDFS_SYS_NODE(VTON(vp)->nn_ino))
+ vput(vp);
+
+ i++;
+ nandfs_dirty_bufs_decrement(fsdev);
+ }
+out:
+ if (error) {
+ nandfs_clean_segblocks(seg, unlock);
+ VOP_UNLOCK(fsdev->nd_devvp, 0);
+ return (error);
+ }
+
+ VOP_UNLOCK(fsdev->nd_devvp, 0);
+ return (error);
+}
+
+
+static void
+clean_seginfo(struct nandfs_seginfo *seginfo, uint8_t unlock)
+{
+ struct nandfs_segment *seg;
+
+ DPRINTF(SYNC, ("%s: seginfo %p\n", __func__, seginfo));
+
+ LIST_FOREACH(seg, &seginfo->seg_list, seg_link) {
+ nandfs_clean_segblocks(seg, unlock);
+ }
+}
+
+static int
+save_seginfo(struct nandfs_seginfo *seginfo, uint8_t unlock)
+{
+ struct nandfs_segment *seg;
+ struct nandfs_device *fsdev;
+ struct nandfs_segment_summary *ss;
+ int error = 0;
+
+ fsdev = seginfo->fsdev;
+
+ DPRINTF(SYNC, ("%s: seginfo %p\n", __func__, seginfo));
+
+ LIST_FOREACH(seg, &seginfo->seg_list, seg_link) {
+ if (LIST_NEXT(seg, seg_link)) {
+ nandfs_fill_segsum(seg, 0);
+ error = nandfs_save_segblocks(seg, unlock);
+ if (error) {
+ nandfs_error("%s: error:%d saving seg:%p\n",
+ __func__, error, seg);
+ goto out;
+ }
+ } else {
+ ss = nandfs_fill_segsum(seg, 1);
+ fsdev->nd_last_segsum = *ss;
+ error = nandfs_save_segblocks(seg, unlock);
+ if (error) {
+ nandfs_error("%s: error:%d saving seg:%p\n",
+ __func__, error, seg);
+ goto out;
+ }
+ fsdev->nd_last_cno++;
+ fsdev->nd_last_pseg = seg->start_block;
+ }
+ }
+out:
+ if (error)
+ clean_seginfo(seginfo, unlock);
+ return (error);
+}
+
+static void
+nandfs_invalidate_bufs(struct nandfs_device *fsdev, uint64_t segno)
+{
+ uint64_t start, end;
+ struct buf *bp, *tbd;
+ struct bufobj *bo;
+
+ nandfs_get_segment_range(fsdev, segno, &start, &end);
+
+ bo = &NTOV(fsdev->nd_gc_node)->v_bufobj;
+
+ BO_LOCK(bo);
+restart_locked_gc:
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, tbd) {
+ if (!(bp->b_lblkno >= start && bp->b_lblkno <= end))
+ continue;
+
+ if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL))
+ goto restart_locked_gc;
+
+ bremfree(bp);
+ bp->b_flags |= (B_INVAL | B_RELBUF);
+ bp->b_flags &= ~(B_ASYNC | B_MANAGED);
+ BO_UNLOCK(bo);
+ brelse(bp);
+ BO_LOCK(bo);
+ }
+ BO_UNLOCK(bo);
+}
+
+/* Process segments marks to free by cleaner */
+static void
+nandfs_process_segments(struct nandfs_device *fsdev)
+{
+ uint64_t saved_segment;
+ int i;
+
+ if (fsdev->nd_free_base) {
+ saved_segment = nandfs_get_segnum_of_block(fsdev,
+ fsdev->nd_super.s_last_pseg);
+ for (i = 0; i < fsdev->nd_free_count; i++) {
+ if (fsdev->nd_free_base[i] == NANDFS_NOSEGMENT)
+ continue;
+ /* Update superblock if clearing segment point by it */
+ if (fsdev->nd_free_base[i] == saved_segment) {
+ nandfs_write_superblock(fsdev);
+ saved_segment = nandfs_get_segnum_of_block(
+ fsdev, fsdev->nd_super.s_last_pseg);
+ }
+ nandfs_invalidate_bufs(fsdev, fsdev->nd_free_base[i]);
+ nandfs_clear_segment(fsdev, fsdev->nd_free_base[i]);
+ }
+
+ free(fsdev->nd_free_base, M_NANDFSTEMP);
+ fsdev->nd_free_base = NULL;
+ fsdev->nd_free_count = 0;
+ }
+}
+
+/* Collect and write dirty buffers */
+int
+nandfs_sync_file(struct vnode *vp)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_node *nandfs_node;
+ struct nandfsmount *nmp;
+ struct nandfs_node *dat, *su, *ifile, *cp;
+ struct nandfs_seginfo *seginfo = NULL;
+ struct nandfs_segment *seg;
+ int update, error;
+ int cno_changed;
+
+ ASSERT_VOP_LOCKED(vp, __func__);
+ DPRINTF(SYNC, ("%s: START\n", __func__));
+
+ error = 0;
+ nmp = VFSTONANDFS(vp->v_mount);
+ fsdev = nmp->nm_nandfsdev;
+
+ dat = fsdev->nd_dat_node;
+ su = fsdev->nd_su_node;
+ cp = fsdev->nd_cp_node;
+ ifile = nmp->nm_ifile_node;
+
+ NANDFS_WRITEASSERT(fsdev);
+ if (lockmgr(&fsdev->nd_seg_const, LK_UPGRADE, NULL) != 0) {
+ DPRINTF(SYNC, ("%s: lost shared lock\n", __func__));
+ if (lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL) != 0)
+ panic("couldn't lock exclusive");
+ }
+ DPRINTF(SYNC, ("%s: got lock\n", __func__));
+
+ VOP_LOCK(NTOV(su), LK_EXCLUSIVE);
+ create_seginfo(fsdev, &seginfo);
+
+ update = 0;
+
+ nandfs_node = VTON(vp);
+ if (nandfs_node->nn_flags & IN_MODIFIED) {
+ nandfs_node->nn_flags &= ~(IN_MODIFIED);
+ update = 1;
+ }
+
+ if (vp->v_bufobj.bo_dirty.bv_cnt) {
+ error = nandfs_iterate_dirty_buf(vp, seginfo, 0);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+ nandfs_error("%s: err:%d iterating dirty bufs vp:%p",
+ __func__, error, vp);
+ return (error);
+ }
+ update = 1;
+ }
+
+ if (update) {
+ VOP_LOCK(NTOV(ifile), LK_EXCLUSIVE);
+ error = nandfs_node_update(nandfs_node);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ VOP_UNLOCK(NTOV(ifile), 0);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+ nandfs_error("%s: err:%d updating vp:%p",
+ __func__, error, vp);
+ return (error);
+ }
+ VOP_UNLOCK(NTOV(ifile), 0);
+ }
+
+ cno_changed = 0;
+ if (seginfo->blocks) {
+ VOP_LOCK(NTOV(cp), LK_EXCLUSIVE);
+ cno_changed = 1;
+ /* Create new checkpoint */
+ error = nandfs_get_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ VOP_UNLOCK(NTOV(cp), 0);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+ nandfs_error("%s: err:%d getting cp:%jx",
+ __func__, error, fsdev->nd_last_cno + 1);
+ return (error);
+ }
+
+ /* Reiterate all blocks and assign physical block number */
+ nandfs_seginfo_assign_pblk(seginfo);
+
+ /* Fill checkpoint data */
+ error = nandfs_set_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1,
+ &ifile->nn_inode, seginfo->blocks);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ VOP_UNLOCK(NTOV(cp), 0);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+ nandfs_error("%s: err:%d setting cp:%jx",
+ __func__, error, fsdev->nd_last_cno + 1);
+ return (error);
+ }
+
+ VOP_UNLOCK(NTOV(cp), 0);
+ LIST_FOREACH(seg, &seginfo->seg_list, seg_link)
+ nandfs_update_segment(fsdev, seg->seg_num,
+ seg->nblocks + seg->segsum_blocks);
+
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ error = save_seginfo(seginfo, 0);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ VOP_UNLOCK(NTOV(dat), 0);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+ nandfs_error("%s: err:%d updating seg",
+ __func__, error);
+ return (error);
+ }
+ VOP_UNLOCK(NTOV(dat), 0);
+ }
+
+ VOP_UNLOCK(NTOV(su), 0);
+
+ delete_seginfo(seginfo);
+ lockmgr(&fsdev->nd_seg_const, LK_DOWNGRADE, NULL);
+
+ if (cno_changed && !error) {
+ if (nandfs_cps_between_sblocks != 0 &&
+ fsdev->nd_last_cno % nandfs_cps_between_sblocks == 0)
+ nandfs_write_superblock(fsdev);
+ }
+
+ ASSERT_VOP_LOCKED(vp, __func__);
+ DPRINTF(SYNC, ("%s: END error %d\n", __func__, error));
+ return (error);
+}
+
+int
+nandfs_segment_constructor(struct nandfsmount *nmp, int flags)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_seginfo *seginfo = NULL;
+ struct nandfs_segment *seg;
+ struct nandfs_node *dat, *su, *ifile, *cp, *gc;
+ int cno_changed, error;
+
+ DPRINTF(SYNC, ("%s: START\n", __func__));
+ fsdev = nmp->nm_nandfsdev;
+
+ lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
+ DPRINTF(SYNC, ("%s: git lock\n", __func__));
+again:
+ create_seginfo(fsdev, &seginfo);
+
+ dat = fsdev->nd_dat_node;
+ su = fsdev->nd_su_node;
+ cp = fsdev->nd_cp_node;
+ gc = fsdev->nd_gc_node;
+ ifile = nmp->nm_ifile_node;
+
+ VOP_LOCK(NTOV(su), LK_EXCLUSIVE);
+ VOP_LOCK(NTOV(ifile), LK_EXCLUSIVE);
+ VOP_LOCK(NTOV(gc), LK_EXCLUSIVE);
+ VOP_LOCK(NTOV(cp), LK_EXCLUSIVE);
+
+ nandfs_iterate_system_vnode(gc, seginfo);
+ nandfs_iterate_dirty_vnodes(nmp->nm_vfs_mountp, seginfo);
+ nandfs_iterate_system_vnode(ifile, seginfo);
+ nandfs_iterate_system_vnode(su, seginfo);
+
+ cno_changed = 0;
+ if (seginfo->blocks || flags) {
+ cno_changed = 1;
+ /* Create new checkpoint */
+ error = nandfs_get_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ goto error_locks;
+ }
+
+ /* Collect blocks from system files */
+ nandfs_iterate_system_vnode(cp, seginfo);
+ nandfs_iterate_system_vnode(su, seginfo);
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ nandfs_iterate_system_vnode(dat, seginfo);
+ VOP_UNLOCK(NTOV(dat), 0);
+reiterate:
+ seginfo->reiterate = 0;
+ nandfs_iterate_system_vnode(su, seginfo);
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ nandfs_iterate_system_vnode(dat, seginfo);
+ VOP_UNLOCK(NTOV(dat), 0);
+ if (seginfo->reiterate)
+ goto reiterate;
+ if (!(seginfo->curseg) || !seginfo->curseg->num_blocks) {
+ error = create_segment(seginfo);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ goto error_locks;
+ }
+ goto reiterate;
+ }
+
+ /* Reiterate all blocks and assign physical block number */
+ nandfs_seginfo_assign_pblk(seginfo);
+
+ /* Fill superroot */
+ error = nandfs_add_superroot(seginfo);
+ if (error) {
+ clean_seginfo(seginfo, 0);
+ delete_seginfo(seginfo);
+ goto error_locks;
+ }
+ KASSERT(!(seginfo->reiterate), ("reiteration after superroot"));
+
+ /* Fill checkpoint data */
+ nandfs_set_checkpoint(fsdev, cp, fsdev->nd_last_cno + 1,
+ &ifile->nn_inode, seginfo->blocks);
+
+ LIST_FOREACH(seg, &seginfo->seg_list, seg_link)
+ nandfs_update_segment(fsdev, seg->seg_num,
+ seg->nblocks + seg->segsum_blocks);
+
+ VOP_LOCK(NTOV(dat), LK_EXCLUSIVE);
+ error = save_seginfo(seginfo, 1);
+ if (error) {
+ clean_seginfo(seginfo, 1);
+ delete_seginfo(seginfo);
+ goto error_dat;
+ }
+ VOP_UNLOCK(NTOV(dat), 0);
+ }
+
+ VOP_UNLOCK(NTOV(cp), 0);
+ VOP_UNLOCK(NTOV(gc), 0);
+ VOP_UNLOCK(NTOV(ifile), 0);
+
+ nandfs_process_segments(fsdev);
+
+ VOP_UNLOCK(NTOV(su), 0);
+
+ delete_seginfo(seginfo);
+
+ /*
+ * XXX: a hack, will go away soon
+ */
+ if ((NTOV(dat)->v_bufobj.bo_dirty.bv_cnt != 0 ||
+ NTOV(cp)->v_bufobj.bo_dirty.bv_cnt != 0 ||
+ NTOV(gc)->v_bufobj.bo_dirty.bv_cnt != 0 ||
+ NTOV(ifile)->v_bufobj.bo_dirty.bv_cnt != 0 ||
+ NTOV(su)->v_bufobj.bo_dirty.bv_cnt != 0) &&
+ (flags & NANDFS_UMOUNT)) {
+ DPRINTF(SYNC, ("%s: RERUN\n", __func__));
+ goto again;
+ }
+
+ MPASS(fsdev->nd_free_base == NULL);
+
+ lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
+
+ if (cno_changed) {
+ if ((nandfs_cps_between_sblocks != 0 &&
+ fsdev->nd_last_cno % nandfs_cps_between_sblocks == 0) ||
+ flags & NANDFS_UMOUNT)
+ nandfs_write_superblock(fsdev);
+ }
+
+ DPRINTF(SYNC, ("%s: END\n", __func__));
+ return (0);
+error_dat:
+ VOP_UNLOCK(NTOV(dat), 0);
+error_locks:
+ VOP_UNLOCK(NTOV(cp), 0);
+ VOP_UNLOCK(NTOV(gc), 0);
+ VOP_UNLOCK(NTOV(ifile), 0);
+ VOP_UNLOCK(NTOV(su), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
+
+ return (error);
+}
+
+#ifdef DDB
+/*
+ * Show details about the given NANDFS mount point.
+ */
+DB_SHOW_COMMAND(nandfs, db_show_nandfs)
+{
+ struct mount *mp;
+ struct nandfs_device *nffsdev;
+ struct nandfs_segment *seg;
+ struct nandfsmount *nmp;
+ struct buf *bp;
+ struct vnode *vp;
+
+ if (!have_addr) {
+ db_printf("\nUsage: show nandfs <mount_addr>\n");
+ return;
+ }
+
+ mp = (struct mount *)addr;
+ db_printf("%p %s on %s (%s)\n", mp, mp->mnt_stat.f_mntfromname,
+ mp->mnt_stat.f_mntonname, mp->mnt_stat.f_fstypename);
+
+
+ nmp = (struct nandfsmount *)(mp->mnt_data);
+ nffsdev = nmp->nm_nandfsdev;
+ db_printf("dev vnode:%p\n", nffsdev->nd_devvp);
+ db_printf("blocksize:%jx last cno:%jx last pseg:%jx seg num:%jx\n",
+ (uintmax_t)nffsdev->nd_blocksize, (uintmax_t)nffsdev->nd_last_cno,
+ (uintmax_t)nffsdev->nd_last_pseg, (uintmax_t)nffsdev->nd_seg_num);
+ db_printf("system nodes: dat:%p cp:%p su:%p ifile:%p gc:%p\n",
+ nffsdev->nd_dat_node, nffsdev->nd_cp_node, nffsdev->nd_su_node,
+ nmp->nm_ifile_node, nffsdev->nd_gc_node);
+
+ if (nffsdev->nd_seginfo != NULL) {
+ LIST_FOREACH(seg, &nffsdev->nd_seginfo->seg_list, seg_link) {
+ db_printf("seg: %p\n", seg);
+ TAILQ_FOREACH(bp, &seg->segsum,
+ b_cluster.cluster_entry)
+ db_printf("segbp %p\n", bp);
+ TAILQ_FOREACH(bp, &seg->data,
+ b_cluster.cluster_entry) {
+ vp = bp->b_vp;
+ db_printf("bp:%p bp->b_vp:%p ino:%jx\n", bp, vp,
+ (uintmax_t)(vp ? VTON(vp)->nn_ino : 0));
+ }
+ }
+ }
+}
+#endif
diff --git a/sys/fs/nandfs/nandfs_subr.c b/sys/fs/nandfs/nandfs_subr.c
new file mode 100644
index 0000000..b485422
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_subr.c
@@ -0,0 +1,1120 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_subr.c,v 1.4 2009/07/29 17:06:57 reinoud
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/proc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/signalvar.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/lockf.h>
+#include <sys/libkern.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include <machine/_inttypes.h>
+#include "nandfs_mount.h"
+#include "nandfs.h"
+#include "nandfs_subr.h"
+
+MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount");;
+MALLOC_DEFINE(M_NANDFSTEMP, "nandfs_tmt", "NANDFS tmp");
+
+uma_zone_t nandfs_node_zone;
+
+void nandfs_bdflush(struct bufobj *bo, struct buf *bp);
+int nandfs_bufsync(struct bufobj *bo, int waitfor);
+
+struct buf_ops buf_ops_nandfs = {
+ .bop_name = "buf_ops_nandfs",
+ .bop_write = bufwrite,
+ .bop_strategy = bufstrategy,
+ .bop_sync = nandfs_bufsync,
+ .bop_bdflush = nandfs_bdflush,
+};
+
+int
+nandfs_bufsync(struct bufobj *bo, int waitfor)
+{
+ struct vnode *vp;
+ int error = 0;
+
+ vp = bo->__bo_vnode;
+
+ ASSERT_VOP_LOCKED(vp, __func__);
+ error = nandfs_sync_file(vp);
+ if (error)
+ nandfs_warning("%s: cannot flush buffers err:%d\n",
+ __func__, error);
+
+ return (error);
+}
+
+void
+nandfs_bdflush(bo, bp)
+ struct bufobj *bo;
+ struct buf *bp;
+{
+ struct vnode *vp;
+ int error;
+
+ if (bo->bo_dirty.bv_cnt <= ((dirtybufthresh * 8) / 10))
+ return;
+
+ vp = bp->b_vp;
+ if (NANDFS_SYS_NODE(VTON(vp)->nn_ino))
+ return;
+
+ if (NANDFS_IS_INDIRECT(bp))
+ return;
+
+ error = nandfs_sync_file(vp);
+ if (error)
+ nandfs_warning("%s: cannot flush buffers err:%d\n",
+ __func__, error);
+}
+
+int
+nandfs_init(struct vfsconf *vfsp)
+{
+
+ nandfs_node_zone = uma_zcreate("nandfs node zone",
+ sizeof(struct nandfs_node), NULL, NULL, NULL, NULL, 0, 0);
+
+ return (0);
+}
+
+int
+nandfs_uninit(struct vfsconf *vfsp)
+{
+
+ uma_zdestroy(nandfs_node_zone);
+ return (0);
+}
+
+/* Basic calculators */
+uint64_t
+nandfs_get_segnum_of_block(struct nandfs_device *nandfsdev,
+ nandfs_daddr_t blocknr)
+{
+ uint64_t segnum, blks_per_seg;
+
+ MPASS(blocknr >= nandfsdev->nd_fsdata.f_first_data_block);
+
+ blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment;
+
+ segnum = blocknr / blks_per_seg;
+ segnum -= nandfsdev->nd_fsdata.f_first_data_block / blks_per_seg;
+
+ DPRINTF(SYNC, ("%s: returning blocknr %jx -> segnum %jx\n", __func__,
+ blocknr, segnum));
+
+ return (segnum);
+}
+
+void
+nandfs_get_segment_range(struct nandfs_device *nandfsdev, uint64_t segnum,
+ uint64_t *seg_start, uint64_t *seg_end)
+{
+ uint64_t blks_per_seg;
+
+ blks_per_seg = nandfsdev->nd_fsdata.f_blocks_per_segment;
+ *seg_start = nandfsdev->nd_fsdata.f_first_data_block +
+ blks_per_seg * segnum;
+ if (seg_end != NULL)
+ *seg_end = *seg_start + blks_per_seg -1;
+}
+
+void nandfs_calc_mdt_consts(struct nandfs_device *nandfsdev,
+ struct nandfs_mdt *mdt, int entry_size)
+{
+ uint32_t blocksize = nandfsdev->nd_blocksize;
+
+ mdt->entries_per_group = blocksize * 8;
+ mdt->entries_per_block = blocksize / entry_size;
+
+ mdt->blocks_per_group =
+ (mdt->entries_per_group -1) / mdt->entries_per_block + 1 + 1;
+ mdt->groups_per_desc_block =
+ blocksize / sizeof(struct nandfs_block_group_desc);
+ mdt->blocks_per_desc_block =
+ mdt->groups_per_desc_block * mdt->blocks_per_group + 1;
+}
+
+int
+nandfs_dev_bread(struct nandfs_device *nandfsdev, nandfs_lbn_t blocknr,
+ struct ucred *cred, int flags, struct buf **bpp)
+{
+ int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
+ int error;
+
+ DPRINTF(BLOCK, ("%s: read from block %jx vp %p\n", __func__,
+ blocknr * blk2dev, nandfsdev->nd_devvp));
+ error = bread(nandfsdev->nd_devvp, blocknr * blk2dev,
+ nandfsdev->nd_blocksize, NOCRED, bpp);
+ if (error)
+ nandfs_error("%s: cannot read from device - blk:%jx\n",
+ __func__, blocknr);
+ return (error);
+}
+
+/* Read on a node */
+int
+nandfs_bread(struct nandfs_node *node, nandfs_lbn_t blocknr,
+ struct ucred *cred, int flags, struct buf **bpp)
+{
+ nandfs_daddr_t vblk;
+ int error;
+
+ DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
+ blocknr));
+
+ error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
+ cred, bpp);
+
+ KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__,
+ NTOV(node), blocknr, error));
+
+ if (!nandfs_vblk_get(*bpp) &&
+ ((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) {
+ nandfs_bmap_lookup(node, blocknr, &vblk);
+ nandfs_vblk_set(*bpp, vblk);
+ }
+ return (error);
+}
+
+int
+nandfs_bread_meta(struct nandfs_node *node, nandfs_lbn_t blocknr,
+ struct ucred *cred, int flags, struct buf **bpp)
+{
+ nandfs_daddr_t vblk;
+ int error;
+
+ DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
+ blocknr));
+
+ error = bread(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
+ cred, bpp);
+
+ KASSERT(error == 0, ("%s: vp:%p lbn:%#jx err:%d\n", __func__,
+ NTOV(node), blocknr, error));
+
+ if (!nandfs_vblk_get(*bpp) &&
+ ((*bpp)->b_flags & B_CACHE) && node->nn_ino != NANDFS_DAT_INO) {
+ nandfs_bmap_lookup(node, blocknr, &vblk);
+ nandfs_vblk_set(*bpp, vblk);
+ }
+
+ return (error);
+}
+
+int
+nandfs_bdestroy(struct nandfs_node *node, nandfs_daddr_t vblk)
+{
+ int error;
+
+ if (!NANDFS_SYS_NODE(node->nn_ino))
+ NANDFS_WRITEASSERT(node->nn_nandfsdev);
+
+ error = nandfs_vblock_end(node->nn_nandfsdev, vblk);
+ if (error) {
+ nandfs_error("%s: ending vblk: %jx failed\n",
+ __func__, (uintmax_t)vblk);
+ return (error);
+ }
+ node->nn_inode.i_blocks--;
+
+ return (0);
+}
+
+int
+nandfs_bcreate(struct nandfs_node *node, nandfs_lbn_t blocknr,
+ struct ucred *cred, int flags, struct buf **bpp)
+{
+ int error;
+
+ ASSERT_VOP_LOCKED(NTOV(node), __func__);
+ if (!NANDFS_SYS_NODE(node->nn_ino))
+ NANDFS_WRITEASSERT(node->nn_nandfsdev);
+
+ DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
+ blocknr));
+
+ *bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
+ 0, 0, 0);
+
+ KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__,
+ NTOV(node), blocknr));
+
+ if (*bpp) {
+ vfs_bio_clrbuf(*bpp);
+ (*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */
+ error = nandfs_bmap_insert_block(node, blocknr, *bpp);
+ if (error) {
+ nandfs_warning("%s: failed bmap insert node:%p"
+ " blk:%jx\n", __func__, node, blocknr);
+ brelse(*bpp);
+ return (error);
+ }
+ node->nn_inode.i_blocks++;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int
+nandfs_bcreate_meta(struct nandfs_node *node, nandfs_lbn_t blocknr,
+ struct ucred *cred, int flags, struct buf **bpp)
+{
+ struct nandfs_device *fsdev;
+ nandfs_daddr_t vblk;
+ int error;
+
+ ASSERT_VOP_LOCKED(NTOV(node), __func__);
+ NANDFS_WRITEASSERT(node->nn_nandfsdev);
+
+ DPRINTF(BLOCK, ("%s: vp:%p lbn:%#jx\n", __func__, NTOV(node),
+ blocknr));
+
+ fsdev = node->nn_nandfsdev;
+
+ *bpp = getblk(NTOV(node), blocknr, node->nn_nandfsdev->nd_blocksize,
+ 0, 0, 0);
+
+ KASSERT((*bpp), ("%s: vp:%p lbn:%#jx\n", __func__,
+ NTOV(node), blocknr));
+
+ memset((*bpp)->b_data, 0, fsdev->nd_blocksize);
+
+ vfs_bio_clrbuf(*bpp);
+ (*bpp)->b_blkno = ~(0); /* To avoid VOP_BMAP in bdwrite */
+
+ nandfs_buf_set(*bpp, NANDFS_VBLK_ASSIGNED);
+
+ if (node->nn_ino != NANDFS_DAT_INO) {
+ error = nandfs_vblock_alloc(fsdev, &vblk);
+ if (error) {
+ nandfs_buf_clear(*bpp, NANDFS_VBLK_ASSIGNED);
+ brelse(*bpp);
+ return (error);
+ }
+ } else
+ vblk = fsdev->nd_fakevblk++;
+
+ nandfs_vblk_set(*bpp, vblk);
+
+ nandfs_bmap_insert_block(node, blocknr, *bpp);
+ return (0);
+}
+
+/* Translate index to a file block number and an entry */
+void
+nandfs_mdt_trans(struct nandfs_mdt *mdt, uint64_t index,
+ nandfs_lbn_t *blocknr, uint32_t *entry_in_block)
+{
+ uint64_t blknr;
+ uint64_t group, group_offset, blocknr_in_group;
+ uint64_t desc_block, desc_offset;
+
+ /* Calculate our offset in the file */
+ group = index / mdt->entries_per_group;
+ group_offset = index % mdt->entries_per_group;
+ desc_block = group / mdt->groups_per_desc_block;
+ desc_offset = group % mdt->groups_per_desc_block;
+ blocknr_in_group = group_offset / mdt->entries_per_block;
+
+ /* To descgroup offset */
+ blknr = 1 + desc_block * mdt->blocks_per_desc_block;
+
+ /* To group offset */
+ blknr += desc_offset * mdt->blocks_per_group;
+
+ /* To actual file block */
+ blknr += 1 + blocknr_in_group;
+
+ *blocknr = blknr;
+ *entry_in_block = group_offset % mdt->entries_per_block;
+}
+
+void
+nandfs_mdt_trans_blk(struct nandfs_mdt *mdt, uint64_t index,
+ uint64_t *desc, uint64_t *bitmap, nandfs_lbn_t *blocknr,
+ uint32_t *entry_in_block)
+{
+ uint64_t blknr;
+ uint64_t group, group_offset, blocknr_in_group;
+ uint64_t desc_block, desc_offset;
+
+ /* Calculate our offset in the file */
+ group = index / mdt->entries_per_group;
+ group_offset = index % mdt->entries_per_group;
+ desc_block = group / mdt->groups_per_desc_block;
+ desc_offset = group % mdt->groups_per_desc_block;
+ blocknr_in_group = group_offset / mdt->entries_per_block;
+
+ /* To descgroup offset */
+ *desc = desc_block * mdt->blocks_per_desc_block;
+ blknr = 1 + desc_block * mdt->blocks_per_desc_block;
+
+ /* To group offset */
+ blknr += desc_offset * mdt->blocks_per_group;
+ *bitmap = blknr;
+
+ /* To actual file block */
+ blknr += 1 + blocknr_in_group;
+
+ *blocknr = blknr;
+ *entry_in_block = group_offset % mdt->entries_per_block;
+
+ DPRINTF(ALLOC,
+ ("%s: desc_buf: %jx bitmap_buf: %jx entry_buf: %jx entry: %x\n",
+ __func__, (uintmax_t)*desc, (uintmax_t)*bitmap,
+ (uintmax_t)*blocknr, *entry_in_block));
+}
+
+int
+nandfs_vtop(struct nandfs_node *node, nandfs_daddr_t vblocknr,
+ nandfs_daddr_t *pblocknr)
+{
+ struct nandfs_node *dat_node;
+ struct nandfs_dat_entry *entry;
+ struct buf *bp;
+ nandfs_lbn_t ldatblknr;
+ uint32_t entry_in_block;
+ int locked, error;
+
+ if (node->nn_ino == NANDFS_DAT_INO || node->nn_ino == NANDFS_GC_INO) {
+ *pblocknr = vblocknr;
+ return (0);
+ }
+
+ /* only translate valid vblocknrs */
+ if (vblocknr == 0)
+ return (0);
+
+ dat_node = node->nn_nandfsdev->nd_dat_node;
+ nandfs_mdt_trans(&node->nn_nandfsdev->nd_dat_mdt, vblocknr, &ldatblknr,
+ &entry_in_block);
+
+ locked = NANDFS_VOP_ISLOCKED(NTOV(dat_node));
+ if (!locked)
+ VOP_LOCK(NTOV(dat_node), LK_SHARED);
+ error = nandfs_bread(dat_node, ldatblknr, NOCRED, 0, &bp);
+ if (error) {
+ DPRINTF(TRANSLATE, ("vtop: can't read in DAT block %#jx!\n",
+ (uintmax_t)ldatblknr));
+ brelse(bp);
+ VOP_UNLOCK(NTOV(dat_node), 0);
+ return (error);
+ }
+
+ /* Get our translation */
+ entry = ((struct nandfs_dat_entry *) bp->b_data) + entry_in_block;
+ DPRINTF(TRANSLATE, ("\tentry %p data %p entry_in_block %x\n",
+ entry, bp->b_data, entry_in_block))
+ DPRINTF(TRANSLATE, ("\tvblk %#jx -> %#jx for cp [%#jx-%#jx]\n",
+ (uintmax_t)vblocknr, (uintmax_t)entry->de_blocknr,
+ (uintmax_t)entry->de_start, (uintmax_t)entry->de_end));
+
+ *pblocknr = entry->de_blocknr;
+ brelse(bp);
+ if (!locked)
+ VOP_UNLOCK(NTOV(dat_node), 0);
+
+ MPASS(*pblocknr >= node->nn_nandfsdev->nd_fsdata.f_first_data_block ||
+ *pblocknr == 0);
+
+ return (0);
+}
+
+int
+nandfs_segsum_valid(struct nandfs_segment_summary *segsum)
+{
+
+ return (segsum->ss_magic == NANDFS_SEGSUM_MAGIC);
+}
+
+int
+nandfs_load_segsum(struct nandfs_device *fsdev, nandfs_daddr_t blocknr,
+ struct nandfs_segment_summary *segsum)
+{
+ struct buf *bp;
+ int error;
+
+ DPRINTF(VOLUMES, ("nandfs: try segsum at block %jx\n",
+ (uintmax_t)blocknr));
+
+ error = nandfs_dev_bread(fsdev, blocknr, NOCRED, 0, &bp);
+ if (error)
+ return (error);
+
+ memcpy(segsum, bp->b_data, sizeof(struct nandfs_segment_summary));
+ brelse(bp);
+
+ if (!nandfs_segsum_valid(segsum)) {
+ DPRINTF(VOLUMES, ("%s: bad magic pseg:%jx\n", __func__,
+ blocknr));
+ return (EINVAL);
+ }
+
+ return (error);
+}
+
+static int
+nandfs_load_super_root(struct nandfs_device *nandfsdev,
+ struct nandfs_segment_summary *segsum, uint64_t pseg)
+{
+ struct nandfs_super_root super_root;
+ struct buf *bp;
+ uint64_t blocknr;
+ uint32_t super_root_crc, comp_crc;
+ int off, error;
+
+ /* Check if there is a superroot */
+ if ((segsum->ss_flags & NANDFS_SS_SR) == 0) {
+ DPRINTF(VOLUMES, ("%s: no super root in pseg:%jx\n", __func__,
+ pseg));
+ return (ENOENT);
+ }
+
+ /* Get our super root, located at the end of the pseg */
+ blocknr = pseg + segsum->ss_nblocks - 1;
+ DPRINTF(VOLUMES, ("%s: try at %#jx\n", __func__, (uintmax_t)blocknr));
+
+ error = nandfs_dev_bread(nandfsdev, blocknr, NOCRED, 0, &bp);
+ if (error)
+ return (error);
+
+ memcpy(&super_root, bp->b_data, sizeof(struct nandfs_super_root));
+ brelse(bp);
+
+ /* Check super root CRC */
+ super_root_crc = super_root.sr_sum;
+ off = sizeof(super_root.sr_sum);
+ comp_crc = crc32((uint8_t *)&super_root + off,
+ NANDFS_SR_BYTES - off);
+
+ if (super_root_crc != comp_crc) {
+ DPRINTF(VOLUMES, ("%s: invalid crc:%#x [expect:%#x]\n",
+ __func__, super_root_crc, comp_crc));
+ return (EINVAL);
+ }
+
+ nandfsdev->nd_super_root = super_root;
+ DPRINTF(VOLUMES, ("%s: got valid superroot\n", __func__));
+
+ return (0);
+}
+
+/*
+ * Search for the last super root recorded.
+ */
+int
+nandfs_search_super_root(struct nandfs_device *nandfsdev)
+{
+ struct nandfs_super_block *super;
+ struct nandfs_segment_summary segsum;
+ uint64_t seg_start, seg_end, cno, seq, create, pseg;
+ uint64_t segnum;
+ int error, found;
+
+ error = found = 0;
+
+ /* Search for last super root */
+ pseg = nandfsdev->nd_super.s_last_pseg;
+ segnum = nandfs_get_segnum_of_block(nandfsdev, pseg);
+
+ cno = nandfsdev->nd_super.s_last_cno;
+ create = seq = 0;
+ DPRINTF(VOLUMES, ("%s: start in pseg %#jx\n", __func__,
+ (uintmax_t)pseg));
+
+ for (;;) {
+ error = nandfs_load_segsum(nandfsdev, pseg, &segsum);
+ if (error)
+ break;
+
+ if (segsum.ss_seq < seq || segsum.ss_create < create)
+ break;
+
+ /* Try to load super root */
+ if (segsum.ss_flags & NANDFS_SS_SR) {
+ error = nandfs_load_super_root(nandfsdev, &segsum, pseg);
+ if (error)
+ break; /* confused */
+ found = 1;
+
+ super = &nandfsdev->nd_super;
+ nandfsdev->nd_last_segsum = segsum;
+ super->s_last_pseg = pseg;
+ super->s_last_cno = cno++;
+ super->s_last_seq = segsum.ss_seq;
+ super->s_state = NANDFS_VALID_FS;
+ seq = segsum.ss_seq;
+ create = segsum.ss_create;
+ } else {
+ seq = segsum.ss_seq;
+ create = segsum.ss_create;
+ }
+
+ /* Calculate next partial segment location */
+ pseg += segsum.ss_nblocks;
+ DPRINTF(VOLUMES, ("%s: next partial seg is %jx\n", __func__,
+ (uintmax_t)pseg));
+
+ /* Did we reach the end of the segment? if so, go to the next */
+ nandfs_get_segment_range(nandfsdev, segnum, &seg_start,
+ &seg_end);
+ if (pseg >= seg_end) {
+ pseg = segsum.ss_next;
+ DPRINTF(VOLUMES,
+ (" partial seg oor next is %jx[%jx - %jx]\n",
+ (uintmax_t)pseg, (uintmax_t)seg_start,
+ (uintmax_t)seg_end));
+ }
+ segnum = nandfs_get_segnum_of_block(nandfsdev, pseg);
+ }
+
+ if (error && !found)
+ return (error);
+
+ return (0);
+}
+
+int
+nandfs_get_node_raw(struct nandfs_device *nandfsdev, struct nandfsmount *nmp,
+ uint64_t ino, struct nandfs_inode *inode, struct nandfs_node **nodep)
+{
+ struct nandfs_node *node;
+ struct vnode *nvp;
+ struct mount *mp;
+ int error;
+
+ *nodep = NULL;
+
+ /* Associate with mountpoint if present */
+ if (nmp) {
+ mp = nmp->nm_vfs_mountp;
+ error = getnewvnode("nandfs", mp, &nandfs_vnodeops, &nvp);
+ if (error) {
+ return (error);
+ }
+ } else {
+ mp = NULL;
+ error = getnewvnode("snandfs", mp, &nandfs_system_vnodeops,
+ &nvp);
+ if (error) {
+ return (error);
+ }
+ }
+
+ if (mp)
+ NANDFS_WRITELOCK(nandfsdev);
+
+ DPRINTF(IFILE, ("%s: ino: %#jx -> vp: %p\n",
+ __func__, (uintmax_t)ino, nvp));
+ /* Lock node */
+ lockmgr(nvp->v_vnlock, LK_EXCLUSIVE, NULL);
+
+ if (mp) {
+ error = insmntque(nvp, mp);
+ if (error != 0) {
+ *nodep = NULL;
+ return (error);
+ }
+ }
+
+ node = uma_zalloc(nandfs_node_zone, M_WAITOK | M_ZERO);
+
+ /* Crosslink */
+ node->nn_vnode = nvp;
+ nvp->v_bufobj.bo_ops = &buf_ops_nandfs;
+ node->nn_nmp = nmp;
+ node->nn_nandfsdev = nandfsdev;
+ nvp->v_data = node;
+
+ /* Initiase NANDFS node */
+ node->nn_ino = ino;
+ if (inode != NULL)
+ node->nn_inode = *inode;
+
+ nandfs_vinit(nvp, ino);
+
+ /* Return node */
+ *nodep = node;
+ DPRINTF(IFILE, ("%s: ino:%#jx vp:%p node:%p\n",
+ __func__, (uintmax_t)ino, nvp, *nodep));
+
+ return (0);
+}
+
+int
+nandfs_get_node(struct nandfsmount *nmp, uint64_t ino,
+ struct nandfs_node **nodep)
+{
+ struct nandfs_device *nandfsdev;
+ struct nandfs_inode inode, *entry;
+ struct vnode *nvp, *vpp;
+ struct thread *td;
+ struct buf *bp;
+ uint64_t ivblocknr;
+ uint32_t entry_in_block;
+ int error;
+
+ /* Look up node in hash table */
+ td = curthread;
+ *nodep = NULL;
+
+ if ((ino < NANDFS_ATIME_INO) && (ino != NANDFS_ROOT_INO)) {
+ printf("nandfs_get_node: system ino %"PRIu64" not in mount "
+ "point!\n", ino);
+ return (ENOENT);
+ }
+
+ error = vfs_hash_get(nmp->nm_vfs_mountp, ino, LK_EXCLUSIVE, td, &nvp,
+ NULL, NULL);
+ if (error)
+ return (error);
+
+ if (nvp != NULL) {
+ *nodep = (struct nandfs_node *)nvp->v_data;
+ return (0);
+ }
+
+ /* Look up inode structure in mountpoints ifile */
+ nandfsdev = nmp->nm_nandfsdev;
+ nandfs_mdt_trans(&nandfsdev->nd_ifile_mdt, ino, &ivblocknr,
+ &entry_in_block);
+
+ VOP_LOCK(NTOV(nmp->nm_ifile_node), LK_SHARED);
+ error = nandfs_bread(nmp->nm_ifile_node, ivblocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0);
+ return (ENOENT);
+ }
+
+ /* Get inode entry */
+ entry = (struct nandfs_inode *) bp->b_data + entry_in_block;
+ memcpy(&inode, entry, sizeof(struct nandfs_inode));
+ brelse(bp);
+ VOP_UNLOCK(NTOV(nmp->nm_ifile_node), 0);
+
+ /* Get node */
+ error = nandfs_get_node_raw(nmp->nm_nandfsdev, nmp, ino, &inode, nodep);
+ if (error) {
+ *nodep = NULL;
+ return (error);
+ }
+
+ nvp = (*nodep)->nn_vnode;
+ error = vfs_hash_insert(nvp, ino, 0, td, &vpp, NULL, NULL);
+ if (error) {
+ *nodep = NULL;
+ return (error);
+ }
+
+ return (error);
+}
+
+void
+nandfs_dispose_node(struct nandfs_node **nodep)
+{
+ struct nandfs_node *node;
+ struct vnode *vp;
+
+ /* Protect against rogue values */
+ node = *nodep;
+ if (!node) {
+ return;
+ }
+ DPRINTF(NODE, ("nandfs_dispose_node: %p\n", *nodep));
+
+ vp = NTOV(node);
+ vp->v_data = NULL;
+
+ /* Free our associated memory */
+ uma_zfree(nandfs_node_zone, node);
+
+ *nodep = NULL;
+}
+
+int
+nandfs_lookup_name_in_dir(struct vnode *dvp, const char *name, int namelen,
+ uint64_t *ino, int *found, uint64_t *off)
+{
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfs_dir_entry *ndirent;
+ struct buf *bp;
+ uint64_t file_size, diroffset, blkoff;
+ uint64_t blocknr;
+ uint32_t blocksize = dir_node->nn_nandfsdev->nd_blocksize;
+ uint8_t *pos, name_len;
+ int error;
+
+ *found = 0;
+
+ DPRINTF(VNCALL, ("%s: %s file\n", __func__, name));
+ if (dvp->v_type != VDIR) {
+ return (ENOTDIR);
+ }
+
+ /* Get directory filesize */
+ file_size = dir_node->nn_inode.i_size;
+
+ /* Walk the directory */
+ diroffset = 0;
+ blocknr = 0;
+ blkoff = 0;
+ error = nandfs_bread(dir_node, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (EIO);
+ }
+
+ while (diroffset < file_size) {
+ if (blkoff >= blocksize) {
+ blkoff = 0; blocknr++;
+ brelse(bp);
+ error = nandfs_bread(dir_node, blocknr, NOCRED, 0,
+ &bp);
+ if (error) {
+ brelse(bp);
+ return (EIO);
+ }
+ }
+
+ /* Read in one dirent */
+ pos = (uint8_t *) bp->b_data + blkoff;
+ ndirent = (struct nandfs_dir_entry *) pos;
+ name_len = ndirent->name_len;
+
+ if ((name_len == namelen) &&
+ (strncmp(name, ndirent->name, name_len) == 0) &&
+ (ndirent->inode != 0)) {
+ *ino = ndirent->inode;
+ *off = diroffset;
+ DPRINTF(LOOKUP, ("found `%.*s` with ino %"PRIx64"\n",
+ name_len, ndirent->name, *ino));
+ *found = 1;
+ break;
+ }
+
+ /* Advance */
+ diroffset += ndirent->rec_len;
+ blkoff += ndirent->rec_len;
+ }
+ brelse(bp);
+
+ return (error);
+}
+
+int
+nandfs_get_fsinfo(struct nandfsmount *nmp, struct nandfs_fsinfo *fsinfo)
+{
+ struct nandfs_device *fsdev;
+
+ fsdev = nmp->nm_nandfsdev;
+
+ memcpy(&fsinfo->fs_fsdata, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata));
+ memcpy(&fsinfo->fs_super, &fsdev->nd_super, sizeof(fsdev->nd_super));
+ snprintf(fsinfo->fs_dev, sizeof(fsinfo->fs_dev),
+ "%s", nmp->nm_vfs_mountp->mnt_stat.f_mntfromname);
+
+ return (0);
+}
+
+void
+nandfs_inode_init(struct nandfs_inode *inode, uint16_t mode)
+{
+ struct timespec ts;
+
+ vfs_timestamp(&ts);
+
+ inode->i_blocks = 0;
+ inode->i_size = 0;
+ inode->i_ctime = ts.tv_sec;
+ inode->i_ctime_nsec = ts.tv_nsec;
+ inode->i_mtime = ts.tv_sec;
+ inode->i_mtime_nsec = ts.tv_nsec;
+ inode->i_mode = mode;
+ inode->i_links_count = 1;
+ if (S_ISDIR(mode))
+ inode->i_links_count = 2;
+ inode->i_flags = 0;
+
+ inode->i_special = 0;
+ memset(inode->i_db, 0, sizeof(inode->i_db));
+ memset(inode->i_ib, 0, sizeof(inode->i_ib));
+}
+
+void
+nandfs_inode_destroy(struct nandfs_inode *inode)
+{
+
+ MPASS(inode->i_blocks == 0);
+ bzero(inode, sizeof(*inode));
+}
+
+int
+nandfs_fs_full(struct nandfs_device *nffsdev)
+{
+ uint64_t space, bps;
+
+ bps = nffsdev->nd_fsdata.f_blocks_per_segment;
+ space = (nffsdev->nd_clean_segs - 1) * bps;
+
+ DPRINTF(BUF, ("%s: bufs:%jx space:%jx\n", __func__,
+ (uintmax_t)nffsdev->nd_dirty_bufs, (uintmax_t)space));
+
+ if (nffsdev->nd_dirty_bufs + (10 * bps) >= space)
+ return (1);
+
+ return (0);
+}
+
+static int
+_nandfs_dirty_buf(struct buf *bp, int dirty_meta, int force)
+{
+ struct nandfs_device *nffsdev;
+ struct nandfs_node *node;
+ uint64_t ino, bps;
+
+ if (NANDFS_ISGATHERED(bp)) {
+ bqrelse(bp);
+ return (0);
+ }
+ if ((bp->b_flags & (B_MANAGED | B_DELWRI)) == (B_MANAGED | B_DELWRI)) {
+ bqrelse(bp);
+ return (0);
+ }
+
+ node = VTON(bp->b_vp);
+ nffsdev = node->nn_nandfsdev;
+ DPRINTF(BUF, ("%s: buf:%p\n", __func__, bp));
+ ino = node->nn_ino;
+
+ if (nandfs_fs_full(nffsdev) && !NANDFS_SYS_NODE(ino) && !force) {
+ brelse(bp);
+ return (ENOSPC);
+ }
+
+ bp->b_flags |= B_MANAGED;
+ bdwrite(bp);
+
+ nandfs_dirty_bufs_increment(nffsdev);
+
+ KASSERT((bp->b_vp), ("vp missing for bp"));
+ KASSERT((nandfs_vblk_get(bp) || ino == NANDFS_DAT_INO),
+ ("bp vblk is 0"));
+
+ /*
+ * To maintain consistency of FS we need to force making
+ * meta buffers dirty, even if free space is low.
+ */
+ if (dirty_meta && ino != NANDFS_GC_INO)
+ nandfs_bmap_dirty_blocks(VTON(bp->b_vp), bp, 1);
+
+ bps = nffsdev->nd_fsdata.f_blocks_per_segment;
+
+ if (nffsdev->nd_dirty_bufs >= (bps * nandfs_max_dirty_segs)) {
+ mtx_lock(&nffsdev->nd_sync_mtx);
+ if (nffsdev->nd_syncing == 0) {
+ DPRINTF(SYNC, ("%s: wakeup gc\n", __func__));
+ nffsdev->nd_syncing = 1;
+ wakeup(&nffsdev->nd_syncing);
+ }
+ mtx_unlock(&nffsdev->nd_sync_mtx);
+ }
+
+ return (0);
+}
+
+int
+nandfs_dirty_buf(struct buf *bp, int force)
+{
+
+ return (_nandfs_dirty_buf(bp, 1, force));
+}
+
+int
+nandfs_dirty_buf_meta(struct buf *bp, int force)
+{
+
+ return (_nandfs_dirty_buf(bp, 0, force));
+}
+
+void
+nandfs_undirty_buf_fsdev(struct nandfs_device *nffsdev, struct buf *bp)
+{
+
+ BUF_ASSERT_HELD(bp);
+
+ if (bp->b_flags & B_DELWRI) {
+ bp->b_flags &= ~(B_DELWRI|B_MANAGED);
+ nandfs_dirty_bufs_decrement(nffsdev);
+ }
+ /*
+ * Since it is now being written, we can clear its deferred write flag.
+ */
+ bp->b_flags &= ~B_DEFERRED;
+
+ brelse(bp);
+}
+
+void
+nandfs_undirty_buf(struct buf *bp)
+{
+ struct nandfs_node *node;
+
+ node = VTON(bp->b_vp);
+
+ nandfs_undirty_buf_fsdev(node->nn_nandfsdev, bp);
+}
+
+void
+nandfs_vblk_set(struct buf *bp, nandfs_daddr_t blocknr)
+{
+
+ nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1);
+ *vblk = blocknr;
+}
+
+nandfs_daddr_t
+nandfs_vblk_get(struct buf *bp)
+{
+
+ nandfs_daddr_t *vblk = (nandfs_daddr_t *)(&bp->b_fsprivate1);
+ return (*vblk);
+}
+
+void
+nandfs_buf_set(struct buf *bp, uint32_t bits)
+{
+ uintptr_t flags;
+
+ flags = (uintptr_t)bp->b_fsprivate3;
+ flags |= (uintptr_t)bits;
+ bp->b_fsprivate3 = (void *)flags;
+}
+
+void
+nandfs_buf_clear(struct buf *bp, uint32_t bits)
+{
+ uintptr_t flags;
+
+ flags = (uintptr_t)bp->b_fsprivate3;
+ flags &= ~(uintptr_t)bits;
+ bp->b_fsprivate3 = (void *)flags;
+}
+
+int
+nandfs_buf_check(struct buf *bp, uint32_t bits)
+{
+ uintptr_t flags;
+
+ flags = (uintptr_t)bp->b_fsprivate3;
+ if (flags & bits)
+ return (1);
+ return (0);
+}
+
+int
+nandfs_erase(struct nandfs_device *fsdev, off_t offset, size_t size)
+{
+ struct buf *bp;
+ int read_size, error, i;
+
+ DPRINTF(BLOCK, ("%s: performing erase at offset %jx size %zx\n",
+ __func__, offset, size));
+
+ MPASS(size % fsdev->nd_erasesize == 0);
+
+ if (fsdev->nd_is_nand) {
+ error = g_delete_data(fsdev->nd_gconsumer, offset, size);
+ return (error);
+ }
+
+ if (size > MAXBSIZE)
+ read_size = MAXBSIZE;
+ else
+ read_size = size;
+
+ error = 0;
+ for (i = 0; i < size / MAXBSIZE; i++) {
+ error = bread(fsdev->nd_devvp, btodb(offset + i * read_size),
+ read_size, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ memset(bp->b_data, 0xff, read_size);
+ error = bwrite(bp);
+ if (error) {
+ nandfs_error("%s: err:%d from bwrite\n",
+ __func__, error);
+ return (error);
+ }
+ }
+
+ return (error);
+}
+
+int
+nandfs_vop_islocked(struct vnode *vp)
+{
+ int islocked;
+
+ islocked = VOP_ISLOCKED(vp);
+ return (islocked == LK_EXCLUSIVE || islocked == LK_SHARED);
+}
+
+nandfs_daddr_t
+nandfs_block_to_dblock(struct nandfs_device *fsdev, nandfs_lbn_t block)
+{
+
+ return (btodb(block * fsdev->nd_blocksize));
+}
diff --git a/sys/fs/nandfs/nandfs_subr.h b/sys/fs/nandfs/nandfs_subr.h
new file mode 100644
index 0000000..0bcda18
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_subr.h
@@ -0,0 +1,238 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_subr.h,v 1.1 2009/07/18 16:31:42 reinoud
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FS_NANDFS_NANDFS_SUBR_H_
+#define _FS_NANDFS_NANDFS_SUBR_H_
+
+struct nandfs_mdt;
+
+struct nandfs_alloc_request
+{
+ uint64_t entrynum;
+ struct buf *bp_desc;
+ struct buf *bp_bitmap;
+ struct buf *bp_entry;
+};
+
+/* Segment creation */
+void nandfs_wakeup_wait_sync(struct nandfs_device *, int);
+int nandfs_segment_constructor(struct nandfsmount *, int);
+int nandfs_sync_file(struct vnode *);
+
+/* Basic calculators */
+uint64_t nandfs_get_segnum_of_block(struct nandfs_device *, nandfs_daddr_t);
+void nandfs_get_segment_range(struct nandfs_device *, uint64_t, uint64_t *,
+ uint64_t *);
+void nandfs_calc_mdt_consts(struct nandfs_device *, struct nandfs_mdt *, int);
+
+/* Log reading / volume helpers */
+int nandfs_search_super_root(struct nandfs_device *);
+
+/* Reading */
+int nandfs_dev_bread(struct nandfs_device *, nandfs_daddr_t, struct ucred *,
+ int, struct buf **);
+int nandfs_bread(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
+ struct buf **);
+int nandfs_bread_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
+ struct buf **);
+int nandfs_bdestroy(struct nandfs_node *, nandfs_daddr_t);
+int nandfs_bcreate(struct nandfs_node *, nandfs_lbn_t, struct ucred *, int,
+ struct buf **);
+int nandfs_bcreate_meta(struct nandfs_node *, nandfs_lbn_t, struct ucred *,
+ int, struct buf **);
+int nandfs_bread_create(struct nandfs_node *, nandfs_lbn_t, struct ucred *,
+ int, struct buf **);
+
+/* vtop operations */
+int nandfs_vtop(struct nandfs_node *, nandfs_daddr_t, nandfs_daddr_t *);
+
+/* Node action implementators */
+int nandfs_vinit(struct vnode *, uint64_t);
+int nandfs_get_node(struct nandfsmount *, uint64_t, struct nandfs_node **);
+int nandfs_get_node_raw(struct nandfs_device *, struct nandfsmount *, uint64_t,
+ struct nandfs_inode *, struct nandfs_node **);
+void nandfs_dispose_node(struct nandfs_node **);
+
+void nandfs_itimes(struct vnode *);
+int nandfs_lookup_name_in_dir(struct vnode *, const char *, int, uint64_t *,
+ int *, uint64_t *);
+int nandfs_create_node(struct vnode *, struct vnode **, struct vattr *,
+ struct componentname *);
+void nandfs_delete_node(struct nandfs_node *);
+
+int nandfs_chsize(struct vnode *, u_quad_t, struct ucred *);
+int nandfs_dir_detach(struct nandfsmount *, struct nandfs_node *,
+ struct nandfs_node *, struct componentname *);
+int nandfs_dir_attach(struct nandfsmount *, struct nandfs_node *,
+ struct nandfs_node *, struct vattr *, struct componentname *);
+
+int nandfs_dirty_buf(struct buf *, int);
+int nandfs_dirty_buf_meta(struct buf *, int);
+int nandfs_fs_full(struct nandfs_device *);
+void nandfs_undirty_buf_fsdev(struct nandfs_device *, struct buf *);
+void nandfs_undirty_buf(struct buf *);
+
+void nandfs_clear_buf(struct buf *);
+void nandfs_buf_set(struct buf *, uint32_t);
+void nandfs_buf_clear(struct buf *, uint32_t);
+int nandfs_buf_check(struct buf *, uint32_t);
+
+int nandfs_find_free_entry(struct nandfs_mdt *, struct nandfs_node *,
+ struct nandfs_alloc_request *);
+int nandfs_find_entry(struct nandfs_mdt *, struct nandfs_node *,
+ struct nandfs_alloc_request *);
+int nandfs_alloc_entry(struct nandfs_mdt *, struct nandfs_alloc_request *);
+void nandfs_abort_entry(struct nandfs_alloc_request *);
+int nandfs_free_entry(struct nandfs_mdt *, struct nandfs_alloc_request *);
+int nandfs_get_entry_block(struct nandfs_mdt *, struct nandfs_node *,
+ struct nandfs_alloc_request *, uint32_t *, int);
+
+/* inode managment */
+int nandfs_node_create(struct nandfsmount *, struct nandfs_node **, uint16_t);
+int nandfs_node_destroy(struct nandfs_node *);
+int nandfs_node_update(struct nandfs_node *);
+int nandfs_get_node_entry(struct nandfsmount *, struct nandfs_inode **,
+ uint64_t, struct buf **);
+void nandfs_mdt_trans_blk(struct nandfs_mdt *, uint64_t, uint64_t *,
+ uint64_t *, nandfs_lbn_t *, uint32_t *);
+
+/* vblock management */
+void nandfs_mdt_trans(struct nandfs_mdt *, uint64_t, nandfs_lbn_t *, uint32_t *);
+int nandfs_vblock_alloc(struct nandfs_device *, nandfs_daddr_t *);
+int nandfs_vblock_end(struct nandfs_device *, nandfs_daddr_t);
+int nandfs_vblock_assign(struct nandfs_device *, nandfs_daddr_t,
+ nandfs_lbn_t);
+int nandfs_vblock_free(struct nandfs_device *, nandfs_daddr_t);
+
+/* Checkpoint management */
+int nandfs_get_checkpoint(struct nandfs_device *, struct nandfs_node *,
+ uint64_t);
+int nandfs_set_checkpoint(struct nandfs_device *, struct nandfs_node *,
+ uint64_t, struct nandfs_inode *, uint64_t);
+
+/* Segment management */
+int nandfs_alloc_segment(struct nandfs_device *, uint64_t *);
+int nandfs_update_segment(struct nandfs_device *, uint64_t, uint32_t);
+int nandfs_free_segment(struct nandfs_device *, uint64_t);
+int nandfs_clear_segment(struct nandfs_device *, uint64_t);
+int nandfs_touch_segment(struct nandfs_device *, uint64_t);
+int nandfs_markgc_segment(struct nandfs_device *, uint64_t);
+
+int nandfs_bmap_insert_block(struct nandfs_node *, nandfs_lbn_t, struct buf *);
+int nandfs_bmap_update_block(struct nandfs_node *, struct buf *, nandfs_lbn_t);
+int nandfs_bmap_update_dat(struct nandfs_node *, nandfs_daddr_t, struct buf *);
+int nandfs_bmap_dirty_blocks(struct nandfs_node *, struct buf *, int);
+int nandfs_bmap_truncate_mapping(struct nandfs_node *, nandfs_lbn_t,
+ nandfs_lbn_t);
+int nandfs_bmap_lookup(struct nandfs_node *, nandfs_lbn_t, nandfs_daddr_t *);
+
+/* dirent */
+int nandfs_add_dirent(struct vnode *, uint64_t, char *, long, uint8_t);
+int nandfs_remove_dirent(struct vnode *, struct nandfs_node *,
+ struct componentname *);
+int nandfs_update_dirent(struct vnode *, struct nandfs_node *,
+ struct nandfs_node *);
+int nandfs_init_dir(struct vnode *, uint64_t, uint64_t);
+int nandfs_update_parent_dir(struct vnode *, uint64_t);
+
+void nandfs_vblk_set(struct buf *, nandfs_daddr_t);
+nandfs_daddr_t nandfs_vblk_get(struct buf *);
+
+void nandfs_inode_init(struct nandfs_inode *, uint16_t);
+void nandfs_inode_destroy(struct nandfs_inode *);
+
+/* ioctl */
+int nandfs_get_seg_stat(struct nandfs_device *, struct nandfs_seg_stat *);
+int nandfs_chng_cpmode(struct nandfs_node *, struct nandfs_cpmode *);
+int nandfs_get_cpinfo_ioctl(struct nandfs_node *, struct nandfs_argv *);
+int nandfs_delete_cp(struct nandfs_node *, uint64_t start, uint64_t);
+int nandfs_make_snap(struct nandfs_device *, uint64_t *);
+int nandfs_delete_snap(struct nandfs_device *, uint64_t);
+int nandfs_get_cpstat(struct nandfs_node *, struct nandfs_cpstat *);
+int nandfs_get_segment_info_ioctl(struct nandfs_device *, struct nandfs_argv *);
+int nandfs_get_dat_vinfo_ioctl(struct nandfs_device *, struct nandfs_argv *);
+int nandfs_get_dat_bdescs_ioctl(struct nandfs_device *, struct nandfs_argv *);
+int nandfs_get_fsinfo(struct nandfsmount *, struct nandfs_fsinfo *);
+
+int nandfs_get_cpinfo(struct nandfs_node *, uint64_t, uint16_t,
+ struct nandfs_cpinfo *, uint32_t, uint32_t *);
+
+nandfs_lbn_t nandfs_get_maxfilesize(struct nandfs_device *);
+
+int nandfs_write_superblock(struct nandfs_device *);
+
+extern int nandfs_sync_interval;
+extern int nandfs_max_dirty_segs;
+extern int nandfs_cps_between_sblocks;
+
+struct buf *nandfs_geteblk(int, int);
+
+void nandfs_dirty_bufs_increment(struct nandfs_device *);
+void nandfs_dirty_bufs_decrement(struct nandfs_device *);
+
+int nandfs_start_cleaner(struct nandfs_device *);
+int nandfs_stop_cleaner(struct nandfs_device *);
+
+int nandfs_segsum_valid(struct nandfs_segment_summary *);
+int nandfs_load_segsum(struct nandfs_device *, nandfs_daddr_t,
+ struct nandfs_segment_summary *);
+int nandfs_get_segment_info(struct nandfs_device *, struct nandfs_suinfo *,
+ uint32_t, uint64_t);
+int nandfs_get_segment_info_filter(struct nandfs_device *,
+ struct nandfs_suinfo *, uint32_t, uint64_t, uint64_t *, uint32_t, uint32_t);
+int nandfs_get_dat_vinfo(struct nandfs_device *, struct nandfs_vinfo *,
+ uint32_t);
+int nandfs_get_dat_bdescs(struct nandfs_device *, struct nandfs_bdesc *,
+ uint32_t);
+
+#define NANDFS_VBLK_ASSIGNED 1
+
+#define NANDFS_IS_INDIRECT(bp) ((bp)->b_lblkno < 0)
+
+int nandfs_erase(struct nandfs_device *, off_t, size_t);
+
+#define NANDFS_VOP_ISLOCKED(vp) nandfs_vop_islocked((vp))
+int nandfs_vop_islocked(struct vnode *vp);
+
+nandfs_daddr_t nandfs_block_to_dblock(struct nandfs_device *, nandfs_lbn_t);
+
+#define DEBUG_MODE
+#if defined(DEBUG_MODE)
+#define nandfs_error panic
+#define nandfs_warning printf
+#elif defined(TEST_MODE)
+#define nandfs_error printf
+#define nandfs_warning printf
+#else
+#define nandfs_error(...)
+#define nandfs_warning(...)
+#endif
+
+#endif /* !_FS_NANDFS_NANDFS_SUBR_H_ */
diff --git a/sys/fs/nandfs/nandfs_sufile.c b/sys/fs/nandfs/nandfs_sufile.c
new file mode 100644
index 0000000..d4f4326
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_sufile.c
@@ -0,0 +1,569 @@
+/*-
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+#define SU_USAGE_OFF(bp, offset) \
+ ((struct nandfs_segment_usage *)((bp)->b_data + offset))
+
+static int
+nandfs_seg_usage_blk_offset(struct nandfs_device *fsdev, uint64_t seg,
+ uint64_t *blk, uint64_t *offset)
+{
+ uint64_t off;
+ uint16_t seg_size;
+
+ seg_size = fsdev->nd_fsdata.f_segment_usage_size;
+
+ off = roundup(sizeof(struct nandfs_sufile_header), seg_size);
+ off += (seg * seg_size);
+
+ *blk = off / fsdev->nd_blocksize;
+ *offset = off % fsdev->nd_blocksize;
+ return (0);
+}
+
+/* Alloc new segment */
+int
+nandfs_alloc_segment(struct nandfs_device *fsdev, uint64_t *seg)
+{
+ struct nandfs_node *su_node;
+ struct nandfs_sufile_header *su_header;
+ struct nandfs_segment_usage *su_usage;
+ struct buf *bp_header, *bp;
+ uint64_t blk, vblk, offset, i, rest, nsegments;
+ uint16_t seg_size;
+ int error, found;
+
+ seg_size = fsdev->nd_fsdata.f_segment_usage_size;
+ nsegments = fsdev->nd_fsdata.f_nsegments;
+
+ su_node = fsdev->nd_su_node;
+ ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
+
+ /* Read header buffer */
+ error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
+ if (error) {
+ brelse(bp_header);
+ return (error);
+ }
+
+ su_header = (struct nandfs_sufile_header *)bp_header->b_data;
+
+ /* Get last allocated segment */
+ i = su_header->sh_last_alloc + 1;
+
+ found = 0;
+ bp = NULL;
+ while (!found) {
+ nandfs_seg_usage_blk_offset(fsdev, i, &blk, &offset);
+ if(blk != 0) {
+ error = nandfs_bmap_lookup(su_node, blk, &vblk);
+ if (error) {
+ nandfs_error("%s: cannot find vblk for blk "
+ "blk:%jx\n", __func__, blk);
+ return (error);
+ }
+ if (vblk)
+ error = nandfs_bread(su_node, blk, NOCRED, 0,
+ &bp);
+ else
+ error = nandfs_bcreate(su_node, blk, NOCRED, 0,
+ &bp);
+ if (error) {
+ nandfs_error("%s: cannot create/read "
+ "vblk:%jx\n", __func__, vblk);
+ if (bp)
+ brelse(bp);
+ return (error);
+ }
+
+ su_usage = SU_USAGE_OFF(bp, offset);
+ } else {
+ su_usage = SU_USAGE_OFF(bp_header, offset);
+ bp = bp_header;
+ }
+
+ rest = (fsdev->nd_blocksize - offset) / seg_size;
+ /* Go through all su usage in block */
+ while (rest) {
+ /* When last check start from beggining */
+ if (i == nsegments)
+ break;
+
+ if (!su_usage->su_flags) {
+ su_usage->su_flags = 1;
+ found = 1;
+ break;
+ }
+ su_usage++;
+ i++;
+
+ /* If all checked return error */
+ if (i == su_header->sh_last_alloc) {
+ DPRINTF(SEG, ("%s: cannot allocate segment \n",
+ __func__));
+ brelse(bp_header);
+ if (blk != 0)
+ brelse(bp);
+ return (1);
+ }
+ rest--;
+ }
+ if (!found) {
+ /* Otherwise read another block */
+ if (blk != 0)
+ brelse(bp);
+ if (i == nsegments) {
+ blk = 0;
+ i = 0;
+ } else
+ blk++;
+ offset = 0;
+ }
+ }
+
+ if (found) {
+ *seg = i;
+ su_header->sh_last_alloc = i;
+ su_header->sh_ncleansegs--;
+ su_header->sh_ndirtysegs++;
+
+ fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
+ fsdev->nd_fsdata.f_blocks_per_segment;
+ fsdev->nd_clean_segs--;
+
+ /*
+ * It is mostly called from syncer() so we want to force
+ * making buf dirty.
+ */
+ error = nandfs_dirty_buf(bp_header, 1);
+ if (error) {
+ if (bp && bp != bp_header)
+ brelse(bp);
+ return (error);
+ }
+ if (bp && bp != bp_header)
+ nandfs_dirty_buf(bp, 1);
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)i));
+
+ return (0);
+ }
+
+ DPRINTF(SEG, ("%s: failed\n", __func__));
+
+ return (1);
+}
+
+/*
+ * Make buffer dirty, it will be updated soon but first it need to be
+ * gathered by syncer.
+ */
+int
+nandfs_touch_segment(struct nandfs_device *fsdev, uint64_t seg)
+{
+ struct nandfs_node *su_node;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ su_node = fsdev->nd_su_node;
+ ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
+
+ nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
+
+ error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ nandfs_error("%s: cannot preallocate new segment\n", __func__);
+ return (error);
+ } else
+ nandfs_dirty_buf(bp, 1);
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
+ return (error);
+}
+
+/* Update block count of segment */
+int
+nandfs_update_segment(struct nandfs_device *fsdev, uint64_t seg, uint32_t nblks)
+{
+ struct nandfs_node *su_node;
+ struct nandfs_segment_usage *su_usage;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ su_node = fsdev->nd_su_node;
+ ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
+
+ nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
+
+ error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ nandfs_error("%s: read block:%jx to update\n",
+ __func__, blk);
+ brelse(bp);
+ return (error);
+ }
+
+ su_usage = SU_USAGE_OFF(bp, offset);
+ su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
+ su_usage->su_flags = NANDFS_SEGMENT_USAGE_DIRTY;
+ su_usage->su_nblocks += nblks;
+
+ DPRINTF(SEG, ("%s: seg:%#jx inc:%#x cur:%#x\n", __func__,
+ (uintmax_t)seg, nblks, su_usage->su_nblocks));
+
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+/* Make segment free */
+int
+nandfs_free_segment(struct nandfs_device *fsdev, uint64_t seg)
+{
+ struct nandfs_node *su_node;
+ struct nandfs_sufile_header *su_header;
+ struct nandfs_segment_usage *su_usage;
+ struct buf *bp_header, *bp;
+ uint64_t blk, offset;
+ int error;
+
+ su_node = fsdev->nd_su_node;
+ ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
+
+ /* Read su header */
+ error = nandfs_bread(su_node, 0, NOCRED, 0, &bp_header);
+ if (error) {
+ brelse(bp_header);
+ return (error);
+ }
+
+ su_header = (struct nandfs_sufile_header *)bp_header->b_data;
+ nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
+
+ /* Read su usage block if other than su header block */
+ if (blk != 0) {
+ error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ brelse(bp_header);
+ return (error);
+ }
+ } else
+ bp = bp_header;
+
+ /* Reset su usage data */
+ su_usage = SU_USAGE_OFF(bp, offset);
+ su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
+ su_usage->su_nblocks = 0;
+ su_usage->su_flags = 0;
+
+ /* Update clean/dirty counter in header */
+ su_header->sh_ncleansegs++;
+ su_header->sh_ndirtysegs--;
+
+ /*
+ * Make buffers dirty, called by cleaner
+ * so force dirty even if no much space left
+ * on device
+ */
+ nandfs_dirty_buf(bp_header, 1);
+ if (bp != bp_header)
+ nandfs_dirty_buf(bp, 1);
+
+ /* Update free block count */
+ fsdev->nd_super.s_free_blocks_count = su_header->sh_ncleansegs *
+ fsdev->nd_fsdata.f_blocks_per_segment;
+ fsdev->nd_clean_segs++;
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
+
+ return (0);
+}
+
+static int
+nandfs_bad_segment(struct nandfs_device *fsdev, uint64_t seg)
+{
+ struct nandfs_node *su_node;
+ struct nandfs_segment_usage *su_usage;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ su_node = fsdev->nd_su_node;
+ ASSERT_VOP_LOCKED(NTOV(su_node), __func__);
+
+ nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
+
+ error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ su_usage = SU_USAGE_OFF(bp, offset);
+ su_usage->su_lastmod = fsdev->nd_ts.tv_sec;
+ su_usage->su_flags = NANDFS_SEGMENT_USAGE_ERROR;
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
+
+ nandfs_dirty_buf(bp, 1);
+
+ return (0);
+}
+
+int
+nandfs_markgc_segment(struct nandfs_device *fsdev, uint64_t seg)
+{
+ struct nandfs_node *su_node;
+ struct nandfs_segment_usage *su_usage;
+ struct buf *bp;
+ uint64_t blk, offset;
+ int error;
+
+ su_node = fsdev->nd_su_node;
+
+ VOP_LOCK(NTOV(su_node), LK_EXCLUSIVE);
+
+ nandfs_seg_usage_blk_offset(fsdev, seg, &blk, &offset);
+
+ error = nandfs_bread(su_node, blk, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(su_node), 0);
+ return (error);
+ }
+
+ su_usage = SU_USAGE_OFF(bp, offset);
+ MPASS((su_usage->su_flags & NANDFS_SEGMENT_USAGE_GC) == 0);
+ su_usage->su_flags |= NANDFS_SEGMENT_USAGE_GC;
+
+ brelse(bp);
+ VOP_UNLOCK(NTOV(su_node), 0);
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
+
+ return (0);
+}
+
+int
+nandfs_clear_segment(struct nandfs_device *fsdev, uint64_t seg)
+{
+ uint64_t offset, segsize;
+ uint32_t bps, bsize;
+ int error = 0;
+
+ bps = fsdev->nd_fsdata.f_blocks_per_segment;
+ bsize = fsdev->nd_blocksize;
+ segsize = bsize * bps;
+ nandfs_get_segment_range(fsdev, seg, &offset, NULL);
+ offset *= bsize;
+
+ DPRINTF(SEG, ("%s: seg:%#jx\n", __func__, (uintmax_t)seg));
+
+ /* Erase it and mark it bad when fail */
+ if (nandfs_erase(fsdev, offset, segsize))
+ error = nandfs_bad_segment(fsdev, seg);
+
+ if (error)
+ return (error);
+
+ /* Mark it free */
+ error = nandfs_free_segment(fsdev, seg);
+
+ return (error);
+}
+
+int
+nandfs_get_seg_stat(struct nandfs_device *nandfsdev,
+ struct nandfs_seg_stat *nss)
+{
+ struct nandfs_sufile_header *suhdr;
+ struct nandfs_node *su_node;
+ struct buf *bp;
+ int err;
+
+ su_node = nandfsdev->nd_su_node;
+
+ NANDFS_WRITELOCK(nandfsdev);
+ VOP_LOCK(NTOV(su_node), LK_SHARED);
+ err = nandfs_bread(nandfsdev->nd_su_node, 0, NOCRED, 0, &bp);
+ if (err) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(su_node), 0);
+ NANDFS_WRITEUNLOCK(nandfsdev);
+ return (-1);
+ }
+
+ suhdr = (struct nandfs_sufile_header *)bp->b_data;
+ nss->nss_nsegs = nandfsdev->nd_fsdata.f_nsegments;
+ nss->nss_ncleansegs = suhdr->sh_ncleansegs;
+ nss->nss_ndirtysegs = suhdr->sh_ndirtysegs;
+ nss->nss_ctime = 0;
+ nss->nss_nongc_ctime = nandfsdev->nd_ts.tv_sec;
+ nss->nss_prot_seq = nandfsdev->nd_seg_sequence;
+
+ brelse(bp);
+ VOP_UNLOCK(NTOV(su_node), 0);
+
+ NANDFS_WRITEUNLOCK(nandfsdev);
+
+ return (0);
+}
+
+int
+nandfs_get_segment_info_ioctl(struct nandfs_device *fsdev,
+ struct nandfs_argv *nargv)
+{
+ struct nandfs_suinfo *nsi;
+ int error;
+
+ if (nargv->nv_nmembs > NANDFS_SEGMENTS_MAX)
+ return (EINVAL);
+
+ nsi = malloc(sizeof(struct nandfs_suinfo) * nargv->nv_nmembs,
+ M_NANDFSTEMP, M_WAITOK | M_ZERO);
+
+ error = nandfs_get_segment_info(fsdev, nsi, nargv->nv_nmembs,
+ nargv->nv_index);
+
+ if (error == 0)
+ error = copyout(nsi, (void *)(uintptr_t)nargv->nv_base,
+ sizeof(struct nandfs_suinfo) * nargv->nv_nmembs);
+
+ free(nsi, M_NANDFSTEMP);
+ return (error);
+}
+
+int
+nandfs_get_segment_info(struct nandfs_device *fsdev, struct nandfs_suinfo *nsi,
+ uint32_t nmembs, uint64_t segment)
+{
+
+ return (nandfs_get_segment_info_filter(fsdev, nsi, nmembs, segment,
+ NULL, 0, 0));
+}
+
+int
+nandfs_get_segment_info_filter(struct nandfs_device *fsdev,
+ struct nandfs_suinfo *nsi, uint32_t nmembs, uint64_t segment,
+ uint64_t *nsegs, uint32_t filter, uint32_t nfilter)
+{
+ struct nandfs_segment_usage *su;
+ struct nandfs_node *su_node;
+ struct buf *bp;
+ uint64_t curr, blocknr, blockoff, i;
+ uint32_t flags;
+ int err = 0;
+
+ curr = ~(0);
+
+ lockmgr(&fsdev->nd_seg_const, LK_EXCLUSIVE, NULL);
+ su_node = fsdev->nd_su_node;
+
+ VOP_LOCK(NTOV(su_node), LK_SHARED);
+
+ bp = NULL;
+ if (nsegs != NULL)
+ *nsegs = 0;
+ for (i = 0; i < nmembs; segment++) {
+ if (segment == fsdev->nd_fsdata.f_nsegments)
+ break;
+
+ nandfs_seg_usage_blk_offset(fsdev, segment, &blocknr,
+ &blockoff);
+
+ if (i == 0 || curr != blocknr) {
+ if (bp != NULL)
+ brelse(bp);
+ err = nandfs_bread(su_node, blocknr, NOCRED,
+ 0, &bp);
+ if (err) {
+ goto out;
+ }
+ curr = blocknr;
+ }
+
+ su = SU_USAGE_OFF(bp, blockoff);
+ flags = su->su_flags;
+ if (segment == fsdev->nd_seg_num ||
+ segment == fsdev->nd_next_seg_num)
+ flags |= NANDFS_SEGMENT_USAGE_ACTIVE;
+
+ if (nfilter != 0 && (flags & nfilter) != 0)
+ continue;
+ if (filter != 0 && (flags & filter) == 0)
+ continue;
+
+ nsi->nsi_num = segment;
+ nsi->nsi_lastmod = su->su_lastmod;
+ nsi->nsi_blocks = su->su_nblocks;
+ nsi->nsi_flags = flags;
+ nsi++;
+ i++;
+ if (nsegs != NULL)
+ (*nsegs)++;
+ }
+
+out:
+ if (bp != NULL)
+ brelse(bp);
+ VOP_UNLOCK(NTOV(su_node), 0);
+ lockmgr(&fsdev->nd_seg_const, LK_RELEASE, NULL);
+
+ return (err);
+}
diff --git a/sys/fs/nandfs/nandfs_vfsops.c b/sys/fs/nandfs/nandfs_vfsops.c
new file mode 100644
index 0000000..ba53546
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_vfsops.c
@@ -0,0 +1,1590 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_vfsops.c,v 1.1 2009/07/18 16:31:42 reinoud Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/fcntl.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/priv.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/sysctl.h>
+#include <sys/libkern.h>
+
+#include <geom/geom.h>
+#include <geom/geom_vfs.h>
+
+#include <machine/_inttypes.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+static MALLOC_DEFINE(M_NANDFSMNT, "nandfs_mount", "NANDFS mount structure");
+
+#define NANDFS_SET_SYSTEMFILE(vp) { \
+ (vp)->v_vflag |= VV_SYSTEM; \
+ vref(vp); \
+ vput(vp); }
+
+#define NANDFS_UNSET_SYSTEMFILE(vp) { \
+ VOP_LOCK(vp, LK_EXCLUSIVE); \
+ MPASS(vp->v_bufobj.bo_dirty.bv_cnt == 0); \
+ (vp)->v_vflag &= ~VV_SYSTEM; \
+ vgone(vp); \
+ vput(vp); }
+
+/* Globals */
+struct _nandfs_devices nandfs_devices;
+
+/* Parameters */
+int nandfs_verbose = 0;
+
+static void
+nandfs_tunable_init(void *arg)
+{
+
+ TUNABLE_INT_FETCH("vfs.nandfs.verbose", &nandfs_verbose);
+}
+SYSINIT(nandfs_tunables, SI_SUB_VFS, SI_ORDER_ANY, nandfs_tunable_init, NULL);
+
+static SYSCTL_NODE(_vfs, OID_AUTO, nandfs, CTLFLAG_RD, 0, "NAND filesystem");
+static SYSCTL_NODE(_vfs_nandfs, OID_AUTO, mount, CTLFLAG_RD, 0,
+ "NANDFS mountpoints");
+SYSCTL_INT(_vfs_nandfs, OID_AUTO, verbose, CTLFLAG_RW, &nandfs_verbose, 0, "");
+
+#define NANDFS_CONSTR_INTERVAL 5
+int nandfs_sync_interval = NANDFS_CONSTR_INTERVAL; /* sync every 5 seconds */
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, sync_interval, CTLFLAG_RW,
+ &nandfs_sync_interval, 0, "");
+
+#define NANDFS_MAX_DIRTY_SEGS 5
+int nandfs_max_dirty_segs = NANDFS_MAX_DIRTY_SEGS; /* sync when 5 dirty seg */
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, max_dirty_segs, CTLFLAG_RW,
+ &nandfs_max_dirty_segs, 0, "");
+
+#define NANDFS_CPS_BETWEEN_SBLOCKS 5
+int nandfs_cps_between_sblocks = NANDFS_CPS_BETWEEN_SBLOCKS; /* write superblock every 5 checkpoints */
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cps_between_sblocks, CTLFLAG_RW,
+ &nandfs_cps_between_sblocks, 0, "");
+
+#define NANDFS_CLEANER_ENABLE 1
+int nandfs_cleaner_enable = NANDFS_CLEANER_ENABLE;
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_enable, CTLFLAG_RW,
+ &nandfs_cleaner_enable, 0, "");
+
+#define NANDFS_CLEANER_INTERVAL 5
+int nandfs_cleaner_interval = NANDFS_CLEANER_INTERVAL;
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_interval, CTLFLAG_RW,
+ &nandfs_cleaner_interval, 0, "");
+
+#define NANDFS_CLEANER_SEGMENTS 5
+int nandfs_cleaner_segments = NANDFS_CLEANER_SEGMENTS;
+SYSCTL_UINT(_vfs_nandfs, OID_AUTO, cleaner_segments, CTLFLAG_RW,
+ &nandfs_cleaner_segments, 0, "");
+
+static int nandfs_mountfs(struct vnode *devvp, struct mount *mp);
+static vfs_mount_t nandfs_mount;
+static vfs_root_t nandfs_root;
+static vfs_statfs_t nandfs_statfs;
+static vfs_unmount_t nandfs_unmount;
+static vfs_vget_t nandfs_vget;
+static vfs_sync_t nandfs_sync;
+static const char *nandfs_opts[] = {
+ "snap", "from", "noatime", NULL
+};
+
+/* System nodes */
+static int
+nandfs_create_system_nodes(struct nandfs_device *nandfsdev)
+{
+ int error;
+
+ error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_DAT_INO,
+ &nandfsdev->nd_super_root.sr_dat, &nandfsdev->nd_dat_node);
+ if (error)
+ goto errorout;
+
+ error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_CPFILE_INO,
+ &nandfsdev->nd_super_root.sr_cpfile, &nandfsdev->nd_cp_node);
+ if (error)
+ goto errorout;
+
+ error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_SUFILE_INO,
+ &nandfsdev->nd_super_root.sr_sufile, &nandfsdev->nd_su_node);
+ if (error)
+ goto errorout;
+
+ error = nandfs_get_node_raw(nandfsdev, NULL, NANDFS_GC_INO,
+ NULL, &nandfsdev->nd_gc_node);
+ if (error)
+ goto errorout;
+
+ NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node));
+ NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node));
+ NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node));
+ NANDFS_SET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node));
+
+ DPRINTF(VOLUMES, ("System vnodes: dat: %p cp: %p su: %p\n",
+ NTOV(nandfsdev->nd_dat_node), NTOV(nandfsdev->nd_cp_node),
+ NTOV(nandfsdev->nd_su_node)));
+ return (0);
+
+errorout:
+ nandfs_dispose_node(&nandfsdev->nd_gc_node);
+ nandfs_dispose_node(&nandfsdev->nd_dat_node);
+ nandfs_dispose_node(&nandfsdev->nd_cp_node);
+ nandfs_dispose_node(&nandfsdev->nd_su_node);
+
+ return (error);
+}
+
+static void
+nandfs_release_system_nodes(struct nandfs_device *nandfsdev)
+{
+
+ if (!nandfsdev)
+ return;
+ if (nandfsdev->nd_refcnt > 0)
+ return;
+
+ if (nandfsdev->nd_gc_node)
+ NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_gc_node));
+ if (nandfsdev->nd_dat_node)
+ NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_dat_node));
+ if (nandfsdev->nd_cp_node)
+ NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_cp_node));
+ if (nandfsdev->nd_su_node)
+ NANDFS_UNSET_SYSTEMFILE(NTOV(nandfsdev->nd_su_node));
+}
+
+static int
+nandfs_check_fsdata_crc(struct nandfs_fsdata *fsdata)
+{
+ uint32_t fsdata_crc, comp_crc;
+
+ if (fsdata->f_magic != NANDFS_FSDATA_MAGIC)
+ return (0);
+
+ /* Preserve CRC */
+ fsdata_crc = fsdata->f_sum;
+
+ /* Calculate */
+ fsdata->f_sum = (0);
+ comp_crc = crc32((uint8_t *)fsdata, fsdata->f_bytes);
+
+ /* Restore */
+ fsdata->f_sum = fsdata_crc;
+
+ /* Check CRC */
+ return (fsdata_crc == comp_crc);
+}
+
+static int
+nandfs_check_superblock_crc(struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ uint32_t super_crc, comp_crc;
+
+ /* Check super block magic */
+ if (super->s_magic != NANDFS_SUPER_MAGIC)
+ return (0);
+
+ /* Preserve CRC */
+ super_crc = super->s_sum;
+
+ /* Calculate */
+ super->s_sum = (0);
+ comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes);
+
+ /* Restore */
+ super->s_sum = super_crc;
+
+ /* Check CRC */
+ return (super_crc == comp_crc);
+}
+
+static void
+nandfs_calc_superblock_crc(struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ uint32_t comp_crc;
+
+ /* Calculate */
+ super->s_sum = 0;
+ comp_crc = crc32((uint8_t *)super, fsdata->f_sbbytes);
+
+ /* Restore */
+ super->s_sum = comp_crc;
+}
+
+static int
+nandfs_is_empty(u_char *area, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ if (area[i] != 0xff)
+ return (0);
+
+ return (1);
+}
+
+static __inline int
+nandfs_sblocks_in_esize(struct nandfs_device *fsdev)
+{
+
+ return ((fsdev->nd_erasesize - NANDFS_SBLOCK_OFFSET_BYTES) /
+ sizeof(struct nandfs_super_block));
+}
+
+static __inline int
+nandfs_max_sblocks(struct nandfs_device *fsdev)
+{
+
+ return (NANDFS_NFSAREAS * nandfs_sblocks_in_esize(fsdev));
+}
+
+static __inline int
+nandfs_sblocks_in_block(struct nandfs_device *fsdev)
+{
+
+ return (fsdev->nd_devblocksize / sizeof(struct nandfs_super_block));
+}
+
+static __inline int
+nandfs_sblocks_in_first_block(struct nandfs_device *fsdev)
+{
+ int n;
+
+ n = nandfs_sblocks_in_block(fsdev) -
+ NANDFS_SBLOCK_OFFSET_BYTES / sizeof(struct nandfs_super_block);
+ if (n < 0)
+ n = 0;
+
+ return (n);
+}
+
+static int
+nandfs_write_superblock_at(struct nandfs_device *fsdev,
+ struct nandfs_fsarea *fstp)
+{
+ struct nandfs_super_block *super, *supert;
+ struct buf *bp;
+ int sb_per_sector, sbs_in_fsd, read_block;
+ int index, pos, error;
+ off_t offset;
+
+ DPRINTF(SYNC, ("%s: last_used %d nandfs_sblocks_in_esize %d\n",
+ __func__, fstp->last_used, nandfs_sblocks_in_esize(fsdev)));
+ if (fstp->last_used == nandfs_sblocks_in_esize(fsdev) - 1)
+ index = 0;
+ else
+ index = fstp->last_used + 1;
+
+ super = &fsdev->nd_super;
+ supert = NULL;
+
+ sb_per_sector = nandfs_sblocks_in_block(fsdev);
+ sbs_in_fsd = sizeof(struct nandfs_fsdata) /
+ sizeof(struct nandfs_super_block);
+ index += sbs_in_fsd;
+ offset = fstp->offset;
+
+ DPRINTF(SYNC, ("%s: offset %#jx s_last_pseg %#jx s_last_cno %#jx "
+ "s_last_seq %#jx wtime %jd index %d\n", __func__, offset,
+ super->s_last_pseg, super->s_last_cno, super->s_last_seq,
+ super->s_wtime, index));
+
+ read_block = btodb(offset + ((index / sb_per_sector) * sb_per_sector)
+ * sizeof(struct nandfs_super_block));
+
+ DPRINTF(SYNC, ("%s: read_block %#x\n", __func__, read_block));
+
+ if (index == sbs_in_fsd) {
+ error = nandfs_erase(fsdev, offset, fsdev->nd_erasesize);
+ if (error)
+ return (error);
+
+ error = bread(fsdev->nd_devvp, btodb(offset),
+ fsdev->nd_devblocksize, NOCRED, &bp);
+ if (error) {
+ printf("NANDFS: couldn't read initial data: %d\n",
+ error);
+ brelse(bp);
+ return (error);
+ }
+ memcpy(bp->b_data, &fsdev->nd_fsdata, sizeof(fsdev->nd_fsdata));
+ /*
+ * 0xff-out the rest. This bp could be cached, so potentially
+ * b_data contains stale super blocks.
+ *
+ * We don't mind cached bp since most of the time we just add
+ * super blocks to already 0xff-out b_data and don't need to
+ * perform actual read.
+ */
+ if (fsdev->nd_devblocksize > sizeof(fsdev->nd_fsdata))
+ memset(bp->b_data + sizeof(fsdev->nd_fsdata), 0xff,
+ fsdev->nd_devblocksize - sizeof(fsdev->nd_fsdata));
+ error = bwrite(bp);
+ if (error) {
+ printf("NANDFS: cannot rewrite initial data at %jx\n",
+ offset);
+ return (error);
+ }
+ }
+
+ error = bread(fsdev->nd_devvp, read_block, fsdev->nd_devblocksize,
+ NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+
+ supert = (struct nandfs_super_block *)(bp->b_data);
+ pos = index % sb_per_sector;
+
+ DPRINTF(SYNC, ("%s: storing at %d\n", __func__, pos));
+ memcpy(&supert[pos], super, sizeof(struct nandfs_super_block));
+
+ /*
+ * See comment above in code that performs erase.
+ */
+ if (pos == 0)
+ memset(&supert[1], 0xff,
+ (sb_per_sector - 1) * sizeof(struct nandfs_super_block));
+
+ error = bwrite(bp);
+ if (error) {
+ printf("NANDFS: cannot update superblock at %jx\n", offset);
+ return (error);
+ }
+
+ DPRINTF(SYNC, ("%s: fstp->last_used %d -> %d\n", __func__,
+ fstp->last_used, index - sbs_in_fsd));
+ fstp->last_used = index - sbs_in_fsd;
+
+ return (0);
+}
+
+int
+nandfs_write_superblock(struct nandfs_device *fsdev)
+{
+ struct nandfs_super_block *super;
+ struct timespec ts;
+ int error;
+ int i, j;
+
+ vfs_timestamp(&ts);
+
+ super = &fsdev->nd_super;
+
+ super->s_last_pseg = fsdev->nd_last_pseg;
+ super->s_last_cno = fsdev->nd_last_cno;
+ super->s_last_seq = fsdev->nd_seg_sequence;
+ super->s_wtime = ts.tv_sec;
+
+ nandfs_calc_superblock_crc(&fsdev->nd_fsdata, super);
+
+ error = 0;
+ for (i = 0, j = fsdev->nd_last_fsarea; i < NANDFS_NFSAREAS;
+ i++, j = (j + 1 % NANDFS_NFSAREAS)) {
+ if (fsdev->nd_fsarea[j].flags & NANDFS_FSSTOR_FAILED) {
+ DPRINTF(SYNC, ("%s: skipping %d\n", __func__, j));
+ continue;
+ }
+ error = nandfs_write_superblock_at(fsdev, &fsdev->nd_fsarea[j]);
+ if (error) {
+ printf("NANDFS: writing superblock at offset %d failed:"
+ "%d\n", j * fsdev->nd_erasesize, error);
+ fsdev->nd_fsarea[j].flags |= NANDFS_FSSTOR_FAILED;
+ } else
+ break;
+ }
+
+ if (i == NANDFS_NFSAREAS) {
+ printf("NANDFS: superblock was not written\n");
+ /*
+ * TODO: switch to read-only?
+ */
+ return (error);
+ } else
+ fsdev->nd_last_fsarea = (j + 1) % NANDFS_NFSAREAS;
+
+ return (0);
+}
+
+static int
+nandfs_select_fsdata(struct nandfs_device *fsdev,
+ struct nandfs_fsdata *fsdatat, struct nandfs_fsdata **fsdata, int nfsds)
+{
+ int i;
+
+ *fsdata = NULL;
+ for (i = 0; i < nfsds; i++) {
+ DPRINTF(VOLUMES, ("%s: i %d f_magic %x f_crc %x\n", __func__,
+ i, fsdatat[i].f_magic, fsdatat[i].f_sum));
+ if (!nandfs_check_fsdata_crc(&fsdatat[i]))
+ continue;
+ *fsdata = &fsdatat[i];
+ break;
+ }
+
+ return (*fsdata != NULL ? 0 : EINVAL);
+}
+
+static int
+nandfs_select_sb(struct nandfs_device *fsdev,
+ struct nandfs_super_block *supert, struct nandfs_super_block **super,
+ int nsbs)
+{
+ int i;
+
+ *super = NULL;
+ for (i = 0; i < nsbs; i++) {
+ if (!nandfs_check_superblock_crc(&fsdev->nd_fsdata, &supert[i]))
+ continue;
+ DPRINTF(SYNC, ("%s: i %d s_last_cno %jx s_magic %x "
+ "s_wtime %jd\n", __func__, i, supert[i].s_last_cno,
+ supert[i].s_magic, supert[i].s_wtime));
+ if (*super == NULL || supert[i].s_last_cno >
+ (*super)->s_last_cno)
+ *super = &supert[i];
+ }
+
+ return (*super != NULL ? 0 : EINVAL);
+}
+
+static int
+nandfs_read_structures_at(struct nandfs_device *fsdev,
+ struct nandfs_fsarea *fstp, struct nandfs_fsdata *fsdata,
+ struct nandfs_super_block *super)
+{
+ struct nandfs_super_block *tsuper, *tsuperd;
+ struct buf *bp;
+ int error, read_size;
+ int i;
+ int offset;
+
+ offset = fstp->offset;
+
+ if (fsdev->nd_erasesize > MAXBSIZE)
+ read_size = MAXBSIZE;
+ else
+ read_size = fsdev->nd_erasesize;
+
+ error = bread(fsdev->nd_devvp, btodb(offset), read_size, NOCRED, &bp);
+ if (error) {
+ printf("couldn't read: %d\n", error);
+ brelse(bp);
+ fstp->flags |= NANDFS_FSSTOR_FAILED;
+ return (error);
+ }
+
+ tsuper = super;
+
+ memcpy(fsdata, bp->b_data, sizeof(struct nandfs_fsdata));
+ memcpy(tsuper, (bp->b_data + sizeof(struct nandfs_fsdata)),
+ read_size - sizeof(struct nandfs_fsdata));
+ brelse(bp);
+
+ tsuper += (read_size - sizeof(struct nandfs_fsdata)) /
+ sizeof(struct nandfs_super_block);
+
+ for (i = 1; i < fsdev->nd_erasesize / read_size; i++) {
+ error = bread(fsdev->nd_devvp, btodb(offset + i * read_size),
+ read_size, NOCRED, &bp);
+ if (error) {
+ printf("couldn't read: %d\n", error);
+ brelse(bp);
+ fstp->flags |= NANDFS_FSSTOR_FAILED;
+ return (error);
+ }
+ memcpy(tsuper, bp->b_data, read_size);
+ tsuper += read_size / sizeof(struct nandfs_super_block);
+ brelse(bp);
+ }
+
+ tsuper -= 1;
+ fstp->last_used = nandfs_sblocks_in_esize(fsdev) - 1;
+ for (tsuperd = super - 1; (tsuper != tsuperd); tsuper -= 1) {
+ if (nandfs_is_empty((u_char *)tsuper, sizeof(*tsuper)))
+ fstp->last_used--;
+ else
+ break;
+ }
+
+ DPRINTF(VOLUMES, ("%s: last_used %d\n", __func__, fstp->last_used));
+
+ return (0);
+}
+
+static int
+nandfs_read_structures(struct nandfs_device *fsdev)
+{
+ struct nandfs_fsdata *fsdata, *fsdatat;
+ struct nandfs_super_block *sblocks, *ssblock;
+ int nsbs, nfsds, i;
+ int error = 0;
+ int nrsbs;
+
+ nfsds = NANDFS_NFSAREAS;
+ nsbs = nandfs_max_sblocks(fsdev);
+
+ fsdatat = malloc(sizeof(struct nandfs_fsdata) * nfsds, M_NANDFSTEMP,
+ M_WAITOK | M_ZERO);
+ sblocks = malloc(sizeof(struct nandfs_super_block) * nsbs, M_NANDFSTEMP,
+ M_WAITOK | M_ZERO);
+
+ nrsbs = 0;
+ for (i = 0; i < NANDFS_NFSAREAS; i++) {
+ fsdev->nd_fsarea[i].offset = i * fsdev->nd_erasesize;
+ error = nandfs_read_structures_at(fsdev, &fsdev->nd_fsarea[i],
+ &fsdatat[i], sblocks + nrsbs);
+ if (error)
+ continue;
+ nrsbs += (fsdev->nd_fsarea[i].last_used + 1);
+ if (fsdev->nd_fsarea[fsdev->nd_last_fsarea].last_used >
+ fsdev->nd_fsarea[i].last_used)
+ fsdev->nd_last_fsarea = i;
+ }
+
+ if (nrsbs == 0) {
+ printf("nandfs: no valid superblocks found\n");
+ error = EINVAL;
+ goto out;
+ }
+
+ error = nandfs_select_fsdata(fsdev, fsdatat, &fsdata, nfsds);
+ if (error)
+ goto out;
+ memcpy(&fsdev->nd_fsdata, fsdata, sizeof(struct nandfs_fsdata));
+
+ error = nandfs_select_sb(fsdev, sblocks, &ssblock, nsbs);
+ if (error)
+ goto out;
+
+ memcpy(&fsdev->nd_super, ssblock, sizeof(struct nandfs_super_block));
+out:
+ free(fsdatat, M_NANDFSTEMP);
+ free(sblocks, M_NANDFSTEMP);
+
+ if (error == 0)
+ DPRINTF(VOLUMES, ("%s: selected sb with w_time %jd "
+ "last_pseg %#jx\n", __func__, fsdev->nd_super.s_wtime,
+ fsdev->nd_super.s_last_pseg));
+
+ return (error);
+}
+
+static void
+nandfs_unmount_base(struct nandfs_device *nandfsdev)
+{
+ int error;
+
+ if (!nandfsdev)
+ return;
+
+ /* Remove all our information */
+ error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0);
+ if (error) {
+ /*
+ * Flushing buffers failed when fs was umounting, can't do
+ * much now, just printf error and continue with umount.
+ */
+ nandfs_error("%s(): error:%d when umounting FS\n",
+ __func__, error);
+ }
+
+ /* Release the device's system nodes */
+ nandfs_release_system_nodes(nandfsdev);
+}
+
+static void
+nandfs_get_ncleanseg(struct nandfs_device *nandfsdev)
+{
+ struct nandfs_seg_stat nss;
+
+ nandfs_get_seg_stat(nandfsdev, &nss);
+ nandfsdev->nd_clean_segs = nss.nss_ncleansegs;
+ DPRINTF(VOLUMES, ("nandfs_mount: clean segs: %jx\n",
+ (uintmax_t)nandfsdev->nd_clean_segs));
+}
+
+
+static int
+nandfs_mount_base(struct nandfs_device *nandfsdev, struct mount *mp,
+ struct nandfs_args *args)
+{
+ uint32_t log_blocksize;
+ int error;
+
+ /* Flush out any old buffers remaining from a previous use. */
+ if ((error = vinvalbuf(nandfsdev->nd_devvp, V_SAVE, 0, 0)))
+ return (error);
+
+ error = nandfs_read_structures(nandfsdev);
+ if (error) {
+ printf("nandfs: could not get valid filesystem structures\n");
+ return (error);
+ }
+
+ if (nandfsdev->nd_fsdata.f_rev_level != NANDFS_CURRENT_REV) {
+ printf("nandfs: unsupported file system revision: %d "
+ "(supported is %d).\n", nandfsdev->nd_fsdata.f_rev_level,
+ NANDFS_CURRENT_REV);
+ return (EINVAL);
+ }
+
+ if (nandfsdev->nd_fsdata.f_erasesize != nandfsdev->nd_erasesize) {
+ printf("nandfs: erasesize mismatch (device %#x, fs %#x)\n",
+ nandfsdev->nd_erasesize, nandfsdev->nd_fsdata.f_erasesize);
+ return (EINVAL);
+ }
+
+ /* Get our blocksize */
+ log_blocksize = nandfsdev->nd_fsdata.f_log_block_size;
+ nandfsdev->nd_blocksize = (uint64_t) 1 << (log_blocksize + 10);
+ DPRINTF(VOLUMES, ("%s: blocksize:%x\n", __func__,
+ nandfsdev->nd_blocksize));
+
+ DPRINTF(VOLUMES, ("%s: accepted super block with cp %#jx\n", __func__,
+ (uintmax_t)nandfsdev->nd_super.s_last_cno));
+
+ /* Calculate dat structure parameters */
+ nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_dat_mdt,
+ nandfsdev->nd_fsdata.f_dat_entry_size);
+ nandfs_calc_mdt_consts(nandfsdev, &nandfsdev->nd_ifile_mdt,
+ nandfsdev->nd_fsdata.f_inode_size);
+
+ /* Search for the super root and roll forward when needed */
+ if (nandfs_search_super_root(nandfsdev)) {
+ printf("Cannot find valid SuperRoot\n");
+ return (EINVAL);
+ }
+
+ nandfsdev->nd_mount_state = nandfsdev->nd_super.s_state;
+ if (nandfsdev->nd_mount_state != NANDFS_VALID_FS) {
+ printf("FS is seriously damaged, needs repairing\n");
+ printf("aborting mount\n");
+ return (EINVAL);
+ }
+
+ /*
+ * FS should be ok now. The superblock and the last segsum could be
+ * updated from the repair so extract running values again.
+ */
+ nandfsdev->nd_last_pseg = nandfsdev->nd_super.s_last_pseg;
+ nandfsdev->nd_seg_sequence = nandfsdev->nd_super.s_last_seq;
+ nandfsdev->nd_seg_num = nandfs_get_segnum_of_block(nandfsdev,
+ nandfsdev->nd_last_pseg);
+ nandfsdev->nd_next_seg_num = nandfs_get_segnum_of_block(nandfsdev,
+ nandfsdev->nd_last_segsum.ss_next);
+ nandfsdev->nd_ts.tv_sec = nandfsdev->nd_last_segsum.ss_create;
+ nandfsdev->nd_last_cno = nandfsdev->nd_super.s_last_cno;
+ nandfsdev->nd_fakevblk = 1;
+ nandfsdev->nd_last_ino = NANDFS_USER_INO;
+ DPRINTF(VOLUMES, ("%s: last_pseg %#jx last_cno %#jx last_seq %#jx\n"
+ "fsdev: last_seg: seq %#jx num %#jx, next_seg_num %#jx\n",
+ __func__, (uintmax_t)nandfsdev->nd_last_pseg,
+ (uintmax_t)nandfsdev->nd_last_cno,
+ (uintmax_t)nandfsdev->nd_seg_sequence,
+ (uintmax_t)nandfsdev->nd_seg_sequence,
+ (uintmax_t)nandfsdev->nd_seg_num,
+ (uintmax_t)nandfsdev->nd_next_seg_num));
+
+ DPRINTF(VOLUMES, ("nandfs_mount: accepted super root\n"));
+
+ /* Create system vnodes for DAT, CP and SEGSUM */
+ error = nandfs_create_system_nodes(nandfsdev);
+ if (error)
+ nandfs_unmount_base(nandfsdev);
+
+ nandfs_get_ncleanseg(nandfsdev);
+
+ return (error);
+}
+
+static void
+nandfs_unmount_device(struct nandfs_device *nandfsdev)
+{
+
+ /* Is there anything? */
+ if (nandfsdev == NULL)
+ return;
+
+ /* Remove the device only if we're the last reference */
+ nandfsdev->nd_refcnt--;
+ if (nandfsdev->nd_refcnt >= 1)
+ return;
+
+ MPASS(nandfsdev->nd_syncer == NULL);
+ MPASS(nandfsdev->nd_cleaner == NULL);
+ MPASS(nandfsdev->nd_free_base == NULL);
+
+ /* Unmount our base */
+ nandfs_unmount_base(nandfsdev);
+
+ /* Remove from our device list */
+ SLIST_REMOVE(&nandfs_devices, nandfsdev, nandfs_device, nd_next_device);
+
+ DROP_GIANT();
+ g_topology_lock();
+ g_vfs_close(nandfsdev->nd_gconsumer);
+ g_topology_unlock();
+ PICKUP_GIANT();
+
+ DPRINTF(VOLUMES, ("closing device\n"));
+
+ /* Clear our mount reference and release device node */
+ vrele(nandfsdev->nd_devvp);
+
+ dev_rel(nandfsdev->nd_devvp->v_rdev);
+
+ /* Free our device info */
+ cv_destroy(&nandfsdev->nd_sync_cv);
+ mtx_destroy(&nandfsdev->nd_sync_mtx);
+ cv_destroy(&nandfsdev->nd_clean_cv);
+ mtx_destroy(&nandfsdev->nd_clean_mtx);
+ mtx_destroy(&nandfsdev->nd_mutex);
+ lockdestroy(&nandfsdev->nd_seg_const);
+ free(nandfsdev, M_NANDFSMNT);
+}
+
+static int
+nandfs_check_mounts(struct nandfs_device *nandfsdev, struct mount *mp,
+ struct nandfs_args *args)
+{
+ struct nandfsmount *nmp;
+ uint64_t last_cno;
+
+ /* no double-mounting of the same checkpoint */
+ STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
+ if (nmp->nm_mount_args.cpno == args->cpno)
+ return (EBUSY);
+ }
+
+ /* Allow readonly mounts without questioning here */
+ if (mp->mnt_flag & MNT_RDONLY)
+ return (0);
+
+ /* Read/write mount */
+ STAILQ_FOREACH(nmp, &nandfsdev->nd_mounts, nm_next_mount) {
+ /* Only one RW mount on this device! */
+ if ((nmp->nm_vfs_mountp->mnt_flag & MNT_RDONLY)==0)
+ return (EROFS);
+ /* RDONLY on last mountpoint is device busy */
+ last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno;
+ if (nmp->nm_mount_args.cpno == last_cno)
+ return (EBUSY);
+ }
+
+ /* OK for now */
+ return (0);
+}
+
+static int
+nandfs_mount_device(struct vnode *devvp, struct mount *mp,
+ struct nandfs_args *args, struct nandfs_device **nandfsdev_p)
+{
+ struct nandfs_device *nandfsdev;
+ struct g_provider *pp;
+ struct g_consumer *cp;
+ struct cdev *dev;
+ uint32_t erasesize;
+ int error, size;
+ int ronly;
+
+ DPRINTF(VOLUMES, ("Mounting NANDFS device\n"));
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+
+ /* Look up device in our nandfs_mountpoints */
+ *nandfsdev_p = NULL;
+ SLIST_FOREACH(nandfsdev, &nandfs_devices, nd_next_device)
+ if (nandfsdev->nd_devvp == devvp)
+ break;
+
+ if (nandfsdev) {
+ DPRINTF(VOLUMES, ("device already mounted\n"));
+ error = nandfs_check_mounts(nandfsdev, mp, args);
+ if (error)
+ return error;
+ nandfsdev->nd_refcnt++;
+ *nandfsdev_p = nandfsdev;
+
+ if (!ronly) {
+ DROP_GIANT();
+ g_topology_lock();
+ error = g_access(nandfsdev->nd_gconsumer, 0, 1, 0);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ }
+ return (error);
+ }
+
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+ dev = devvp->v_rdev;
+ dev_ref(dev);
+ DROP_GIANT();
+ g_topology_lock();
+ error = g_vfs_open(devvp, &cp, "nandfs", ronly ? 0 : 1);
+ pp = g_dev_getprovider(dev);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ VOP_UNLOCK(devvp, 0);
+ if (error) {
+ dev_rel(dev);
+ return (error);
+ }
+
+ nandfsdev = malloc(sizeof(struct nandfs_device), M_NANDFSMNT, M_WAITOK | M_ZERO);
+
+ /* Initialise */
+ nandfsdev->nd_refcnt = 1;
+ nandfsdev->nd_devvp = devvp;
+ nandfsdev->nd_syncing = 0;
+ nandfsdev->nd_cleaning = 0;
+ nandfsdev->nd_gconsumer = cp;
+ cv_init(&nandfsdev->nd_sync_cv, "nandfssync");
+ mtx_init(&nandfsdev->nd_sync_mtx, "nffssyncmtx", NULL, MTX_DEF);
+ cv_init(&nandfsdev->nd_clean_cv, "nandfsclean");
+ mtx_init(&nandfsdev->nd_clean_mtx, "nffscleanmtx", NULL, MTX_DEF);
+ mtx_init(&nandfsdev->nd_mutex, "nandfsdev lock", NULL, MTX_DEF);
+ lockinit(&nandfsdev->nd_seg_const, PVFS, "nffssegcon", VLKTIMEOUT,
+ LK_CANRECURSE);
+ STAILQ_INIT(&nandfsdev->nd_mounts);
+
+ nandfsdev->nd_devsize = pp->mediasize;
+ nandfsdev->nd_devblocksize = pp->sectorsize;
+
+ size = sizeof(erasesize);
+ error = g_io_getattr("NAND::blocksize", nandfsdev->nd_gconsumer, &size,
+ &erasesize);
+ if (error) {
+ DPRINTF(VOLUMES, ("couldn't get erasesize: %d\n", error));
+
+ if (error == ENOIOCTL || error == EOPNOTSUPP) {
+ /*
+ * We conclude that this is not NAND storage
+ */
+ nandfsdev->nd_erasesize = NANDFS_DEF_ERASESIZE;
+ nandfsdev->nd_is_nand = 0;
+ } else {
+ DROP_GIANT();
+ g_topology_lock();
+ g_vfs_close(nandfsdev->nd_gconsumer);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ dev_rel(dev);
+ free(nandfsdev, M_NANDFSMNT);
+ return (error);
+ }
+ } else {
+ nandfsdev->nd_erasesize = erasesize;
+ nandfsdev->nd_is_nand = 1;
+ }
+
+ DPRINTF(VOLUMES, ("%s: erasesize %x\n", __func__,
+ nandfsdev->nd_erasesize));
+
+ /* Register nandfs_device in list */
+ SLIST_INSERT_HEAD(&nandfs_devices, nandfsdev, nd_next_device);
+
+ error = nandfs_mount_base(nandfsdev, mp, args);
+ if (error) {
+ /* Remove all our information */
+ nandfs_unmount_device(nandfsdev);
+ return (EINVAL);
+ }
+
+ nandfsdev->nd_maxfilesize = nandfs_get_maxfilesize(nandfsdev);
+
+ *nandfsdev_p = nandfsdev;
+ DPRINTF(VOLUMES, ("NANDFS device mounted ok\n"));
+
+ return (0);
+}
+
+static int
+nandfs_mount_checkpoint(struct nandfsmount *nmp)
+{
+ struct nandfs_cpfile_header *cphdr;
+ struct nandfs_checkpoint *cp;
+ struct nandfs_inode ifile_inode;
+ struct nandfs_node *cp_node;
+ struct buf *bp;
+ uint64_t ncp, nsn, cpno, fcpno, blocknr, last_cno;
+ uint32_t off, dlen;
+ int cp_per_block, error;
+
+ cpno = nmp->nm_mount_args.cpno;
+ if (cpno == 0)
+ cpno = nmp->nm_nandfsdev->nd_super.s_last_cno;
+
+ DPRINTF(VOLUMES, ("%s: trying to mount checkpoint number %"PRIu64"\n",
+ __func__, cpno));
+
+ cp_node = nmp->nm_nandfsdev->nd_cp_node;
+
+ VOP_LOCK(NTOV(cp_node), LK_SHARED);
+ /* Get cpfile header from 1st block of cp file */
+ error = nandfs_bread(cp_node, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (error);
+ }
+
+ cphdr = (struct nandfs_cpfile_header *) bp->b_data;
+ ncp = cphdr->ch_ncheckpoints;
+ nsn = cphdr->ch_nsnapshots;
+
+ brelse(bp);
+
+ DPRINTF(VOLUMES, ("mount_nandfs: checkpoint header read in\n"));
+ DPRINTF(VOLUMES, ("\tNumber of checkpoints %"PRIu64"\n", ncp));
+ DPRINTF(VOLUMES, ("\tNumber of snapshots %"PRIu64"\n", nsn));
+
+ /* Read in our specified checkpoint */
+ dlen = nmp->nm_nandfsdev->nd_fsdata.f_checkpoint_size;
+ cp_per_block = nmp->nm_nandfsdev->nd_blocksize / dlen;
+
+ fcpno = cpno + NANDFS_CPFILE_FIRST_CHECKPOINT_OFFSET - 1;
+ blocknr = fcpno / cp_per_block;
+ off = (fcpno % cp_per_block) * dlen;
+ error = nandfs_bread(cp_node, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ printf("mount_nandfs: couldn't read cp block %"PRIu64"\n",
+ fcpno);
+ return (EINVAL);
+ }
+
+ /* Needs to be a valid checkpoint */
+ cp = (struct nandfs_checkpoint *) ((uint8_t *) bp->b_data + off);
+ if (cp->cp_flags & NANDFS_CHECKPOINT_INVALID) {
+ printf("mount_nandfs: checkpoint marked invalid\n");
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (EINVAL);
+ }
+
+ /* Is this really the checkpoint we want? */
+ if (cp->cp_cno != cpno) {
+ printf("mount_nandfs: checkpoint file corrupt? "
+ "expected cpno %"PRIu64", found cpno %"PRIu64"\n",
+ cpno, cp->cp_cno);
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (EINVAL);
+ }
+
+ /* Check if it's a snapshot ! */
+ last_cno = nmp->nm_nandfsdev->nd_super.s_last_cno;
+ if (cpno != last_cno) {
+ /* Only allow snapshots if not mounting on the last cp */
+ if ((cp->cp_flags & NANDFS_CHECKPOINT_SNAPSHOT) == 0) {
+ printf( "mount_nandfs: checkpoint %"PRIu64" is not a "
+ "snapshot\n", cpno);
+ brelse(bp);
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (EINVAL);
+ }
+ }
+
+ ifile_inode = cp->cp_ifile_inode;
+ brelse(bp);
+
+ /* Get ifile inode */
+ error = nandfs_get_node_raw(nmp->nm_nandfsdev, NULL, NANDFS_IFILE_INO,
+ &ifile_inode, &nmp->nm_ifile_node);
+ if (error) {
+ printf("mount_nandfs: can't read ifile node\n");
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ return (EINVAL);
+ }
+
+ NANDFS_SET_SYSTEMFILE(NTOV(nmp->nm_ifile_node));
+ VOP_UNLOCK(NTOV(cp_node), 0);
+ /* Get root node? */
+
+ return (0);
+}
+
+static void
+free_nandfs_mountinfo(struct mount *mp)
+{
+ struct nandfsmount *nmp = VFSTONANDFS(mp);
+
+ if (nmp == NULL)
+ return;
+
+ free(nmp, M_NANDFSMNT);
+}
+
+void
+nandfs_wakeup_wait_sync(struct nandfs_device *nffsdev, int reason)
+{
+ char *reasons[] = {
+ "umount",
+ "vfssync",
+ "bdflush",
+ "fforce",
+ "fsync",
+ "ro_upd"
+ };
+
+ DPRINTF(SYNC, ("%s: %s\n", __func__, reasons[reason]));
+ mtx_lock(&nffsdev->nd_sync_mtx);
+ if (nffsdev->nd_syncing)
+ cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx);
+ if (reason == SYNCER_UMOUNT)
+ nffsdev->nd_syncer_exit = 1;
+ nffsdev->nd_syncing = 1;
+ wakeup(&nffsdev->nd_syncing);
+ cv_wait(&nffsdev->nd_sync_cv, &nffsdev->nd_sync_mtx);
+
+ mtx_unlock(&nffsdev->nd_sync_mtx);
+}
+
+static void
+nandfs_gc_finished(struct nandfs_device *nffsdev, int exit)
+{
+ int error;
+
+ mtx_lock(&nffsdev->nd_sync_mtx);
+ nffsdev->nd_syncing = 0;
+ DPRINTF(SYNC, ("%s: cleaner finish\n", __func__));
+ cv_broadcast(&nffsdev->nd_sync_cv);
+ mtx_unlock(&nffsdev->nd_sync_mtx);
+ if (!exit) {
+ error = tsleep(&nffsdev->nd_syncing, PRIBIO, "-",
+ hz * nandfs_sync_interval);
+ DPRINTF(SYNC, ("%s: cleaner waked up: %d\n",
+ __func__, error));
+ }
+}
+
+static void
+nandfs_syncer(struct nandfsmount *nmp)
+{
+ struct nandfs_device *nffsdev;
+ struct mount *mp;
+ int flags, error;
+
+ mp = nmp->nm_vfs_mountp;
+ nffsdev = nmp->nm_nandfsdev;
+ tsleep(&nffsdev->nd_syncing, PRIBIO, "-", hz * nandfs_sync_interval);
+
+ while (!nffsdev->nd_syncer_exit) {
+ DPRINTF(SYNC, ("%s: syncer run\n", __func__));
+ nffsdev->nd_syncing = 1;
+
+ flags = (nmp->nm_flags & (NANDFS_FORCE_SYNCER | NANDFS_UMOUNT));
+
+ error = nandfs_segment_constructor(nmp, flags);
+ if (error)
+ nandfs_error("%s: error:%d when creating segments\n",
+ __func__, error);
+
+ nmp->nm_flags &= ~flags;
+
+ nandfs_gc_finished(nffsdev, 0);
+ }
+
+ MPASS(nffsdev->nd_cleaner == NULL);
+ error = nandfs_segment_constructor(nmp,
+ NANDFS_FORCE_SYNCER | NANDFS_UMOUNT);
+ if (error)
+ nandfs_error("%s: error:%d when creating segments\n",
+ __func__, error);
+ nandfs_gc_finished(nffsdev, 1);
+ nffsdev->nd_syncer = NULL;
+ MPASS(nffsdev->nd_free_base == NULL);
+
+ DPRINTF(SYNC, ("%s: exiting\n", __func__));
+ kthread_exit();
+}
+
+static int
+start_syncer(struct nandfsmount *nmp)
+{
+ int error;
+
+ MPASS(nmp->nm_nandfsdev->nd_syncer == NULL);
+
+ DPRINTF(SYNC, ("%s: start syncer\n", __func__));
+
+ nmp->nm_nandfsdev->nd_syncer_exit = 0;
+
+ error = kthread_add((void(*)(void *))nandfs_syncer, nmp, NULL,
+ &nmp->nm_nandfsdev->nd_syncer, 0, 0, "nandfs_syncer");
+
+ if (error)
+ printf("nandfs: could not start syncer: %d\n", error);
+
+ return (error);
+}
+
+static int
+stop_syncer(struct nandfsmount *nmp)
+{
+
+ MPASS(nmp->nm_nandfsdev->nd_syncer != NULL);
+
+ nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_UMOUNT);
+
+ DPRINTF(SYNC, ("%s: stop syncer\n", __func__));
+ return (0);
+}
+
+/*
+ * Mount null layer
+ */
+static int
+nandfs_mount(struct mount *mp)
+{
+ struct nandfsmount *nmp;
+ struct vnode *devvp;
+ struct nameidata nd;
+ struct vfsoptlist *opts;
+ struct thread *td;
+ char *from;
+ int error = 0, flags;
+
+ DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp));
+
+ td = curthread;
+ opts = mp->mnt_optnew;
+
+ if (vfs_filteropt(opts, nandfs_opts))
+ return (EINVAL);
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ nmp = VFSTONANDFS(mp);
+ if (vfs_flagopt(mp->mnt_optnew, "export", NULL, 0)) {
+ return (error);
+ }
+ if (!(nmp->nm_ronly) && vfs_flagopt(opts, "ro", NULL, 0)) {
+ vn_start_write(NULL, &mp, V_WAIT);
+ error = VFS_SYNC(mp, MNT_WAIT);
+ if (error)
+ return (error);
+ vn_finished_write(mp);
+
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ nandfs_wakeup_wait_sync(nmp->nm_nandfsdev,
+ SYNCER_ROUPD);
+ error = vflush(mp, 0, flags, td);
+ if (error)
+ return (error);
+
+ nandfs_stop_cleaner(nmp->nm_nandfsdev);
+ stop_syncer(nmp);
+ DROP_GIANT();
+ g_topology_lock();
+ g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1, 0);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= MNT_RDONLY;
+ MNT_IUNLOCK(mp);
+ nmp->nm_ronly = 1;
+
+ } else if ((nmp->nm_ronly) &&
+ !vfs_flagopt(opts, "ro", NULL, 0)) {
+ /*
+ * Don't allow read-write snapshots.
+ */
+ if (nmp->nm_mount_args.cpno != 0)
+ return (EROFS);
+ /*
+ * If upgrade to read-write by non-root, then verify
+ * that user has necessary permissions on the device.
+ */
+ devvp = nmp->nm_nandfsdev->nd_devvp;
+ vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
+ error = VOP_ACCESS(devvp, VREAD | VWRITE,
+ td->td_ucred, td);
+ if (error) {
+ error = priv_check(td, PRIV_VFS_MOUNT_PERM);
+ if (error) {
+ VOP_UNLOCK(devvp, 0);
+ return (error);
+ }
+ }
+
+ VOP_UNLOCK(devvp, 0);
+ DROP_GIANT();
+ g_topology_lock();
+ error = g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, 1,
+ 0);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ if (error)
+ return (error);
+
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~MNT_RDONLY;
+ MNT_IUNLOCK(mp);
+ error = start_syncer(nmp);
+ if (error == 0)
+ error = nandfs_start_cleaner(nmp->nm_nandfsdev);
+ if (error) {
+ DROP_GIANT();
+ g_topology_lock();
+ g_access(nmp->nm_nandfsdev->nd_gconsumer, 0, -1,
+ 0);
+ g_topology_unlock();
+ PICKUP_GIANT();
+ return (error);
+ }
+
+ nmp->nm_ronly = 0;
+ }
+ return (0);
+ }
+
+ from = vfs_getopts(opts, "from", &error);
+ if (error)
+ return (error);
+
+ /*
+ * Find device node
+ */
+ NDINIT(&nd, LOOKUP, FOLLOW|LOCKLEAF, UIO_SYSSPACE, from, curthread);
+ error = namei(&nd);
+ if (error)
+ return (error);
+ NDFREE(&nd, NDF_ONLY_PNBUF);
+
+ devvp = nd.ni_vp;
+
+ if (!vn_isdisk(devvp, &error)) {
+ vput(devvp);
+ return (error);
+ }
+
+ /* Check the access rights on the mount device */
+ error = VOP_ACCESS(devvp, VREAD, curthread->td_ucred, curthread);
+ if (error)
+ error = priv_check(curthread, PRIV_VFS_MOUNT_PERM);
+ if (error) {
+ vput(devvp);
+ return (error);
+ }
+
+ vfs_getnewfsid(mp);
+
+ error = nandfs_mountfs(devvp, mp);
+ if (error)
+ return (error);
+ vfs_mountedfrom(mp, from);
+
+ return (0);
+}
+
+static int
+nandfs_mountfs(struct vnode *devvp, struct mount *mp)
+{
+ struct nandfsmount *nmp = NULL;
+ struct nandfs_args *args = NULL;
+ struct nandfs_device *nandfsdev;
+ char *from;
+ int error, ronly;
+ char *cpno;
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+
+ if (devvp->v_rdev->si_iosize_max != 0)
+ mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
+ VOP_UNLOCK(devvp, 0);
+
+ if (mp->mnt_iosize_max > MAXPHYS)
+ mp->mnt_iosize_max = MAXPHYS;
+
+ from = vfs_getopts(mp->mnt_optnew, "from", &error);
+ if (error)
+ goto error;
+
+ error = vfs_getopt(mp->mnt_optnew, "snap", (void **)&cpno, NULL);
+ if (error == ENOENT)
+ cpno = NULL;
+ else if (error)
+ goto error;
+
+ args = (struct nandfs_args *)malloc(sizeof(struct nandfs_args),
+ M_NANDFSMNT, M_WAITOK | M_ZERO);
+
+ if (cpno != NULL)
+ args->cpno = strtoul(cpno, (char **)NULL, 10);
+ else
+ args->cpno = 0;
+ args->fspec = from;
+
+ if (args->cpno != 0 && !ronly) {
+ error = EROFS;
+ goto error;
+ }
+
+ printf("WARNING: NANDFS is considered to be a highly experimental "
+ "feature in FreeBSD.\n");
+
+ error = nandfs_mount_device(devvp, mp, args, &nandfsdev);
+ if (error)
+ goto error;
+
+ nmp = (struct nandfsmount *) malloc(sizeof(struct nandfsmount),
+ M_NANDFSMNT, M_WAITOK | M_ZERO);
+
+ mp->mnt_data = nmp;
+ nmp->nm_vfs_mountp = mp;
+ nmp->nm_ronly = ronly;
+ MNT_ILOCK(mp);
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_kern_flag |= MNTK_MPSAFE;
+ MNT_IUNLOCK(mp);
+ nmp->nm_nandfsdev = nandfsdev;
+ /* Add our mountpoint */
+ STAILQ_INSERT_TAIL(&nandfsdev->nd_mounts, nmp, nm_next_mount);
+
+ if (args->cpno > nandfsdev->nd_last_cno) {
+ printf("WARNING: supplied checkpoint number (%jd) is greater "
+ "than last known checkpoint on filesystem (%jd). Mounting"
+ " checkpoint %jd\n", (uintmax_t)args->cpno,
+ (uintmax_t)nandfsdev->nd_last_cno,
+ (uintmax_t)nandfsdev->nd_last_cno);
+ args->cpno = nandfsdev->nd_last_cno;
+ }
+
+ /* Setting up other parameters */
+ nmp->nm_mount_args = *args;
+ free(args, M_NANDFSMNT);
+ error = nandfs_mount_checkpoint(nmp);
+ if (error) {
+ nandfs_unmount(mp, MNT_FORCE);
+ goto unmounted;
+ }
+
+ if (!ronly) {
+ error = start_syncer(nmp);
+ if (error == 0)
+ error = nandfs_start_cleaner(nmp->nm_nandfsdev);
+ if (error)
+ nandfs_unmount(mp, MNT_FORCE);
+ }
+
+ return (0);
+
+error:
+ if (args != NULL)
+ free(args, M_NANDFSMNT);
+
+ if (nmp != NULL) {
+ free(nmp, M_NANDFSMNT);
+ mp->mnt_data = NULL;
+ }
+unmounted:
+ return (error);
+}
+
+static int
+nandfs_unmount(struct mount *mp, int mntflags)
+{
+ struct nandfs_device *nandfsdev;
+ struct nandfsmount *nmp;
+ int error;
+ int flags = 0;
+
+ DPRINTF(VOLUMES, ("%s: mp = %p\n", __func__, (void *)mp));
+
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+
+ nmp = mp->mnt_data;
+ nandfsdev = nmp->nm_nandfsdev;
+
+ error = vflush(mp, 0, flags | SKIPSYSTEM, curthread);
+ if (error)
+ return (error);
+
+ if (!(nmp->nm_ronly)) {
+ nandfs_stop_cleaner(nandfsdev);
+ stop_syncer(nmp);
+ }
+
+ if (nmp->nm_ifile_node)
+ NANDFS_UNSET_SYSTEMFILE(NTOV(nmp->nm_ifile_node));
+
+ /* Remove our mount point */
+ STAILQ_REMOVE(&nandfsdev->nd_mounts, nmp, nandfsmount, nm_next_mount);
+
+ /* Unmount the device itself when we're the last one */
+ nandfs_unmount_device(nandfsdev);
+
+ free_nandfs_mountinfo(mp);
+
+ /*
+ * Finally, throw away the null_mount structure
+ */
+ mp->mnt_data = 0;
+ MNT_ILOCK(mp);
+ mp->mnt_flag &= ~MNT_LOCAL;
+ MNT_IUNLOCK(mp);
+
+ return (0);
+}
+
+static int
+nandfs_statfs(struct mount *mp, struct statfs *sbp)
+{
+ struct nandfsmount *nmp;
+ struct nandfs_device *nandfsdev;
+ struct nandfs_fsdata *fsdata;
+ struct nandfs_super_block *sb;
+ struct nandfs_block_group_desc *groups;
+ struct nandfs_node *ifile;
+ struct nandfs_mdt *mdt;
+ struct buf *bp;
+ int i, error;
+ uint32_t entries_per_group;
+ uint64_t files = 0;
+
+ nmp = mp->mnt_data;
+ nandfsdev = nmp->nm_nandfsdev;
+ fsdata = &nandfsdev->nd_fsdata;
+ sb = &nandfsdev->nd_super;
+ ifile = nmp->nm_ifile_node;
+ mdt = &nandfsdev->nd_ifile_mdt;
+ entries_per_group = mdt->entries_per_group;
+
+ VOP_LOCK(NTOV(ifile), LK_SHARED);
+ error = nandfs_bread(ifile, 0, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ VOP_UNLOCK(NTOV(ifile), 0);
+ return (error);
+ }
+
+ groups = (struct nandfs_block_group_desc *)bp->b_data;
+
+ for (i = 0; i < mdt->groups_per_desc_block; i++)
+ files += (entries_per_group - groups[i].bg_nfrees);
+
+ brelse(bp);
+ VOP_UNLOCK(NTOV(ifile), 0);
+
+ sbp->f_bsize = nandfsdev->nd_blocksize;
+ sbp->f_iosize = sbp->f_bsize;
+ sbp->f_blocks = fsdata->f_blocks_per_segment * fsdata->f_nsegments;
+ sbp->f_bfree = sb->s_free_blocks_count;
+ sbp->f_bavail = sbp->f_bfree;
+ sbp->f_files = files;
+ sbp->f_ffree = 0;
+ return (0);
+}
+
+static int
+nandfs_root(struct mount *mp, int flags, struct vnode **vpp)
+{
+ struct nandfsmount *nmp = VFSTONANDFS(mp);
+ struct nandfs_node *node;
+ int error;
+
+ error = nandfs_get_node(nmp, NANDFS_ROOT_INO, &node);
+ if (error)
+ return (error);
+
+ KASSERT(NTOV(node)->v_vflag & VV_ROOT,
+ ("root_vp->v_vflag & VV_ROOT"));
+
+ *vpp = NTOV(node);
+
+ return (error);
+}
+
+static int
+nandfs_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp)
+{
+ struct nandfsmount *nmp = VFSTONANDFS(mp);
+ struct nandfs_node *node;
+ int error;
+
+ error = nandfs_get_node(nmp, ino, &node);
+ if (node)
+ *vpp = NTOV(node);
+
+ return (error);
+}
+
+static int
+nandfs_sync(struct mount *mp, int waitfor)
+{
+ struct nandfsmount *nmp = VFSTONANDFS(mp);
+
+ DPRINTF(SYNC, ("%s: mp %p waitfor %d\n", __func__, mp, waitfor));
+
+ /*
+ * XXX: A hack to be removed soon
+ */
+ if (waitfor == MNT_LAZY)
+ return (0);
+ if (waitfor == MNT_SUSPEND)
+ return (0);
+ nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_VFS_SYNC);
+ return (0);
+}
+
+static struct vfsops nandfs_vfsops = {
+ .vfs_init = nandfs_init,
+ .vfs_mount = nandfs_mount,
+ .vfs_root = nandfs_root,
+ .vfs_statfs = nandfs_statfs,
+ .vfs_uninit = nandfs_uninit,
+ .vfs_unmount = nandfs_unmount,
+ .vfs_vget = nandfs_vget,
+ .vfs_sync = nandfs_sync,
+};
+
+VFS_SET(nandfs_vfsops, nandfs, VFCF_LOOPBACK);
diff --git a/sys/fs/nandfs/nandfs_vnops.c b/sys/fs/nandfs/nandfs_vnops.c
new file mode 100644
index 0000000..b226d30
--- /dev/null
+++ b/sys/fs/nandfs/nandfs_vnops.c
@@ -0,0 +1,2455 @@
+/*-
+ * Copyright (c) 2010-2012 Semihalf
+ * Copyright (c) 2008, 2009 Reinoud Zandijk
+ * 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 ``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 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: NetBSD: nilfs_vnops.c,v 1.2 2009/08/26 03:40:48 elad
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/lockf.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/mutex.h>
+#include <sys/namei.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <sys/bio.h>
+#include <sys/fcntl.h>
+#include <sys/dirent.h>
+#include <sys/stat.h>
+#include <sys/priv.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_object.h>
+#include <vm/vnode_pager.h>
+
+#include <machine/_inttypes.h>
+
+#include <fs/nandfs/nandfs_mount.h>
+#include <fs/nandfs/nandfs.h>
+#include <fs/nandfs/nandfs_subr.h>
+
+extern uma_zone_t nandfs_node_zone;
+static void nandfs_read_filebuf(struct nandfs_node *, struct buf *);
+static void nandfs_itimes_locked(struct vnode *);
+static int nandfs_truncate(struct vnode *, uint64_t);
+
+static vop_pathconf_t nandfs_pathconf;
+
+#define UPDATE_CLOSE 0
+#define UPDATE_WAIT 0
+
+static int
+nandfs_inactive(struct vop_inactive_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ int error = 0;
+
+ DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, node));
+
+ if (node == NULL) {
+ DPRINTF(NODE, ("%s: inactive NULL node\n", __func__));
+ return (0);
+ }
+
+ if (node->nn_inode.i_mode != 0 && !(node->nn_inode.i_links_count)) {
+ nandfs_truncate(vp, 0);
+ error = nandfs_node_destroy(node);
+ if (error)
+ nandfs_error("%s: destroy node: %p\n", __func__, node);
+ node->nn_flags = 0;
+ vrecycle(vp);
+ }
+
+ return (error);
+}
+
+static int
+nandfs_reclaim(struct vop_reclaim_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *nandfs_node = VTON(vp);
+ struct nandfs_device *fsdev = nandfs_node->nn_nandfsdev;
+ uint64_t ino = nandfs_node->nn_ino;
+
+ DPRINTF(VNCALL, ("%s: vp:%p node:%p\n", __func__, vp, nandfs_node));
+
+ /* Invalidate all entries to a particular vnode. */
+ cache_purge(vp);
+
+ /* Destroy the vm object and flush associated pages. */
+ vnode_destroy_vobject(vp);
+
+ /* Remove from vfs hash if not system vnode */
+ if (!NANDFS_SYS_NODE(nandfs_node->nn_ino))
+ vfs_hash_remove(vp);
+
+ /* Dispose all node knowledge */
+ nandfs_dispose_node(&nandfs_node);
+
+ if (!NANDFS_SYS_NODE(ino))
+ NANDFS_WRITEUNLOCK(fsdev);
+
+ return (0);
+}
+
+static int
+nandfs_read(struct vop_read_args *ap)
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct nandfs_node *node = VTON(vp);
+ struct nandfs_device *nandfsdev = node->nn_nandfsdev;
+ struct uio *uio = ap->a_uio;
+ struct buf *bp;
+ uint64_t size;
+ uint32_t blocksize;
+ off_t bytesinfile;
+ ssize_t toread, off;
+ daddr_t lbn;
+ ssize_t resid;
+ int error = 0;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ size = node->nn_inode.i_size;
+ if (uio->uio_offset >= size)
+ return (0);
+
+ blocksize = nandfsdev->nd_blocksize;
+ bytesinfile = size - uio->uio_offset;
+
+ resid = omin(uio->uio_resid, bytesinfile);
+
+ while (resid) {
+ lbn = uio->uio_offset / blocksize;
+ off = uio->uio_offset & (blocksize - 1);
+
+ toread = omin(resid, blocksize - off);
+
+ DPRINTF(READ, ("nandfs_read bn: 0x%jx toread: 0x%zx (0x%x)\n",
+ (uintmax_t)lbn, toread, blocksize));
+
+ error = nandfs_bread(node, lbn, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ break;
+ }
+
+ error = uiomove(bp->b_data + off, toread, uio);
+ if (error) {
+ brelse(bp);
+ break;
+ }
+
+ brelse(bp);
+ resid -= toread;
+ }
+
+ return (error);
+}
+
+static int
+nandfs_write(struct vop_write_args *ap)
+{
+ struct nandfs_device *fsdev;
+ struct nandfs_node *node;
+ struct vnode *vp;
+ struct uio *uio;
+ struct buf *bp;
+ uint64_t file_size, vblk;
+ uint32_t blocksize;
+ ssize_t towrite, off;
+ daddr_t lbn;
+ ssize_t resid;
+ int error, ioflag, modified;
+
+ vp = ap->a_vp;
+ uio = ap->a_uio;
+ ioflag = ap->a_ioflag;
+ node = VTON(vp);
+ fsdev = node->nn_nandfsdev;
+
+ if (nandfs_fs_full(fsdev))
+ return (ENOSPC);
+
+ DPRINTF(WRITE, ("nandfs_write called %#zx at %#jx\n",
+ uio->uio_resid, (uintmax_t)uio->uio_offset));
+
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ if (uio->uio_resid == 0)
+ return (0);
+
+ blocksize = fsdev->nd_blocksize;
+ file_size = node->nn_inode.i_size;
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = file_size;
+ break;
+ case VDIR:
+ return (EISDIR);
+ case VLNK:
+ break;
+ default:
+ panic("%s: bad file type vp: %p", __func__, vp);
+ }
+
+ /* If explicitly asked to append, uio_offset can be wrong? */
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = file_size;
+
+ resid = uio->uio_resid;
+ modified = error = 0;
+
+ while (uio->uio_resid) {
+ lbn = uio->uio_offset / blocksize;
+ off = uio->uio_offset & (blocksize - 1);
+
+ towrite = omin(uio->uio_resid, blocksize - off);
+
+ DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x)\n",
+ __func__, (uintmax_t)lbn, towrite, blocksize));
+
+ error = nandfs_bmap_lookup(node, lbn, &vblk);
+ if (error)
+ break;
+
+ DPRINTF(WRITE, ("%s: lbn: 0x%jd toread: 0x%zx (0x%x) "
+ "vblk=%jx\n", __func__, (uintmax_t)lbn, towrite, blocksize,
+ vblk));
+
+ if (vblk != 0)
+ error = nandfs_bread(node, lbn, NOCRED, 0, &bp);
+ else
+ error = nandfs_bcreate(node, lbn, NOCRED, 0, &bp);
+
+ DPRINTF(WRITE, ("%s: vp %p bread bp %p lbn %#jx\n", __func__,
+ vp, bp, (uintmax_t)lbn));
+ if (error) {
+ if (bp)
+ brelse(bp);
+ break;
+ }
+
+ error = uiomove((char *)bp->b_data + off, (int)towrite, uio);
+ if (error)
+ break;
+
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ break;
+
+ modified++;
+ }
+
+ /* XXX proper handling when only part of file was properly written */
+ if (modified) {
+ if (resid > uio->uio_resid && ap->a_cred &&
+ ap->a_cred->cr_uid != 0)
+ node->nn_inode.i_mode &= ~(ISUID | ISGID);
+
+ if (file_size < uio->uio_offset + uio->uio_resid) {
+ node->nn_inode.i_size = uio->uio_offset +
+ uio->uio_resid;
+ node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ vnode_pager_setsize(vp, uio->uio_offset +
+ uio->uio_resid);
+ nandfs_itimes(vp);
+ }
+ }
+
+ DPRINTF(WRITE, ("%s: return:%d\n", __func__, error));
+
+ return (error);
+}
+
+static int
+nandfs_lookup(struct vop_cachedlookup_args *ap)
+{
+ struct vnode *dvp, **vpp;
+ struct componentname *cnp;
+ struct ucred *cred;
+ struct thread *td;
+ struct nandfs_node *dir_node, *node;
+ struct nandfsmount *nmp;
+ uint64_t ino, off;
+ const char *name;
+ int namelen, nameiop, islastcn, mounted_ro;
+ int error, found;
+
+ DPRINTF(VNCALL, ("%s\n", __func__));
+
+ dvp = ap->a_dvp;
+ vpp = ap->a_vpp;
+ *vpp = NULL;
+
+ cnp = ap->a_cnp;
+ cred = cnp->cn_cred;
+ td = cnp->cn_thread;
+
+ dir_node = VTON(dvp);
+ nmp = dir_node->nn_nmp;
+
+ /* Simplify/clarification flags */
+ nameiop = cnp->cn_nameiop;
+ islastcn = cnp->cn_flags & ISLASTCN;
+ mounted_ro = dvp->v_mount->mnt_flag & MNT_RDONLY;
+
+ /*
+ * If requesting a modify on the last path element on a read-only
+ * filingsystem, reject lookup;
+ */
+ if (islastcn && mounted_ro && (nameiop == DELETE || nameiop == RENAME))
+ return (EROFS);
+
+ if (dir_node->nn_inode.i_links_count == 0)
+ return (ENOENT);
+
+ /*
+ * Obviously, the file is not (anymore) in the namecache, we have to
+ * search for it. There are three basic cases: '.', '..' and others.
+ *
+ * Following the guidelines of VOP_LOOKUP manpage and tmpfs.
+ */
+ error = 0;
+ if ((cnp->cn_namelen == 1) && (cnp->cn_nameptr[0] == '.')) {
+ DPRINTF(LOOKUP, ("\tlookup '.'\n"));
+ /* Special case 1 '.' */
+ VREF(dvp);
+ *vpp = dvp;
+ /* Done */
+ } else if (cnp->cn_flags & ISDOTDOT) {
+ /* Special case 2 '..' */
+ DPRINTF(LOOKUP, ("\tlookup '..'\n"));
+
+ /* Get our node */
+ name = "..";
+ namelen = 2;
+ error = nandfs_lookup_name_in_dir(dvp, name, namelen, &ino,
+ &found, &off);
+ if (error)
+ goto out;
+ if (!found)
+ error = ENOENT;
+
+ /* First unlock parent */
+ VOP_UNLOCK(dvp, 0);
+
+ if (error == 0) {
+ DPRINTF(LOOKUP, ("\tfound '..'\n"));
+ /* Try to create/reuse the node */
+ error = nandfs_get_node(nmp, ino, &node);
+
+ if (!error) {
+ DPRINTF(LOOKUP,
+ ("\tnode retrieved/created OK\n"));
+ *vpp = NTOV(node);
+ }
+ }
+
+ /* Try to relock parent */
+ vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY);
+ } else {
+ DPRINTF(LOOKUP, ("\tlookup file\n"));
+ /* All other files */
+ /* Look up filename in the directory returning its inode */
+ name = cnp->cn_nameptr;
+ namelen = cnp->cn_namelen;
+ error = nandfs_lookup_name_in_dir(dvp, name, namelen,
+ &ino, &found, &off);
+ if (error)
+ goto out;
+ if (!found) {
+ DPRINTF(LOOKUP, ("\tNOT found\n"));
+ /*
+ * UGH, didn't find name. If we're creating or
+ * renaming on the last name this is OK and we ought
+ * to return EJUSTRETURN if its allowed to be created.
+ */
+ error = ENOENT;
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ islastcn) {
+ error = VOP_ACCESS(dvp, VWRITE, cred,
+ td);
+ if (!error) {
+ /* keep the component name */
+ cnp->cn_flags |= SAVENAME;
+ error = EJUSTRETURN;
+ }
+ }
+ /* Done */
+ } else {
+ if (ino == NANDFS_WHT_INO)
+ cnp->cn_flags |= ISWHITEOUT;
+
+ if ((cnp->cn_flags & ISWHITEOUT) &&
+ (nameiop == LOOKUP))
+ return (ENOENT);
+
+ if ((nameiop == DELETE) && islastcn) {
+ if ((cnp->cn_flags & ISWHITEOUT) &&
+ (cnp->cn_flags & DOWHITEOUT)) {
+ cnp->cn_flags |= SAVENAME;
+ dir_node->nn_diroff = off;
+ return (EJUSTRETURN);
+ }
+
+ error = VOP_ACCESS(dvp, VWRITE, cred,
+ cnp->cn_thread);
+ if (error)
+ return (error);
+
+ /* Try to create/reuse the node */
+ error = nandfs_get_node(nmp, ino, &node);
+ if (!error) {
+ *vpp = NTOV(node);
+ node->nn_diroff = off;
+ }
+
+ if ((dir_node->nn_inode.i_mode & ISVTX) &&
+ cred->cr_uid != 0 &&
+ cred->cr_uid != dir_node->nn_inode.i_uid &&
+ node->nn_inode.i_uid != cred->cr_uid) {
+ vput(*vpp);
+ *vpp = NULL;
+ return (EPERM);
+ }
+ } else if ((nameiop == RENAME) && islastcn) {
+ error = VOP_ACCESS(dvp, VWRITE, cred,
+ cnp->cn_thread);
+ if (error)
+ return (error);
+
+ /* Try to create/reuse the node */
+ error = nandfs_get_node(nmp, ino, &node);
+ if (!error) {
+ *vpp = NTOV(node);
+ node->nn_diroff = off;
+ }
+ } else {
+ /* Try to create/reuse the node */
+ error = nandfs_get_node(nmp, ino, &node);
+ if (!error) {
+ *vpp = NTOV(node);
+ node->nn_diroff = off;
+ }
+ }
+ }
+ }
+
+out:
+ /*
+ * Store result in the cache if requested. If we are creating a file,
+ * the file might not be found and thus putting it into the namecache
+ * might be seen as negative caching.
+ */
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+ cache_enter(dvp, *vpp, cnp);
+
+ return (error);
+
+}
+
+static int
+nandfs_getattr(struct vop_getattr_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_inode *inode = &node->nn_inode;
+
+ DPRINTF(VNCALL, ("%s: vp: %p\n", __func__, vp));
+ nandfs_itimes(vp);
+
+ /* Basic info */
+ VATTR_NULL(vap);
+ vap->va_atime.tv_sec = inode->i_mtime;
+ vap->va_atime.tv_nsec = inode->i_mtime_nsec;
+ vap->va_mtime.tv_sec = inode->i_mtime;
+ vap->va_mtime.tv_nsec = inode->i_mtime_nsec;
+ vap->va_ctime.tv_sec = inode->i_ctime;
+ vap->va_ctime.tv_nsec = inode->i_ctime_nsec;
+ vap->va_type = IFTOVT(inode->i_mode);
+ vap->va_mode = inode->i_mode & ~S_IFMT;
+ vap->va_nlink = inode->i_links_count;
+ vap->va_uid = inode->i_uid;
+ vap->va_gid = inode->i_gid;
+ vap->va_rdev = inode->i_special;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_fileid = node->nn_ino;
+ vap->va_size = inode->i_size;
+ vap->va_blocksize = node->nn_nandfsdev->nd_blocksize;
+ vap->va_gen = 0;
+ vap->va_flags = inode->i_flags;
+ vap->va_bytes = inode->i_blocks * vap->va_blocksize;
+ vap->va_filerev = 0;
+ vap->va_vaflags = 0;
+
+ return (0);
+}
+
+static int
+nandfs_vtruncbuf(struct vnode *vp, uint64_t nblks)
+{
+ struct nandfs_device *nffsdev;
+ struct bufobj *bo;
+ struct buf *bp, *nbp;
+
+ bo = &vp->v_bufobj;
+ nffsdev = VTON(vp)->nn_nandfsdev;
+
+ ASSERT_VOP_LOCKED(vp, "nandfs_truncate");
+restart:
+ BO_LOCK(bo);
+restart_locked:
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_clean.bv_hd, b_bobufs, nbp) {
+ if (bp->b_lblkno < nblks)
+ continue;
+ if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT, NULL))
+ goto restart_locked;
+
+ bremfree(bp);
+ bp->b_flags |= (B_INVAL | B_RELBUF);
+ bp->b_flags &= ~(B_ASYNC | B_MANAGED);
+ BO_UNLOCK(bo);
+ brelse(bp);
+ BO_LOCK(bo);
+ }
+
+ TAILQ_FOREACH_SAFE(bp, &bo->bo_dirty.bv_hd, b_bobufs, nbp) {
+ if (bp->b_lblkno < nblks)
+ continue;
+ if (BUF_LOCK(bp,
+ LK_EXCLUSIVE | LK_SLEEPFAIL | LK_INTERLOCK,
+ BO_MTX(bo)) == ENOLCK)
+ goto restart;
+ bp->b_flags |= (B_INVAL | B_RELBUF);
+ bp->b_flags &= ~(B_ASYNC | B_MANAGED);
+ brelse(bp);
+ nandfs_dirty_bufs_decrement(nffsdev);
+ BO_LOCK(bo);
+ }
+
+ BO_UNLOCK(bo);
+
+ return (0);
+}
+
+static int
+nandfs_truncate(struct vnode *vp, uint64_t newsize)
+{
+ struct nandfs_device *nffsdev;
+ struct nandfs_node *node;
+ struct nandfs_inode *inode;
+ struct buf *bp = NULL;
+ uint64_t oblks, nblks, vblk, size, rest;
+ int error;
+
+ node = VTON(vp);
+ nffsdev = node->nn_nandfsdev;
+ inode = &node->nn_inode;
+
+ /* Calculate end of file */
+ size = inode->i_size;
+
+ if (newsize == size) {
+ node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ nandfs_itimes(vp);
+ return (0);
+ }
+
+ if (newsize > size) {
+ inode->i_size = newsize;
+ vnode_pager_setsize(vp, newsize);
+ node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ nandfs_itimes(vp);
+ return (0);
+ }
+
+ nblks = howmany(newsize, nffsdev->nd_blocksize);
+ oblks = howmany(size, nffsdev->nd_blocksize);
+ rest = newsize % nffsdev->nd_blocksize;
+
+ if (rest) {
+ error = nandfs_bmap_lookup(node, nblks - 1, &vblk);
+ if (error)
+ return (error);
+
+ if (vblk != 0)
+ error = nandfs_bread(node, nblks - 1, NOCRED, 0, &bp);
+ else
+ error = nandfs_bcreate(node, nblks - 1, NOCRED, 0, &bp);
+
+ if (error) {
+ if (bp)
+ brelse(bp);
+ return (error);
+ }
+
+ bzero((char *)bp->b_data + rest,
+ (u_int)(nffsdev->nd_blocksize - rest));
+ error = nandfs_dirty_buf(bp, 0);
+ if (error)
+ return (error);
+ }
+
+ DPRINTF(VNCALL, ("%s: vp %p oblks %jx nblks %jx\n", __func__, vp, oblks,
+ nblks));
+
+ error = nandfs_bmap_truncate_mapping(node, oblks - 1, nblks - 1);
+ if (error) {
+ if (bp)
+ nandfs_undirty_buf(bp);
+ return (error);
+ }
+
+ error = nandfs_vtruncbuf(vp, nblks);
+ if (error) {
+ if (bp)
+ nandfs_undirty_buf(bp);
+ return (error);
+ }
+
+ inode->i_size = newsize;
+ vnode_pager_setsize(vp, newsize);
+ node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ nandfs_itimes(vp);
+
+ return (error);
+}
+
+static void
+nandfs_itimes_locked(struct vnode *vp)
+{
+ struct nandfs_node *node;
+ struct nandfs_inode *inode;
+ struct timespec ts;
+
+ ASSERT_VI_LOCKED(vp, __func__);
+
+ node = VTON(vp);
+ inode = &node->nn_inode;
+
+ if ((node->nn_flags & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
+ return;
+
+ if (((vp->v_mount->mnt_kern_flag &
+ (MNTK_SUSPENDED | MNTK_SUSPEND)) == 0) ||
+ (node->nn_flags & (IN_CHANGE | IN_UPDATE)))
+ node->nn_flags |= IN_MODIFIED;
+
+ vfs_timestamp(&ts);
+ if (node->nn_flags & IN_UPDATE) {
+ inode->i_mtime = ts.tv_sec;
+ inode->i_mtime_nsec = ts.tv_nsec;
+ }
+ if (node->nn_flags & IN_CHANGE) {
+ inode->i_ctime = ts.tv_sec;
+ inode->i_ctime_nsec = ts.tv_nsec;
+ }
+
+ node->nn_flags &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
+}
+
+void
+nandfs_itimes(struct vnode *vp)
+{
+
+ VI_LOCK(vp);
+ nandfs_itimes_locked(vp);
+ VI_UNLOCK(vp);
+}
+
+static int
+nandfs_chmod(struct vnode *vp, int mode, struct ucred *cred, struct thread *td)
+{
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_inode *inode = &node->nn_inode;
+ uint16_t nmode;
+ int error = 0;
+
+ DPRINTF(VNCALL, ("%s: vp %p, mode %x, cred %p, td %p\n", __func__, vp,
+ mode, cred, td));
+ /*
+ * To modify the permissions on a file, must possess VADMIN
+ * for that file.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ return (error);
+
+ /*
+ * Privileged processes may set the sticky bit on non-directories,
+ * as well as set the setgid bit on a file with a group that the
+ * process is not a member of. Both of these are allowed in
+ * jail(8).
+ */
+ if (vp->v_type != VDIR && (mode & S_ISTXT)) {
+ if (priv_check_cred(cred, PRIV_VFS_STICKYFILE, 0))
+ return (EFTYPE);
+ }
+ if (!groupmember(inode->i_gid, cred) && (mode & ISGID)) {
+ error = priv_check_cred(cred, PRIV_VFS_SETGID, 0);
+ if (error)
+ return (error);
+ }
+
+ /*
+ * Deny setting setuid if we are not the file owner.
+ */
+ if ((mode & ISUID) && inode->i_uid != cred->cr_uid) {
+ error = priv_check_cred(cred, PRIV_VFS_ADMIN, 0);
+ if (error)
+ return (error);
+ }
+
+ nmode = inode->i_mode;
+ nmode &= ~ALLPERMS;
+ nmode |= (mode & ALLPERMS);
+ inode->i_mode = nmode;
+ node->nn_flags |= IN_CHANGE;
+
+ DPRINTF(VNCALL, ("%s: to mode %x\n", __func__, nmode));
+
+ return (error);
+}
+
+static int
+nandfs_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
+ struct thread *td)
+{
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_inode *inode = &node->nn_inode;
+ uid_t ouid;
+ gid_t ogid;
+ int error = 0;
+
+ if (uid == (uid_t)VNOVAL)
+ uid = inode->i_uid;
+ if (gid == (gid_t)VNOVAL)
+ gid = inode->i_gid;
+ /*
+ * To modify the ownership of a file, must possess VADMIN for that
+ * file.
+ */
+ if ((error = VOP_ACCESSX(vp, VWRITE_OWNER, cred, td)))
+ return (error);
+ /*
+ * To change the owner of a file, or change the group of a file to a
+ * group of which we are not a member, the caller must have
+ * privilege.
+ */
+ if (((uid != inode->i_uid && uid != cred->cr_uid) ||
+ (gid != inode->i_gid && !groupmember(gid, cred))) &&
+ (error = priv_check_cred(cred, PRIV_VFS_CHOWN, 0)))
+ return (error);
+ ogid = inode->i_gid;
+ ouid = inode->i_uid;
+
+ inode->i_gid = gid;
+ inode->i_uid = uid;
+
+ node->nn_flags |= IN_CHANGE;
+ if ((inode->i_mode & (ISUID | ISGID)) &&
+ (ouid != uid || ogid != gid)) {
+ if (priv_check_cred(cred, PRIV_VFS_RETAINSUGID, 0)) {
+ inode->i_mode &= ~(ISUID | ISGID);
+ }
+ }
+ DPRINTF(VNCALL, ("%s: vp %p, cred %p, td %p - ret OK\n", __func__, vp,
+ cred, td));
+ return (0);
+}
+
+static int
+nandfs_setattr(struct vop_setattr_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_inode *inode = &node->nn_inode;
+ struct vattr *vap = ap->a_vap;
+ struct ucred *cred = ap->a_cred;
+ struct thread *td = curthread;
+ uint32_t flags;
+ int error = 0;
+
+ if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
+ (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
+ (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
+ (vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
+ DPRINTF(VNCALL, ("%s: unsettable attribute\n", __func__));
+ return (EINVAL);
+ }
+
+ if (vap->va_flags != VNOVAL) {
+ DPRINTF(VNCALL, ("%s: vp:%p td:%p flags:%lx\n", __func__, vp,
+ td, vap->va_flags));
+
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ /*
+ * Callers may only modify the file flags on objects they
+ * have VADMIN rights for.
+ */
+ if ((error = VOP_ACCESS(vp, VADMIN, cred, td)))
+ return (error);
+ /*
+ * Unprivileged processes are not permitted to unset system
+ * flags, or modify flags if any system flags are set.
+ * Privileged non-jail processes may not modify system flags
+ * if securelevel > 0 and any existing system flags are set.
+ * Privileged jail processes behave like privileged non-jail
+ * processes if the security.jail.chflags_allowed sysctl is
+ * is non-zero; otherwise, they behave like unprivileged
+ * processes.
+ */
+
+ flags = inode->i_flags;
+ if (!priv_check_cred(cred, PRIV_VFS_SYSFLAGS, 0)) {
+ if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND)) {
+ error = securelevel_gt(cred, 0);
+ if (error)
+ return (error);
+ }
+ /* Snapshot flag cannot be set or cleared */
+ if (((vap->va_flags & SF_SNAPSHOT) != 0 &&
+ (flags & SF_SNAPSHOT) == 0) ||
+ ((vap->va_flags & SF_SNAPSHOT) == 0 &&
+ (flags & SF_SNAPSHOT) != 0))
+ return (EPERM);
+
+ inode->i_flags = vap->va_flags;
+ } else {
+ if (flags & (SF_NOUNLINK | SF_IMMUTABLE | SF_APPEND) ||
+ (vap->va_flags & UF_SETTABLE) != vap->va_flags)
+ return (EPERM);
+
+ flags &= SF_SETTABLE;
+ flags |= (vap->va_flags & UF_SETTABLE);
+ inode->i_flags = flags;
+ }
+ node->nn_flags |= IN_CHANGE;
+ if (vap->va_flags & (IMMUTABLE | APPEND))
+ return (0);
+ }
+ if (inode->i_flags & (IMMUTABLE | APPEND))
+ return (EPERM);
+
+ if (vap->va_size != (u_quad_t)VNOVAL) {
+ DPRINTF(VNCALL, ("%s: vp:%p td:%p size:%jx\n", __func__, vp, td,
+ (uintmax_t)vap->va_size));
+
+ switch (vp->v_type) {
+ case VDIR:
+ return (EISDIR);
+ case VLNK:
+ case VREG:
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ if ((inode->i_flags & SF_SNAPSHOT) != 0)
+ return (EPERM);
+ break;
+ default:
+ return (0);
+ }
+
+ if (vap->va_size > node->nn_nandfsdev->nd_maxfilesize)
+ return (EFBIG);
+
+ KASSERT((vp->v_type == VREG), ("Set size %d", vp->v_type));
+ nandfs_truncate(vp, vap->va_size);
+ node->nn_flags |= IN_CHANGE;
+
+ return (0);
+ }
+
+ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ DPRINTF(VNCALL, ("%s: vp:%p td:%p uid/gid %x/%x\n", __func__,
+ vp, td, vap->va_uid, vap->va_gid));
+ error = nandfs_chown(vp, vap->va_uid, vap->va_gid, cred, td);
+ if (error)
+ return (error);
+ }
+
+ if (vap->va_mode != (mode_t)VNOVAL) {
+ if (vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (EROFS);
+ DPRINTF(VNCALL, ("%s: vp:%p td:%p mode %x\n", __func__, vp, td,
+ vap->va_mode));
+
+ error = nandfs_chmod(vp, (int)vap->va_mode, cred, td);
+ if (error)
+ return (error);
+ }
+ if (vap->va_atime.tv_sec != VNOVAL ||
+ vap->va_mtime.tv_sec != VNOVAL ||
+ vap->va_birthtime.tv_sec != VNOVAL) {
+ DPRINTF(VNCALL, ("%s: vp:%p td:%p time a/m/b %jx/%jx/%jx\n",
+ __func__, vp, td, (uintmax_t)vap->va_atime.tv_sec,
+ (uintmax_t)vap->va_mtime.tv_sec,
+ (uintmax_t)vap->va_birthtime.tv_sec));
+
+ if (vap->va_atime.tv_sec != VNOVAL)
+ node->nn_flags |= IN_ACCESS;
+ if (vap->va_mtime.tv_sec != VNOVAL)
+ node->nn_flags |= IN_CHANGE | IN_UPDATE;
+ if (vap->va_birthtime.tv_sec != VNOVAL)
+ node->nn_flags |= IN_MODIFIED;
+ nandfs_itimes(vp);
+ return (0);
+ }
+
+ return (0);
+}
+
+static int
+nandfs_open(struct vop_open_args *ap)
+{
+ struct nandfs_node *node = VTON(ap->a_vp);
+ uint64_t filesize;
+
+ DPRINTF(VNCALL, ("nandfs_open called ap->a_mode %x\n", ap->a_mode));
+
+ if (ap->a_vp->v_type == VCHR || ap->a_vp->v_type == VBLK)
+ return (EOPNOTSUPP);
+
+ if ((node->nn_inode.i_flags & APPEND) &&
+ (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
+ return (EPERM);
+
+ filesize = node->nn_inode.i_size;
+ vnode_create_vobject(ap->a_vp, filesize, ap->a_td);
+
+ return (0);
+}
+
+static int
+nandfs_close(struct vop_close_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+
+ DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node));
+
+ mtx_lock(&vp->v_interlock);
+ if (vp->v_usecount > 1)
+ nandfs_itimes_locked(vp);
+ mtx_unlock(&vp->v_interlock);
+
+ return (0);
+}
+
+static int
+nandfs_check_possible(struct vnode *vp, struct vattr *vap, mode_t mode)
+{
+
+ /* Check if we are allowed to write */
+ switch (vap->va_type) {
+ case VDIR:
+ case VLNK:
+ case VREG:
+ /*
+ * Normal nodes: check if we're on a read-only mounted
+ * filingsystem and bomb out if we're trying to write.
+ */
+ if ((mode & VWRITE) && (vp->v_mount->mnt_flag & MNT_RDONLY))
+ return (EROFS);
+ break;
+ case VBLK:
+ case VCHR:
+ case VSOCK:
+ case VFIFO:
+ /*
+ * Special nodes: even on read-only mounted filingsystems
+ * these are allowed to be written to if permissions allow.
+ */
+ break;
+ default:
+ /* No idea what this is */
+ return (EINVAL);
+ }
+
+ /* Noone may write immutable files */
+ if ((mode & VWRITE) && (VTON(vp)->nn_inode.i_flags & IMMUTABLE))
+ return (EPERM);
+
+ return (0);
+}
+
+static int
+nandfs_check_permitted(struct vnode *vp, struct vattr *vap, mode_t mode,
+ struct ucred *cred)
+{
+
+ return (vaccess(vp->v_type, vap->va_mode, vap->va_uid, vap->va_gid, mode,
+ cred, NULL));
+}
+
+static int
+nandfs_advlock(struct vop_advlock_args *ap)
+{
+ struct nandfs_node *nvp;
+ quad_t size;
+
+ nvp = VTON(ap->a_vp);
+ size = nvp->nn_inode.i_size;
+ return (lf_advlock(ap, &(nvp->nn_lockf), size));
+}
+
+static int
+nandfs_access(struct vop_access_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ accmode_t accmode = ap->a_accmode;
+ struct ucred *cred = ap->a_cred;
+ struct vattr vap;
+ int error;
+
+ DPRINTF(VNCALL, ("%s: vp:%p mode: %x\n", __func__, vp, accmode));
+
+ error = VOP_GETATTR(vp, &vap, NULL);
+ if (error)
+ return (error);
+
+ error = nandfs_check_possible(vp, &vap, accmode);
+ if (error) {
+ return (error);
+ }
+
+ error = nandfs_check_permitted(vp, &vap, accmode, cred);
+
+ return (error);
+}
+
+static int
+nandfs_print(struct vop_print_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *nvp = VTON(vp);
+
+ printf("\tvp=%p, nandfs_node=%p\n", vp, nvp);
+ printf("nandfs inode %#jx\n", (uintmax_t)nvp->nn_ino);
+ printf("flags = 0x%b\n", (u_int)nvp->nn_flags, PRINT_NODE_FLAGS);
+
+ return (0);
+}
+
+static void
+nandfs_read_filebuf(struct nandfs_node *node, struct buf *bp)
+{
+ struct nandfs_device *nandfsdev = node->nn_nandfsdev;
+ struct buf *nbp;
+ nandfs_daddr_t vblk, pblk;
+ nandfs_lbn_t from;
+ uint32_t blocksize;
+ int error = 0;
+ int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
+
+ /*
+ * Translate all the block sectors into a series of buffers to read
+ * asynchronously from the nandfs device. Note that this lookup may
+ * induce readin's too.
+ */
+
+ blocksize = nandfsdev->nd_blocksize;
+ if (bp->b_bcount / blocksize != 1)
+ panic("invalid b_count in bp %p\n", bp);
+
+ from = bp->b_blkno;
+
+ DPRINTF(READ, ("\tread in from inode %#jx blkno %#jx"
+ " count %#lx\n", (uintmax_t)node->nn_ino, from,
+ bp->b_bcount));
+
+ /* Get virtual block numbers for the vnode's buffer span */
+ error = nandfs_bmap_lookup(node, from, &vblk);
+ if (error) {
+ bp->b_error = EINVAL;
+ bp->b_ioflags |= BIO_ERROR;
+ bufdone(bp);
+ return;
+ }
+
+ /* Translate virtual block numbers to physical block numbers */
+ error = nandfs_vtop(node, vblk, &pblk);
+ if (error) {
+ bp->b_error = EINVAL;
+ bp->b_ioflags |= BIO_ERROR;
+ bufdone(bp);
+ return;
+ }
+
+ /* Issue translated blocks */
+ bp->b_resid = bp->b_bcount;
+
+ /* Note virtual block 0 marks not mapped */
+ if (vblk == 0) {
+ vfs_bio_clrbuf(bp);
+ bufdone(bp);
+ return;
+ }
+
+ nbp = bp;
+ nbp->b_blkno = pblk * blk2dev;
+ bp->b_iooffset = dbtob(nbp->b_blkno);
+ MPASS(bp->b_iooffset >= 0);
+ BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, nbp);
+ nandfs_vblk_set(bp, vblk);
+ DPRINTF(READ, ("read_filebuf : ino %#jx blk %#jx -> "
+ "%#jx -> %#jx [bp %p]\n", (uintmax_t)node->nn_ino,
+ (uintmax_t)(from), (uintmax_t)vblk,
+ (uintmax_t)pblk, nbp));
+}
+
+static void
+nandfs_write_filebuf(struct nandfs_node *node, struct buf *bp)
+{
+ struct nandfs_device *nandfsdev = node->nn_nandfsdev;
+
+ bp->b_iooffset = dbtob(bp->b_blkno);
+ MPASS(bp->b_iooffset >= 0);
+ BO_STRATEGY(&nandfsdev->nd_devvp->v_bufobj, bp);
+}
+
+static int
+nandfs_strategy(struct vop_strategy_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct buf *bp = ap->a_bp;
+ struct nandfs_node *node = VTON(vp);
+
+
+ /* check if we ought to be here */
+ KASSERT((vp->v_type != VBLK && vp->v_type != VCHR),
+ ("nandfs_strategy on type %d", vp->v_type));
+
+ /* Translate if needed and pass on */
+ if (bp->b_iocmd == BIO_READ) {
+ nandfs_read_filebuf(node, bp);
+ return (0);
+ }
+
+ /* Send to segment collector */
+ nandfs_write_filebuf(node, bp);
+ return (0);
+}
+
+static int
+nandfs_readdir(struct vop_readdir_args *ap)
+{
+ struct uio *uio = ap->a_uio;
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_dir_entry *ndirent;
+ struct dirent dirent;
+ struct buf *bp;
+ uint64_t file_size, diroffset, transoffset, blkoff;
+ uint64_t blocknr;
+ uint32_t blocksize = node->nn_nandfsdev->nd_blocksize;
+ uint8_t *pos, name_len;
+ int error;
+
+ DPRINTF(READDIR, ("nandfs_readdir called\n"));
+
+ if (vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ file_size = node->nn_inode.i_size;
+ DPRINTF(READDIR, ("nandfs_readdir filesize %jd resid %zd\n",
+ (uintmax_t)file_size, uio->uio_resid ));
+
+ /* We are called just as long as we keep on pushing data in */
+ error = 0;
+ if ((uio->uio_offset < file_size) &&
+ (uio->uio_resid >= sizeof(struct dirent))) {
+ diroffset = uio->uio_offset;
+ transoffset = diroffset;
+
+ blocknr = diroffset / blocksize;
+ blkoff = diroffset % blocksize;
+ error = nandfs_bread(node, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (EIO);
+ }
+ while (diroffset < file_size) {
+ DPRINTF(READDIR, ("readdir : offset = %"PRIu64"\n",
+ diroffset));
+ if (blkoff >= blocksize) {
+ blkoff = 0; blocknr++;
+ brelse(bp);
+ error = nandfs_bread(node, blocknr, NOCRED, 0,
+ &bp);
+ if (error) {
+ brelse(bp);
+ return (EIO);
+ }
+ }
+
+ /* Read in one dirent */
+ pos = (uint8_t *)bp->b_data + blkoff;
+ ndirent = (struct nandfs_dir_entry *)pos;
+
+ name_len = ndirent->name_len;
+ memset(&dirent, 0, sizeof(struct dirent));
+ dirent.d_fileno = ndirent->inode;
+ if (dirent.d_fileno) {
+ dirent.d_type = ndirent->file_type;
+ dirent.d_namlen = name_len;
+ strncpy(dirent.d_name, ndirent->name, name_len);
+ dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
+ DPRINTF(READDIR, ("copying `%*.*s`\n", name_len,
+ name_len, dirent.d_name));
+ }
+
+ /*
+ * If there isn't enough space in the uio to return a
+ * whole dirent, break off read
+ */
+ if (uio->uio_resid < GENERIC_DIRSIZ(&dirent))
+ break;
+
+ /* Transfer */
+ if (dirent.d_fileno)
+ uiomove(&dirent, GENERIC_DIRSIZ(&dirent), uio);
+
+ /* Advance */
+ diroffset += ndirent->rec_len;
+ blkoff += ndirent->rec_len;
+
+ /* Remember the last entry we transfered */
+ transoffset = diroffset;
+ }
+ brelse(bp);
+
+ /* Pass on last transfered offset */
+ uio->uio_offset = transoffset;
+ }
+
+ if (ap->a_eofflag)
+ *ap->a_eofflag = (uio->uio_offset >= file_size);
+
+ return (error);
+}
+
+static int
+nandfs_dirempty(struct vnode *dvp, uint64_t parentino, struct ucred *cred)
+{
+ struct nandfs_node *dnode = VTON(dvp);
+ struct nandfs_dir_entry *dirent;
+ uint64_t file_size = dnode->nn_inode.i_size;
+ uint64_t blockcount = dnode->nn_inode.i_blocks;
+ uint64_t blocknr;
+ uint32_t blocksize = dnode->nn_nandfsdev->nd_blocksize;
+ uint32_t limit;
+ uint32_t off;
+ uint8_t *pos;
+ struct buf *bp;
+ int error;
+
+ DPRINTF(LOOKUP, ("%s: dvp %p parentino %#jx cred %p\n", __func__, dvp,
+ (uintmax_t)parentino, cred));
+
+ KASSERT((file_size != 0), ("nandfs_dirempty for NULL dir %p", dvp));
+
+ blocknr = 0;
+ while (blocknr < blockcount) {
+ error = nandfs_bread(dnode, blocknr, NOCRED, 0, &bp);
+ if (error) {
+ brelse(bp);
+ return (0);
+ }
+
+ pos = (uint8_t *)bp->b_data;
+ off = 0;
+
+ if (blocknr == (blockcount - 1))
+ limit = file_size % blocksize;
+ else
+ limit = blocksize;
+
+ while (off < limit) {
+ dirent = (struct nandfs_dir_entry *)(pos + off);
+ off += dirent->rec_len;
+
+ if (dirent->inode == 0)
+ continue;
+
+ switch (dirent->name_len) {
+ case 0:
+ break;
+ case 1:
+ if (dirent->name[0] != '.')
+ goto notempty;
+
+ KASSERT(dirent->inode == dnode->nn_ino,
+ (".'s inode does not match dir"));
+ break;
+ case 2:
+ if (dirent->name[0] != '.' &&
+ dirent->name[1] != '.')
+ goto notempty;
+
+ KASSERT(dirent->inode == parentino,
+ ("..'s inode does not match parent"));
+ break;
+ default:
+ goto notempty;
+ }
+ }
+
+ brelse(bp);
+ blocknr++;
+ }
+
+ return (1);
+notempty:
+ brelse(bp);
+ return (0);
+}
+
+static int
+nandfs_link(struct vop_link_args *ap)
+{
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *vp = ap->a_vp;
+ struct componentname *cnp = ap->a_cnp;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_inode *inode = &node->nn_inode;
+ int error;
+
+ if (tdvp->v_mount != vp->v_mount)
+ return (EXDEV);
+
+ if (inode->i_links_count >= LINK_MAX)
+ return (EMLINK);
+
+ if (inode->i_flags & (IMMUTABLE | APPEND))
+ return (EPERM);
+
+ /* Update link count */
+ inode->i_links_count++;
+
+ /* Add dir entry */
+ error = nandfs_add_dirent(tdvp, node->nn_ino, cnp->cn_nameptr,
+ cnp->cn_namelen, IFTODT(inode->i_mode));
+ if (error) {
+ inode->i_links_count--;
+ }
+
+ node->nn_flags |= IN_CHANGE;
+ nandfs_itimes(vp);
+ DPRINTF(VNCALL, ("%s: tdvp %p vp %p cnp %p\n",
+ __func__, tdvp, vp, cnp));
+
+ return (0);
+}
+
+static int
+nandfs_create(struct vop_create_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfsmount *nmp = dir_node->nn_nmp;
+ struct nandfs_node *node;
+ int error;
+
+ DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp));
+
+ if (nandfs_fs_full(dir_node->nn_nandfsdev))
+ return (ENOSPC);
+
+ /* Create new vnode/inode */
+ error = nandfs_node_create(nmp, &node, mode);
+ if (error)
+ return (error);
+ node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
+ node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
+
+ /* Add new dir entry */
+ error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
+ cnp->cn_namelen, IFTODT(mode));
+ if (error) {
+ if (nandfs_node_destroy(node)) {
+ nandfs_error("%s: error destroying node %p\n",
+ __func__, node);
+ }
+ return (error);
+ }
+ *vpp = NTOV(node);
+
+ DPRINTF(VNCALL, ("created file vp %p nandnode %p ino %jx\n", *vpp, node,
+ (uintmax_t)node->nn_ino));
+ return (0);
+}
+
+static int
+nandfs_remove(struct vop_remove_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode *dvp = ap->a_dvp;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_node *dnode = VTON(dvp);
+ struct componentname *cnp = ap->a_cnp;
+
+ DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx link %d\n",
+ __func__, dvp, vp, node, (uintmax_t)node->nn_ino,
+ node->nn_inode.i_links_count));
+
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+
+ /* Files marked as immutable or append-only cannot be deleted. */
+ if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
+ (dnode->nn_inode.i_flags & APPEND))
+ return (EPERM);
+
+ nandfs_remove_dirent(dvp, node, cnp);
+ node->nn_inode.i_links_count--;
+ node->nn_flags |= IN_CHANGE;
+
+ return (0);
+}
+
+/*
+ * Check if source directory is in the path of the target directory.
+ * Target is supplied locked, source is unlocked.
+ * The target is always vput before returning.
+ */
+static int
+nandfs_checkpath(struct nandfs_node *src, struct nandfs_node *dest,
+ struct ucred *cred)
+{
+ struct vnode *vp;
+ int error, rootino;
+ struct nandfs_dir_entry dirent;
+
+ vp = NTOV(dest);
+ if (src->nn_ino == dest->nn_ino) {
+ error = EEXIST;
+ goto out;
+ }
+ rootino = NANDFS_ROOT_INO;
+ error = 0;
+ if (dest->nn_ino == rootino)
+ goto out;
+
+ for (;;) {
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ break;
+ }
+
+ error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirent,
+ NANDFS_DIR_REC_LEN(2), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED | IO_NOMACCHECK, cred, NOCRED,
+ NULL, NULL);
+ if (error != 0)
+ break;
+ if (dirent.name_len != 2 ||
+ dirent.name[0] != '.' ||
+ dirent.name[1] != '.') {
+ error = ENOTDIR;
+ break;
+ }
+ if (dirent.inode == src->nn_ino) {
+ error = EINVAL;
+ break;
+ }
+ if (dirent.inode == rootino)
+ break;
+ vput(vp);
+ if ((error = VFS_VGET(vp->v_mount, dirent.inode,
+ LK_EXCLUSIVE, &vp)) != 0) {
+ vp = NULL;
+ break;
+ }
+ }
+
+out:
+ if (error == ENOTDIR)
+ printf("checkpath: .. not a directory\n");
+ if (vp != NULL)
+ vput(vp);
+ return (error);
+}
+
+static int
+nandfs_rename(struct vop_rename_args *ap)
+{
+ struct vnode *tvp = ap->a_tvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct vnode *fdvp = ap->a_fdvp;
+ struct componentname *tcnp = ap->a_tcnp;
+ struct componentname *fcnp = ap->a_fcnp;
+ int doingdirectory = 0, oldparent = 0, newparent = 0;
+ int error = 0;
+
+ struct nandfs_node *fdnode, *fnode, *fnode1;
+ struct nandfs_node *tdnode = VTON(tdvp);
+ struct nandfs_node *tnode;
+
+ uint32_t tdflags, fflags, fdflags;
+ uint16_t mode;
+
+ DPRINTF(VNCALL, ("%s: fdvp:%p fvp:%p tdvp:%p tdp:%p\n", __func__, fdvp,
+ fvp, tdvp, tvp));
+
+ /*
+ * Check for cross-device rename.
+ */
+ if ((fvp->v_mount != tdvp->v_mount) ||
+ (tvp && (fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+abortit:
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ vrele(fdvp);
+ vrele(fvp);
+ return (error);
+ }
+
+ tdflags = tdnode->nn_inode.i_flags;
+ if (tvp &&
+ ((VTON(tvp)->nn_inode.i_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
+ (tdflags & APPEND))) {
+ error = EPERM;
+ goto abortit;
+ }
+
+ /*
+ * Renaming a file to itself has no effect. The upper layers should
+ * not call us in that case. Temporarily just warn if they do.
+ */
+ if (fvp == tvp) {
+ printf("nandfs_rename: fvp == tvp (can't happen)\n");
+ error = 0;
+ goto abortit;
+ }
+
+ if ((error = vn_lock(fvp, LK_EXCLUSIVE)) != 0)
+ goto abortit;
+
+ fdnode = VTON(fdvp);
+ fnode = VTON(fvp);
+
+ if (fnode->nn_inode.i_links_count >= LINK_MAX) {
+ VOP_UNLOCK(fvp, 0);
+ error = EMLINK;
+ goto abortit;
+ }
+
+ fflags = fnode->nn_inode.i_flags;
+ fdflags = fdnode->nn_inode.i_flags;
+
+ if ((fflags & (NOUNLINK | IMMUTABLE | APPEND)) ||
+ (fdflags & APPEND)) {
+ VOP_UNLOCK(fvp, 0);
+ error = EPERM;
+ goto abortit;
+ }
+
+ mode = fnode->nn_inode.i_mode;
+ if ((mode & S_IFMT) == S_IFDIR) {
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+
+ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
+ (fdvp == fvp) ||
+ ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT) ||
+ (fnode->nn_flags & IN_RENAME)) {
+ VOP_UNLOCK(fvp, 0);
+ error = EINVAL;
+ goto abortit;
+ }
+ fnode->nn_flags |= IN_RENAME;
+ doingdirectory = 1;
+ DPRINTF(VNCALL, ("%s: doingdirectory dvp %p\n", __func__,
+ tdvp));
+ oldparent = fdnode->nn_ino;
+ }
+
+ vrele(fdvp);
+
+ tnode = NULL;
+ if (tvp)
+ tnode = VTON(tvp);
+
+ /*
+ * Bump link count on fvp while we are moving stuff around. If we
+ * crash before completing the work, the link count may be wrong
+ * but correctable.
+ */
+ fnode->nn_inode.i_links_count++;
+
+ /* Check for in path moving XXX */
+ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
+ VOP_UNLOCK(fvp, 0);
+ if (oldparent != tdnode->nn_ino)
+ newparent = tdnode->nn_ino;
+ if (doingdirectory && newparent) {
+ if (error) /* write access check above */
+ goto bad;
+ if (tnode != NULL)
+ vput(tvp);
+
+ error = nandfs_checkpath(fnode, tdnode, tcnp->cn_cred);
+ if (error)
+ goto out;
+
+ VREF(tdvp);
+ error = relookup(tdvp, &tvp, tcnp);
+ if (error)
+ goto out;
+ vrele(tdvp);
+ tdnode = VTON(tdvp);
+ tnode = NULL;
+ if (tvp)
+ tnode = VTON(tvp);
+ }
+
+ /*
+ * If the target doesn't exist, link the target to the source and
+ * unlink the source. Otherwise, rewrite the target directory to
+ * reference the source and remove the original entry.
+ */
+
+ if (tvp == NULL) {
+ /*
+ * Account for ".." in new directory.
+ */
+ if (doingdirectory && fdvp != tdvp)
+ tdnode->nn_inode.i_links_count++;
+
+ DPRINTF(VNCALL, ("%s: new entry in dvp:%p\n", __func__, tdvp));
+ /*
+ * Add name in new directory.
+ */
+ error = nandfs_add_dirent(tdvp, fnode->nn_ino, tcnp->cn_nameptr,
+ tcnp->cn_namelen, IFTODT(fnode->nn_inode.i_mode));
+ if (error) {
+ if (doingdirectory && fdvp != tdvp)
+ tdnode->nn_inode.i_links_count--;
+ goto bad;
+ }
+
+ vput(tdvp);
+ } else {
+ /*
+ * If the parent directory is "sticky", then the user must
+ * own the parent directory, or the destination of the rename,
+ * otherwise the destination may not be changed (except by
+ * root). This implements append-only directories.
+ */
+ if ((tdnode->nn_inode.i_mode & S_ISTXT) &&
+ tcnp->cn_cred->cr_uid != 0 &&
+ tcnp->cn_cred->cr_uid != tdnode->nn_inode.i_uid &&
+ tnode->nn_inode.i_uid != tcnp->cn_cred->cr_uid) {
+ error = EPERM;
+ goto bad;
+ }
+ /*
+ * Target must be empty if a directory and have no links
+ * to it. Also, ensure source and target are compatible
+ * (both directories, or both not directories).
+ */
+ mode = tnode->nn_inode.i_mode;
+ if ((mode & S_IFMT) == S_IFDIR) {
+ if (!nandfs_dirempty(tvp, tdnode->nn_ino,
+ tcnp->cn_cred)) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ /*
+ * Update name cache since directory is going away.
+ */
+ cache_purge(tdvp);
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+
+ DPRINTF(VNCALL, ("%s: update entry dvp:%p\n", __func__, tdvp));
+ /*
+ * Change name tcnp in tdvp to point at fvp.
+ */
+ error = nandfs_update_dirent(tdvp, fnode, tnode);
+ if (error)
+ goto bad;
+
+ if (doingdirectory && !newparent)
+ tdnode->nn_inode.i_links_count--;
+
+ vput(tdvp);
+
+ tnode->nn_inode.i_links_count--;
+ vput(tvp);
+ tnode = NULL;
+ }
+
+ /*
+ * Unlink the source.
+ */
+ fcnp->cn_flags &= ~MODMASK;
+ fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ VREF(fdvp);
+ error = relookup(fdvp, &fvp, fcnp);
+ if (error == 0)
+ vrele(fdvp);
+ if (fvp != NULL) {
+ fnode1 = VTON(fvp);
+ fdnode = VTON(fdvp);
+ } else {
+ /*
+ * From name has disappeared.
+ */
+ if (doingdirectory)
+ panic("nandfs_rename: lost dir entry");
+ vrele(ap->a_fvp);
+ return (0);
+ }
+
+ DPRINTF(VNCALL, ("%s: unlink source fnode:%p\n", __func__, fnode));
+
+ /*
+ * Ensure that the directory entry still exists and has not
+ * changed while the new name has been entered. If the source is
+ * a file then the entry may have been unlinked or renamed. In
+ * either case there is no further work to be done. If the source
+ * is a directory then it cannot have been rmdir'ed; its link
+ * count of three would cause a rmdir to fail with ENOTEMPTY.
+ * The IN_RENAME flag ensures that it cannot be moved by another
+ * rename.
+ */
+ if (fnode != fnode1) {
+ if (doingdirectory)
+ panic("nandfs: lost dir entry");
+ } else {
+ /*
+ * If the source is a directory with a
+ * new parent, the link count of the old
+ * parent directory must be decremented
+ * and ".." set to point to the new parent.
+ */
+ if (doingdirectory && newparent) {
+ DPRINTF(VNCALL, ("%s: new parent %#jx -> %#jx\n",
+ __func__, (uintmax_t) oldparent,
+ (uintmax_t) newparent));
+ error = nandfs_update_parent_dir(fvp, newparent);
+ if (!error) {
+ fdnode->nn_inode.i_links_count--;
+ fdnode->nn_flags |= IN_CHANGE;
+ }
+ }
+ error = nandfs_remove_dirent(fdvp, fnode, fcnp);
+ if (!error) {
+ fnode->nn_inode.i_links_count--;
+ fnode->nn_flags |= IN_CHANGE;
+ }
+ fnode->nn_flags &= ~IN_RENAME;
+ }
+ if (fdnode)
+ vput(fdvp);
+ if (fnode)
+ vput(fvp);
+ vrele(ap->a_fvp);
+ return (error);
+
+bad:
+ DPRINTF(VNCALL, ("%s: error:%d\n", __func__, error));
+ if (tnode)
+ vput(NTOV(tnode));
+ vput(NTOV(tdnode));
+out:
+ if (doingdirectory)
+ fnode->nn_flags &= ~IN_RENAME;
+ if (vn_lock(fvp, LK_EXCLUSIVE) == 0) {
+ fnode->nn_inode.i_links_count--;
+ fnode->nn_flags |= IN_CHANGE;
+ fnode->nn_flags &= ~IN_RENAME;
+ vput(fvp);
+ } else
+ vrele(fvp);
+ return (error);
+}
+
+static int
+nandfs_mkdir(struct vop_mkdir_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfs_inode *dir_inode = &dir_node->nn_inode;
+ struct nandfs_node *node;
+ struct nandfsmount *nmp = dir_node->nn_nmp;
+ uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
+ int error;
+
+ DPRINTF(VNCALL, ("%s: dvp %p\n", __func__, dvp));
+
+ if (nandfs_fs_full(dir_node->nn_nandfsdev))
+ return (ENOSPC);
+
+ if (dir_inode->i_links_count >= LINK_MAX)
+ return (EMLINK);
+
+ error = nandfs_node_create(nmp, &node, mode);
+ if (error)
+ return (error);
+
+ node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
+ node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
+
+ *vpp = NTOV(node);
+
+ error = nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
+ cnp->cn_namelen, IFTODT(mode));
+ if (error) {
+ vput(*vpp);
+ return (error);
+ }
+
+ dir_node->nn_inode.i_links_count++;
+ dir_node->nn_flags |= IN_CHANGE;
+
+ error = nandfs_init_dir(NTOV(node), node->nn_ino, dir_node->nn_ino);
+ if (error) {
+ vput(NTOV(node));
+ return (error);
+ }
+
+ DPRINTF(VNCALL, ("created dir vp %p nandnode %p ino %jx\n", *vpp, node,
+ (uintmax_t)node->nn_ino));
+ return (0);
+}
+
+static int
+nandfs_mknod(struct vop_mknod_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vattr *vap = ap->a_vap;
+ uint16_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
+ struct componentname *cnp = ap->a_cnp;
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfsmount *nmp = dir_node->nn_nmp;
+ struct nandfs_node *node;
+ int error;
+
+ if (nandfs_fs_full(dir_node->nn_nandfsdev))
+ return (ENOSPC);
+
+ error = nandfs_node_create(nmp, &node, mode);
+ if (error)
+ return (error);
+ node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
+ node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
+ if (vap->va_rdev != VNOVAL)
+ node->nn_inode.i_special = vap->va_rdev;
+
+ *vpp = NTOV(node);
+
+ if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
+ cnp->cn_namelen, IFTODT(mode))) {
+ vput(*vpp);
+ return (ENOTDIR);
+ }
+
+ node->nn_flags |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+
+ return (0);
+}
+
+static int
+nandfs_symlink(struct vop_symlink_args *ap)
+{
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ uint16_t mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
+ struct componentname *cnp = ap->a_cnp;
+ struct nandfs_node *dir_node = VTON(dvp);
+ struct nandfsmount *nmp = dir_node->nn_nmp;
+ struct nandfs_node *node;
+ int len, error;
+
+ if (nandfs_fs_full(dir_node->nn_nandfsdev))
+ return (ENOSPC);
+
+ error = nandfs_node_create(nmp, &node, S_IFLNK | mode);
+ if (error)
+ return (error);
+ node->nn_inode.i_gid = dir_node->nn_inode.i_gid;
+ node->nn_inode.i_uid = cnp->cn_cred->cr_uid;
+
+ *vpp = NTOV(node);
+
+ if (nandfs_add_dirent(dvp, node->nn_ino, cnp->cn_nameptr,
+ cnp->cn_namelen, IFTODT(mode))) {
+ vput(*vpp);
+ return (ENOTDIR);
+ }
+
+
+ len = strlen(ap->a_target);
+ error = vn_rdwr(UIO_WRITE, *vpp, ap->a_target, len, (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_NOMACCHECK,
+ cnp->cn_cred, NOCRED, NULL, NULL);
+ if (error)
+ vput(*vpp);
+
+ return (error);
+}
+
+static int
+nandfs_readlink(struct vop_readlink_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+
+ return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+static int
+nandfs_rmdir(struct vop_rmdir_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct vnode *dvp = ap->a_dvp;
+ struct componentname *cnp = ap->a_cnp;
+ struct nandfs_node *node, *dnode;
+ uint32_t dflag, flag;
+ int error = 0;
+
+ node = VTON(vp);
+ dnode = VTON(dvp);
+
+ /* Files marked as immutable or append-only cannot be deleted. */
+ if ((node->nn_inode.i_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
+ (dnode->nn_inode.i_flags & APPEND))
+ return (EPERM);
+
+ DPRINTF(VNCALL, ("%s: dvp %p vp %p nandnode %p ino %#jx\n", __func__,
+ dvp, vp, node, (uintmax_t)node->nn_ino));
+
+ if (node->nn_inode.i_links_count < 2)
+ return (EINVAL);
+
+ if (!nandfs_dirempty(vp, dnode->nn_ino, cnp->cn_cred))
+ return (ENOTEMPTY);
+
+ /* Files marked as immutable or append-only cannot be deleted. */
+ dflag = dnode->nn_inode.i_flags;
+ flag = node->nn_inode.i_flags;
+ if ((dflag & APPEND) ||
+ (flag & (NOUNLINK | IMMUTABLE | APPEND))) {
+ return (EPERM);
+ }
+
+ if (vp->v_mountedhere != 0)
+ return (EINVAL);
+
+ nandfs_remove_dirent(dvp, node, cnp);
+ dnode->nn_inode.i_links_count -= 1;
+ dnode->nn_flags |= IN_CHANGE;
+
+ cache_purge(dvp);
+
+ error = nandfs_truncate(vp, (uint64_t)0);
+ if (error)
+ return (error);
+
+ node->nn_inode.i_links_count -= 2;
+ node->nn_flags |= IN_CHANGE;
+
+ cache_purge(vp);
+
+ return (error);
+}
+
+static int
+nandfs_fsync(struct vop_fsync_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ int locked;
+
+ DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp,
+ node, (uintmax_t)node->nn_ino));
+
+ /*
+ * Start syncing vnode only if inode was modified or
+ * there are some dirty buffers
+ */
+ if (VTON(vp)->nn_flags & IN_MODIFIED ||
+ vp->v_bufobj.bo_dirty.bv_cnt) {
+ locked = VOP_ISLOCKED(vp);
+ VOP_UNLOCK(vp, 0);
+ nandfs_wakeup_wait_sync(node->nn_nandfsdev, SYNCER_FSYNC);
+ VOP_LOCK(vp, locked | LK_RETRY);
+ }
+
+ return (0);
+}
+
+static int
+nandfs_bmap(struct vop_bmap_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *nnode = VTON(vp);
+ struct nandfs_device *nandfsdev = nnode->nn_nandfsdev;
+ nandfs_daddr_t l2vmap, v2pmap;
+ int error;
+ int blk2dev = nandfsdev->nd_blocksize / DEV_BSIZE;
+
+ DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx\n", __func__, vp,
+ nnode, (uintmax_t)nnode->nn_ino));
+
+ if (ap->a_bop != NULL)
+ *ap->a_bop = &nandfsdev->nd_devvp->v_bufobj;
+ if (ap->a_bnp == NULL)
+ return (0);
+ if (ap->a_runp != NULL)
+ *ap->a_runp = 0;
+ if (ap->a_runb != NULL)
+ *ap->a_runb = 0;
+
+ /*
+ * Translate all the block sectors into a series of buffers to read
+ * asynchronously from the nandfs device. Note that this lookup may
+ * induce readin's too.
+ */
+
+ /* Get virtual block numbers for the vnode's buffer span */
+ error = nandfs_bmap_lookup(nnode, ap->a_bn, &l2vmap);
+ if (error)
+ return (-1);
+
+ /* Translate virtual block numbers to physical block numbers */
+ error = nandfs_vtop(nnode, l2vmap, &v2pmap);
+ if (error)
+ return (-1);
+
+ /* Note virtual block 0 marks not mapped */
+ if (l2vmap == 0)
+ *ap->a_bnp = -1;
+ else
+ *ap->a_bnp = v2pmap * blk2dev; /* in DEV_BSIZE */
+
+ DPRINTF(VNCALL, ("%s: vp %p nandnode %p ino %#jx lblk %jx -> blk %jx\n",
+ __func__, vp, nnode, (uintmax_t)nnode->nn_ino, (uintmax_t)ap->a_bn,
+ (uintmax_t)*ap->a_bnp ));
+
+ return (0);
+}
+
+static void
+nandfs_force_syncer(struct nandfsmount *nmp)
+{
+
+ nmp->nm_flags |= NANDFS_FORCE_SYNCER;
+ nandfs_wakeup_wait_sync(nmp->nm_nandfsdev, SYNCER_FFORCE);
+}
+
+static int
+nandfs_ioctl(struct vop_ioctl_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ u_long command = ap->a_command;
+ caddr_t data = ap->a_data;
+ struct nandfs_node *node = VTON(vp);
+ struct nandfs_device *nandfsdev = node->nn_nandfsdev;
+ struct nandfsmount *nmp = node->nn_nmp;
+ uint64_t *tab, *cno;
+ struct nandfs_seg_stat *nss;
+ struct nandfs_cpmode *ncpm;
+ struct nandfs_argv *nargv;
+ struct nandfs_cpstat *ncp;
+ int error;
+
+ DPRINTF(VNCALL, ("%s: %x\n", __func__, (uint32_t)command));
+
+ error = priv_check(ap->a_td, PRIV_VFS_MOUNT);
+ if (error)
+ return (error);
+
+ if (nmp->nm_ronly) {
+ switch (command) {
+ case NANDFS_IOCTL_GET_FSINFO:
+ case NANDFS_IOCTL_GET_SUSTAT:
+ case NANDFS_IOCTL_GET_CPINFO:
+ case NANDFS_IOCTL_GET_CPSTAT:
+ case NANDFS_IOCTL_GET_SUINFO:
+ case NANDFS_IOCTL_GET_VINFO:
+ case NANDFS_IOCTL_GET_BDESCS:
+ break;
+ default:
+ return (EROFS);
+ }
+ }
+
+ switch (command) {
+ case NANDFS_IOCTL_GET_FSINFO:
+ error = nandfs_get_fsinfo(nmp, (struct nandfs_fsinfo *)data);
+ break;
+ case NANDFS_IOCTL_GET_SUSTAT:
+ nss = (struct nandfs_seg_stat *)data;
+ error = nandfs_get_seg_stat(nandfsdev, nss);
+ break;
+ case NANDFS_IOCTL_CHANGE_CPMODE:
+ ncpm = (struct nandfs_cpmode *)data;
+ error = nandfs_chng_cpmode(nandfsdev->nd_cp_node, ncpm);
+ nandfs_force_syncer(nmp);
+ break;
+ case NANDFS_IOCTL_GET_CPINFO:
+ nargv = (struct nandfs_argv *)data;
+ error = nandfs_get_cpinfo_ioctl(nandfsdev->nd_cp_node, nargv);
+ break;
+ case NANDFS_IOCTL_DELETE_CP:
+ tab = (uint64_t *)data;
+ error = nandfs_delete_cp(nandfsdev->nd_cp_node, tab[0], tab[1]);
+ nandfs_force_syncer(nmp);
+ break;
+ case NANDFS_IOCTL_GET_CPSTAT:
+ ncp = (struct nandfs_cpstat *)data;
+ error = nandfs_get_cpstat(nandfsdev->nd_cp_node, ncp);
+ break;
+ case NANDFS_IOCTL_GET_SUINFO:
+ nargv = (struct nandfs_argv *)data;
+ error = nandfs_get_segment_info_ioctl(nandfsdev, nargv);
+ break;
+ case NANDFS_IOCTL_GET_VINFO:
+ nargv = (struct nandfs_argv *)data;
+ error = nandfs_get_dat_vinfo_ioctl(nandfsdev, nargv);
+ break;
+ case NANDFS_IOCTL_GET_BDESCS:
+ nargv = (struct nandfs_argv *)data;
+ error = nandfs_get_dat_bdescs_ioctl(nandfsdev, nargv);
+ break;
+ case NANDFS_IOCTL_SYNC:
+ cno = (uint64_t *)data;
+ nandfs_force_syncer(nmp);
+ *cno = nandfsdev->nd_last_cno;
+ error = 0;
+ break;
+ case NANDFS_IOCTL_MAKE_SNAP:
+ cno = (uint64_t *)data;
+ error = nandfs_make_snap(nandfsdev, cno);
+ nandfs_force_syncer(nmp);
+ break;
+ case NANDFS_IOCTL_DELETE_SNAP:
+ cno = (uint64_t *)data;
+ error = nandfs_delete_snap(nandfsdev, *cno);
+ nandfs_force_syncer(nmp);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+
+ return (error);
+}
+
+/*
+ * Whiteout vnode call
+ */
+static int
+nandfs_whiteout(struct vop_whiteout_args *ap)
+{
+ struct vnode *dvp = ap->a_dvp;
+ struct componentname *cnp = ap->a_cnp;
+ int error = 0;
+
+ switch (ap->a_flags) {
+ case LOOKUP:
+ return (0);
+ case CREATE:
+ /* Create a new directory whiteout */
+#ifdef INVARIANTS
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("ufs_whiteout: missing name");
+#endif
+ error = nandfs_add_dirent(dvp, NANDFS_WHT_INO, cnp->cn_nameptr,
+ cnp->cn_namelen, DT_WHT);
+ break;
+
+ case DELETE:
+ /* Remove an existing directory whiteout */
+ cnp->cn_flags &= ~DOWHITEOUT;
+ error = nandfs_remove_dirent(dvp, NULL, cnp);
+ break;
+ default:
+ panic("nandf_whiteout: unknown op: %d", ap->a_flags);
+ }
+
+ return (error);
+}
+
+static int
+nandfs_pathconf(struct vop_pathconf_args *ap)
+{
+ int error;
+
+ error = 0;
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ break;
+ case _PC_NAME_MAX:
+ *ap->a_retval = NAME_MAX;
+ break;
+ case _PC_PATH_MAX:
+ *ap->a_retval = PATH_MAX;
+ break;
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ break;
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ break;
+ case _PC_NO_TRUNC:
+ *ap->a_retval = 1;
+ break;
+ case _PC_ACL_EXTENDED:
+ *ap->a_retval = 0;
+ break;
+ case _PC_ALLOC_SIZE_MIN:
+ *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_bsize;
+ break;
+ case _PC_FILESIZEBITS:
+ *ap->a_retval = 64;
+ break;
+ case _PC_REC_INCR_XFER_SIZE:
+ *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
+ break;
+ case _PC_REC_MAX_XFER_SIZE:
+ *ap->a_retval = -1; /* means ``unlimited'' */
+ break;
+ case _PC_REC_MIN_XFER_SIZE:
+ *ap->a_retval = ap->a_vp->v_mount->mnt_stat.f_iosize;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return (error);
+}
+
+static int
+nandfs_vnlock1(struct vop_lock1_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ int error, vi_locked;
+
+ /*
+ * XXX can vnode go away while we are sleeping?
+ */
+ vi_locked = mtx_owned(&vp->v_interlock);
+ if (vi_locked)
+ VI_UNLOCK(vp);
+ error = NANDFS_WRITELOCKFLAGS(node->nn_nandfsdev,
+ ap->a_flags & LK_NOWAIT);
+ if (vi_locked && !error)
+ VI_LOCK(vp);
+ if (error)
+ return (error);
+
+ error = vop_stdlock(ap);
+ if (error) {
+ NANDFS_WRITEUNLOCK(node->nn_nandfsdev);
+ return (error);
+ }
+
+ return (0);
+}
+
+static int
+nandfs_vnunlock(struct vop_unlock_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+ int error;
+
+ error = vop_stdunlock(ap);
+ if (error)
+ return (error);
+
+ NANDFS_WRITEUNLOCK(node->nn_nandfsdev);
+
+ return (0);
+}
+
+/*
+ * Global vfs data structures
+ */
+struct vop_vector nandfs_vnodeops = {
+ .vop_default = &default_vnodeops,
+ .vop_access = nandfs_access,
+ .vop_advlock = nandfs_advlock,
+ .vop_bmap = nandfs_bmap,
+ .vop_close = nandfs_close,
+ .vop_create = nandfs_create,
+ .vop_fsync = nandfs_fsync,
+ .vop_getattr = nandfs_getattr,
+ .vop_inactive = nandfs_inactive,
+ .vop_cachedlookup = nandfs_lookup,
+ .vop_ioctl = nandfs_ioctl,
+ .vop_link = nandfs_link,
+ .vop_lookup = vfs_cache_lookup,
+ .vop_mkdir = nandfs_mkdir,
+ .vop_mknod = nandfs_mknod,
+ .vop_open = nandfs_open,
+ .vop_pathconf = nandfs_pathconf,
+ .vop_print = nandfs_print,
+ .vop_read = nandfs_read,
+ .vop_readdir = nandfs_readdir,
+ .vop_readlink = nandfs_readlink,
+ .vop_reclaim = nandfs_reclaim,
+ .vop_remove = nandfs_remove,
+ .vop_rename = nandfs_rename,
+ .vop_rmdir = nandfs_rmdir,
+ .vop_whiteout = nandfs_whiteout,
+ .vop_write = nandfs_write,
+ .vop_setattr = nandfs_setattr,
+ .vop_strategy = nandfs_strategy,
+ .vop_symlink = nandfs_symlink,
+ .vop_lock1 = nandfs_vnlock1,
+ .vop_unlock = nandfs_vnunlock,
+};
+
+struct vop_vector nandfs_system_vnodeops = {
+ .vop_default = &default_vnodeops,
+ .vop_close = nandfs_close,
+ .vop_inactive = nandfs_inactive,
+ .vop_reclaim = nandfs_reclaim,
+ .vop_strategy = nandfs_strategy,
+ .vop_fsync = nandfs_fsync,
+ .vop_bmap = nandfs_bmap,
+ .vop_access = VOP_PANIC,
+ .vop_advlock = VOP_PANIC,
+ .vop_create = VOP_PANIC,
+ .vop_getattr = VOP_PANIC,
+ .vop_cachedlookup = VOP_PANIC,
+ .vop_ioctl = VOP_PANIC,
+ .vop_link = VOP_PANIC,
+ .vop_lookup = VOP_PANIC,
+ .vop_mkdir = VOP_PANIC,
+ .vop_mknod = VOP_PANIC,
+ .vop_open = VOP_PANIC,
+ .vop_pathconf = VOP_PANIC,
+ .vop_print = VOP_PANIC,
+ .vop_read = VOP_PANIC,
+ .vop_readdir = VOP_PANIC,
+ .vop_readlink = VOP_PANIC,
+ .vop_remove = VOP_PANIC,
+ .vop_rename = VOP_PANIC,
+ .vop_rmdir = VOP_PANIC,
+ .vop_whiteout = VOP_PANIC,
+ .vop_write = VOP_PANIC,
+ .vop_setattr = VOP_PANIC,
+ .vop_symlink = VOP_PANIC,
+};
+
+static int
+nandfsfifo_close(struct vop_close_args *ap)
+{
+ struct vnode *vp = ap->a_vp;
+ struct nandfs_node *node = VTON(vp);
+
+ DPRINTF(VNCALL, ("%s: vp %p node %p\n", __func__, vp, node));
+
+ mtx_lock(&vp->v_interlock);
+ if (vp->v_usecount > 1)
+ nandfs_itimes_locked(vp);
+ mtx_unlock(&vp->v_interlock);
+
+ return (fifo_specops.vop_close(ap));
+}
+
+struct vop_vector nandfs_fifoops = {
+ .vop_default = &fifo_specops,
+ .vop_fsync = VOP_PANIC,
+ .vop_access = nandfs_access,
+ .vop_close = nandfsfifo_close,
+ .vop_getattr = nandfs_getattr,
+ .vop_inactive = nandfs_inactive,
+ .vop_print = nandfs_print,
+ .vop_read = VOP_PANIC,
+ .vop_reclaim = nandfs_reclaim,
+ .vop_setattr = nandfs_setattr,
+ .vop_write = VOP_PANIC,
+ .vop_lock1 = nandfs_vnlock1,
+ .vop_unlock = nandfs_vnunlock,
+};
+
+int
+nandfs_vinit(struct vnode *vp, uint64_t ino)
+{
+ struct nandfs_node *node;
+
+ ASSERT_VOP_LOCKED(vp, __func__);
+
+ node = VTON(vp);
+
+ /* Check if we're fetching the root */
+ if (ino == NANDFS_ROOT_INO)
+ vp->v_vflag |= VV_ROOT;
+
+ if (ino != NANDFS_GC_INO)
+ vp->v_type = IFTOVT(node->nn_inode.i_mode);
+ else
+ vp->v_type = VREG;
+
+ if (vp->v_type == VFIFO)
+ vp->v_op = &nandfs_fifoops;
+
+ return (0);
+}
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index ed32b1c..05d3a45 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -217,6 +217,8 @@ SUBDIR= ${_3dfx} \
${_mwlfw} \
mxge \
my \
+ ${_nandfs} \
+ ${_nandsim} \
${_ncp} \
${_ncv} \
${_ndis} \
@@ -335,7 +337,6 @@ SUBDIR= ${_3dfx} \
vx \
${_vxge} \
wb \
- ${_wbwd} \
${_wi} \
wlan \
wlan_acl \
@@ -398,6 +399,11 @@ _ipdivert= ipdivert
_ipfw= ipfw
.endif
+.if ${MK_NAND} != "no" || defined(ALL_MODULES)
+_nandfs= nandfs
+_nandsim= nandsim
+.endif
+
.if ${MK_NETGRAPH} != "no" || defined(ALL_MODULES)
_netgraph= netgraph
.endif
@@ -513,7 +519,6 @@ _stg= stg
_streams= streams
_svr4= svr4
_vxge= vxge
-_wbwd= wbwd
_wi= wi
_xe= xe
.if ${MK_ZFS} != "no" || defined(ALL_MODULES)
@@ -708,7 +713,6 @@ _viawd= viawd
_virtio= virtio
_vxge= vxge
_x86bios= x86bios
-_wbwd= wbwd
_wi= wi
_wpi= wpi
.if ${MK_SOURCELESS_UCODE} != "no"
diff --git a/sys/modules/nandfs/Makefile b/sys/modules/nandfs/Makefile
new file mode 100644
index 0000000..3c67539
--- /dev/null
+++ b/sys/modules/nandfs/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../fs/nandfs
+
+KMOD= nandfs
+SRCS= vnode_if.h \
+ bmap.c nandfs_bmap.c nandfs_dir.c nandfs_subr.c nandfs_vfsops.c \
+ nandfs_vnops.c nandfs_alloc.c nandfs_cpfile.c nandfs_dat.c \
+ nandfs_ifile.c nandfs_segment.c nandfs_sufile.c nandfs_buffer.c \
+ nandfs_cleaner.c
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/nandsim/Makefile b/sys/modules/nandsim/Makefile
new file mode 100644
index 0000000..1dc9501
--- /dev/null
+++ b/sys/modules/nandsim/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/nand
+
+KMOD= nandsim
+SRCS= nandsim.c nandsim_chip.c nandsim_swap.c nandsim_ctrl.c nandsim_log.c\
+ bus_if.h device_if.h vnode_if.h nfc_if.h nand_if.h
+MFILES= kern/bus_if.m kern/device_if.m\
+ dev/nand/nfc_if.m dev/nand/nand_if.m
+
+.include <bsd.kmod.mk>
diff --git a/tools/build/options/WITHOUT_NAND b/tools/build/options/WITHOUT_NAND
new file mode 100644
index 0000000..67cadaf
--- /dev/null
+++ b/tools/build/options/WITHOUT_NAND
@@ -0,0 +1,2 @@
+.\" $FreeBSD$
+Set to not build the NAND Flash components.
diff --git a/tools/build/options/WITH_NAND b/tools/build/options/WITH_NAND
new file mode 100644
index 0000000..527d43d
--- /dev/null
+++ b/tools/build/options/WITH_NAND
@@ -0,0 +1,2 @@
+.\" $FreeBSD$
+Set to build the NAND Flash components.
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 *)&sections;
+ 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 *)&sections;
+ 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 */
OpenPOWER on IntegriCloud