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
This commit is contained in:
Grzegorz Bernacki 2012-05-17 10:11:18 +00:00
parent cac3dcd5f9
commit 7f725bcd5c
101 changed files with 28313 additions and 6 deletions

View File

@ -122,6 +122,8 @@
mpilib
..
..
nand
..
ofw
..
pbio
@ -154,6 +156,8 @@
..
msdosfs
..
nandfs
..
nfs
..
ntfs

View File

@ -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 \

View File

@ -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

9
lib/libnandfs/Makefile Normal file
View File

@ -0,0 +1,9 @@
# $FreeBSD$
LIB= nandfs
SRCS+= nandfs.c
INCS= libnandfs.h
CFLAGS += -I${.CURDIR}
.include <bsd.lib.mk>

65
lib/libnandfs/libnandfs.h Normal file
View File

@ -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 */

247
lib/libnandfs/nandfs.c Normal file
View File

@ -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);
}

View File

@ -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>

1041
lib/libstand/nandfs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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

10
sbin/nandfs/Makefile Normal file
View File

@ -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>

112
sbin/nandfs/lssnap.c Normal file
View File

@ -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);
}

80
sbin/nandfs/mksnap.c Normal file
View File

@ -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);
}

75
sbin/nandfs/nandfs.8 Normal file
View File

@ -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 .

74
sbin/nandfs/nandfs.c Normal file
View File

@ -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);
}

40
sbin/nandfs/nandfs.h Normal file
View File

@ -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 */

87
sbin/nandfs/rmsnap.c Normal file
View File

@ -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);
}

View File

@ -0,0 +1,9 @@
# $FreeBSD$
PROG= newfs_nandfs
MAN= newfs_nandfs.8
LDADD+= -lgeom
DPADD+= ${LIBGEOM}
.include <bsd.prog.mk>

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -250,6 +250,8 @@ MAN= aac.4 \
mwlfw.4 \
mxge.4 \
my.4 \
nand.4 \
nandsim.4 \
natm.4 \
natmip.4 \
ncr.4 \

140
share/man/man4/nand.4 Normal file
View File

@ -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 .

184
share/man/man4/nandsim.4 Normal file
View File

@ -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 .

View File

@ -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

129
share/man/man5/nandfs.5 Normal file
View File

@ -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 .

View File

@ -426,6 +426,7 @@ __DEFAULT_NO_OPTIONS = \
ICONV \
IDEA \
LIBCPLUSPLUS \
NAND \
OFED \
SHARED_TOOLCHAIN

View File

@ -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

View File

@ -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

View File

@ -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).

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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

832
sys/dev/nand/nand.c Normal file
View File

@ -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);
}

385
sys/dev/nand/nand.h Normal file
View File

@ -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_ */

273
sys/dev/nand/nand_bbt.c Normal file
View File

@ -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);
}

413
sys/dev/nand/nand_cdev.c Normal file
View File

@ -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);
}

90
sys/dev/nand/nand_dev.h Normal file
View File

@ -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_ */

View File

@ -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_ */

1320
sys/dev/nand/nand_generic.c Normal file

File diff suppressed because it is too large Load Diff

414
sys/dev/nand/nand_geom.c Normal file
View File

@ -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);
}

60
sys/dev/nand/nand_id.c Normal file
View File

@ -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);
}

168
sys/dev/nand/nand_if.m Normal file
View File

@ -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;
};

530
sys/dev/nand/nandbus.c Normal file
View File

@ -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));
}

49
sys/dev/nand/nandbus.h Normal file
View File

@ -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_ */

100
sys/dev/nand/nandbus_if.m Normal file
View File

@ -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;
};

665
sys/dev/nand/nandsim.c Normal file
View File

@ -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);

175
sys/dev/nand/nandsim.h Normal file
View File

@ -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_ */

901
sys/dev/nand/nandsim_chip.c Normal file
View File

@ -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");
}

159
sys/dev/nand/nandsim_chip.h Normal file
View File

@ -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 */

396
sys/dev/nand/nandsim_ctrl.c Normal file
View File

@ -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);
}
}

186
sys/dev/nand/nandsim_log.c Normal file
View File

@ -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;
}
}
}

View File

@ -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 */

389
sys/dev/nand/nandsim_swap.c Normal file
View File

@ -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);
}

View File

@ -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_ */

165
sys/dev/nand/nfc_if.m Normal file
View File

@ -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;

236
sys/dev/nand/nfc_mv.c Normal file
View File

@ -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 */
}

621
sys/fs/nandfs/bmap.c Normal file
View File

@ -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);
}

40
sys/fs/nandfs/bmap.h Normal file
View File

@ -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 */

310
sys/fs/nandfs/nandfs.h Normal file
View File

@ -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_ */

View File

@ -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);
}

230
sys/fs/nandfs/nandfs_bmap.c Normal file
View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

344
sys/fs/nandfs/nandfs_dat.c Normal file
View File

@ -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);
}

314
sys/fs/nandfs/nandfs_dir.c Normal file
View File

@ -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);
}

565
sys/fs/nandfs/nandfs_fs.h Normal file
View File

@ -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 */

View File

@ -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);
}

View File

@ -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_ */

File diff suppressed because it is too large Load Diff

1120
sys/fs/nandfs/nandfs_subr.c Normal file

File diff suppressed because it is too large Load Diff

238
sys/fs/nandfs/nandfs_subr.h Normal file
View File

@ -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_ */

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

2455
sys/fs/nandfs/nandfs_vnops.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -0,0 +1,2 @@
.\" $FreeBSD$
Set to not build the NAND Flash components.

View File

@ -0,0 +1,2 @@
.\" $FreeBSD$
Set to build the NAND Flash components.

View File

@ -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

View File

@ -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>

230
usr.sbin/nandsim/nandsim.8 Normal file
View File

@ -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 .

1397
usr.sbin/nandsim/nandsim.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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_ */

View File

@ -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]

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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 */

Some files were not shown because too many files have changed in this diff Show More