summaryrefslogtreecommitdiff
path: root/libnandfs
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /libnandfs
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'libnandfs')
-rw-r--r--libnandfs/NOTICE5
-rw-r--r--libnandfs/calcformat.c24
-rw-r--r--libnandfs/correctauxilliary.c81
-rw-r--r--libnandfs/ecc.c98
-rw-r--r--libnandfs/eraseblock.c50
-rw-r--r--libnandfs/extracttags.c26
-rw-r--r--libnandfs/findfreeblock.c28
-rw-r--r--libnandfs/formatblock.c57
-rw-r--r--libnandfs/getblockstatus.c27
-rw-r--r--libnandfs/hamming31_26.c62
-rw-r--r--libnandfs/init.c102
-rw-r--r--libnandfs/local.h51
-rw-r--r--libnandfs/markblockbad.c42
-rw-r--r--libnandfs/mkfile33
-rw-r--r--libnandfs/open.c253
-rw-r--r--libnandfs/readblock.c36
-rw-r--r--libnandfs/readpage.c52
-rw-r--r--libnandfs/readpageauxilliary.c35
-rw-r--r--libnandfs/reformatblock.c34
-rw-r--r--libnandfs/setget.c101
-rw-r--r--libnandfs/updatepage.c38
-rw-r--r--libnandfs/writeblock.c47
-rw-r--r--libnandfs/writepageauxilliary.c33
23 files changed, 1315 insertions, 0 deletions
diff --git a/libnandfs/NOTICE b/libnandfs/NOTICE
new file mode 100644
index 00000000..d8e20618
--- /dev/null
+++ b/libnandfs/NOTICE
@@ -0,0 +1,5 @@
+Developed 2002, 2003 by Vita Nuova Holdings Limited.
+Copyright © 2002, 2003 Vita Nuova Holdings Limited.
+
+The source and binary code for libnandfs may be used only as part of Inferno,
+unless otherwise agreed with Vita Nuova.
diff --git a/libnandfs/calcformat.c b/libnandfs/calcformat.c
new file mode 100644
index 00000000..498427f5
--- /dev/null
+++ b/libnandfs/calcformat.c
@@ -0,0 +1,24 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+int
+nandfscalcformat(Nandfs *nandfs, long base, long limit, long bootsize, long *baseblock, long *limitblock, long *bootblocks)
+{
+ *baseblock = (base + nandfs->rawblocksize - 1) / nandfs->rawblocksize;
+ if (limit == 0)
+ *limitblock = nandfs->limitblock;
+ else
+ *limitblock = limit / nandfs->rawblocksize;
+ *bootblocks = (bootsize + nandfs->rawblocksize - 1) / nandfs->rawblocksize;
+ if (*bootblocks < 3)
+ *bootblocks = 3;
+ /* sanity checks */
+ if (*limitblock > nandfs->limitblock
+ || *baseblock < nandfs->baseblock
+ || *bootblocks > nandfs->limitblock - nandfs->baseblock)
+ return 0;
+ return 1;
+}
+
diff --git a/libnandfs/correctauxilliary.c b/libnandfs/correctauxilliary.c
new file mode 100644
index 00000000..510aeac4
--- /dev/null
+++ b/libnandfs/correctauxilliary.c
@@ -0,0 +1,81 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+static int
+hammingdistance(uchar a, uchar b)
+{
+ uchar c;
+ int i, k;
+ if (a == b)
+ return 0;
+ c = a ^ b;
+ for (i = 0x80, k = 0; i; i >>= 1)
+ if (c & i)
+ k++;
+ return k;
+}
+
+static int
+allones(uchar *data, int len)
+{
+ while (len-- > 0)
+ if (*data++ != 0xff)
+ return 0;
+ return 1;
+}
+
+LogfsLowLevelReadResult
+_nandfscorrectauxiliary(NandfsAuxiliary *hdr)
+{
+ /*
+ * correct single bit errors, detect more than 1, in
+ * tag, signature
+ * TODO: add nerase and path protection
+ */
+ LogfsLowLevelReadResult e;
+ int x;
+ int min, minx;
+
+ e = LogfsLowLevelReadResultOk;
+
+ min = 8;
+ minx = 0;
+ for (x = 0; x < _nandfsvalidtagscount; x++) {
+ int d = hammingdistance(hdr->tag, _nandfsvalidtags[x]);
+ if (d < min) {
+ min = d;
+ minx = x;
+ if (d == 0)
+ break;
+ }
+ }
+ if (min == 1) {
+ hdr->tag = _nandfsvalidtags[minx];
+ e = LogfsLowLevelReadResultSoftError;
+ }
+ else if (min > 1)
+ e = LogfsLowLevelReadResultHardError;
+ else {
+ if (hdr->tag != LogfsTnone) {
+ ulong tmp = getbig4(hdr->parth);
+ if (tmp != 0xfffffffff && _nandfshamming31_26correct(&tmp)) {
+ putbig4(hdr->parth, tmp);
+ if (e != LogfsLowLevelReadResultOk)
+ e = LogfsLowLevelReadResultSoftError;
+ }
+ tmp = (getbig2(hdr->nerasemagicmsw) << 16) | getbig2(hdr->nerasemagiclsw);
+ if (tmp != 0xffffffff && _nandfshamming31_26correct(&tmp)) {
+ putbig2(hdr->nerasemagicmsw, tmp >> 16);
+ putbig2(hdr->nerasemagiclsw, tmp);
+ if (e != LogfsLowLevelReadResultOk)
+ e = LogfsLowLevelReadResultSoftError;
+ }
+ }
+ else if (allones((uchar *)hdr, sizeof(*hdr)))
+ e = LogfsLowLevelReadResultAllOnes;
+ }
+
+ return e;
+}
diff --git a/libnandfs/ecc.c b/libnandfs/ecc.c
new file mode 100644
index 00000000..1cd86d20
--- /dev/null
+++ b/libnandfs/ecc.c
@@ -0,0 +1,98 @@
+#include "lib9.h"
+#include "nandecc.h"
+
+static uchar ecctab[] = {
+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+ 0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0, 0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+ 0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55, 0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+ 0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59, 0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+ 0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc, 0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+ 0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65, 0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+ 0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0, 0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+ 0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc, 0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+ 0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69, 0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+};
+
+ulong
+nandecc(uchar *buf)
+{
+ int cp, zeros, ones, im;
+ int lp, om;
+ int i;
+
+ cp = 0xff;
+ zeros = 0xff;
+ ones = 0xff;
+ for (i = 0; i < 256; i++) {
+ int tabent = ecctab[buf[i]];
+ cp ^= tabent;
+ if (tabent & 1) {
+ zeros ^= ~i;
+ ones ^= i;
+ }
+ }
+ lp = 0;
+ for (im = 0x80, om = 0x8000; im; im >>= 1, om >>= 1) {
+ if (ones & im)
+ lp |= om;
+ om >>= 1;
+ if (zeros & im)
+ lp |= om;
+ }
+ return (((cp & 0xff) | 3) << 16) | lp;
+}
+
+#define CORRECTABLEMASK 0x545555
+
+NandEccError
+nandecccorrect(uchar *buf, ulong calcecc, ulong *storedecc, int reportbad)
+{
+ ulong xorecc;
+ ulong mask;
+ int k;
+
+ if (calcecc == *storedecc)
+ return NandEccErrorGood;
+ if (reportbad)
+ print("nandecccorrect: calculated ecc %.8lux stored ecc %.8lux\n", calcecc, *storedecc);
+ xorecc = calcecc ^ *storedecc;
+ if (((xorecc ^ (xorecc >> 1)) & CORRECTABLEMASK) == CORRECTABLEMASK) {
+ ulong imask;
+ ushort out;
+ ushort omask;
+ int line, col;
+
+ for (imask = 0x800000, omask = 0x800, out = 0; imask; imask >>= 2, omask >>= 1) {
+ if (xorecc & imask)
+ out |= omask;
+ }
+ line = out & 0xff;
+ col = out >> 9;
+ if (reportbad)
+ print("nandecccorrect: single bit error line %d col %d\n", line, col);
+ buf[line] ^= (1 << col);
+ *storedecc = calcecc;
+ return NandEccErrorOneBit;
+ }
+ for (mask = 0x800000, k = 0; mask; mask >>= 1)
+ if (mask & xorecc)
+ k++;
+ if (k == 1) {
+ if (reportbad)
+ print("nandecccorrect: single bit error in ecc\n");
+ // assume the stored ecc was wrong
+ *storedecc = calcecc;
+ return NandEccErrorOneBitInEcc;
+ }
+ if (reportbad)
+ print("nandecccorrect: 2 bit error\n");
+ return NandEccErrorBad;
+}
+
diff --git a/libnandfs/eraseblock.c b/libnandfs/eraseblock.c
new file mode 100644
index 00000000..2ecd490f
--- /dev/null
+++ b/libnandfs/eraseblock.c
@@ -0,0 +1,50 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfseraseblock(Nandfs *nandfs, long block, void **llsavep, int *markedbad)
+{
+ NandfsBlockData *d;
+ char *errmsg;
+
+ if (markedbad)
+ *markedbad = 0;
+
+ errmsg = (*nandfs->erase)(nandfs->magic, nandfs->rawblocksize * (nandfs->baseblock + block));
+ if (errmsg) {
+ if (nandfs->blockdata) {
+ d = &nandfs->blockdata[block];
+ d->tag = LogfsTworse;
+ nandfs->worseblocks = 1;
+ }
+ if (strcmp(errmsg, Eio) != 0)
+ return errmsg;
+ if (markedbad) {
+ *markedbad = 1;
+ errmsg = nandfsmarkblockbad(nandfs, block);
+ if (strcmp(errmsg, Eio) != 0)
+ return errmsg;
+ return nil;
+ }
+ return errmsg;
+ }
+
+ if (nandfs->blockdata) {
+ ulong *llsave;
+ d = &nandfs->blockdata[block];
+ if (llsavep) {
+ llsave = nandfsrealloc(nil, sizeof(ulong));
+ if (llsave == nil)
+ return Enomem;
+ *llsave = d->nerase;
+ *llsavep = llsave;
+ }
+ d->tag = 0xff;
+ d->path = NandfsPathMask;
+ d->nerase = NandfsNeraseMask;
+ }
+ return nil;
+}
+
diff --git a/libnandfs/extracttags.c b/libnandfs/extracttags.c
new file mode 100644
index 00000000..be3fc2da
--- /dev/null
+++ b/libnandfs/extracttags.c
@@ -0,0 +1,26 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+void
+_nandfsextracttags(NandfsAuxiliary *hdr, NandfsTags *tags)
+{
+ ulong tmp;
+ tmp = (getbig2(hdr->nerasemagicmsw) << 16) | getbig2(hdr->nerasemagiclsw);
+ if (tmp == 0xffffffff) {
+ tags->nerase = 0xffffffff;
+ tags->magic = 0xff;
+ }
+ else {
+ tags->nerase = (tmp >> 6) & 0x3ffff;
+ tags->magic = tmp >> 24;
+ }
+ tmp = getbig4(hdr->parth);
+ if (tmp != 0xffffffff)
+ tags->path = tmp >> 6;
+ else
+ tags->path = 0xffffffff;
+ tags->tag = hdr->tag;
+}
+
diff --git a/libnandfs/findfreeblock.c b/libnandfs/findfreeblock.c
new file mode 100644
index 00000000..e17f729a
--- /dev/null
+++ b/libnandfs/findfreeblock.c
@@ -0,0 +1,28 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+long
+nandfsfindfreeblock(Nandfs *nandfs, long *freeblocksp)
+{
+ long bestnewblock;
+ long bestnerase;
+ long i;
+
+ if (freeblocksp)
+ *freeblocksp = 0;
+ for (i = 0, bestnewblock = -1, bestnerase = 0x7fffffff; i < nandfs->ll.blocks; i++) {
+ long nerase;
+ if (nandfsgettag(nandfs, i) == LogfsTnone) {
+ if (freeblocksp) {
+ (*freeblocksp)++;
+ }
+ if ((nerase = nandfsgetnerase(nandfs, i)) < bestnerase) {
+ bestnewblock = i;
+ bestnerase = nerase;
+ }
+ }
+ }
+ return bestnewblock;
+}
diff --git a/libnandfs/formatblock.c b/libnandfs/formatblock.c
new file mode 100644
index 00000000..8f37e2c9
--- /dev/null
+++ b/libnandfs/formatblock.c
@@ -0,0 +1,57 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsformatblock(Nandfs *nandfs, long absblock, uchar tag, ulong path, long baseblock, long sizeinblocks, int xcount, long *xdata, void *llsave, int *markedbad)
+{
+ int page;
+ char *rv;
+ NandfsTags t;
+ int ppb;
+
+ if (markedbad)
+ *markedbad = 0;
+
+ t.tag = tag;
+ t.magic = LogfsMagic;
+ t.nerase = *(ulong *)llsave < NandfsNeraseMask ? *(ulong *)llsave + 1 : 1;
+
+ ppb = 1 << nandfs->ll.l2pagesperblock;
+ for (page = 0, rv = nil; rv == nil && page < ppb; page++) {
+ if (tag == LogfsTboot && page > 0 && page < xcount + 3) {
+ switch (page) {
+ case 1:
+ t.path = baseblock;
+ break;
+ case 2:
+ t.path = sizeinblocks;
+ break;
+ default:
+ t.path = xdata[page - 3];
+ break;
+ }
+ }
+ else
+ t.path = path;
+ rv = nandfswritepageauxiliary(nandfs, &t, absblock, page);
+ if (rv)
+ break;
+ }
+
+ if (rv) {
+ if (strcmp(rv, Eio) != 0)
+ return rv;
+ if (markedbad) {
+ *markedbad = 1;
+ rv = nandfsmarkabsblockbad(nandfs, absblock);
+ if (strcmp(rv, Eio) != 0)
+ return rv;
+ return nil;
+ }
+ return rv;
+ }
+
+ return nil;
+}
diff --git a/libnandfs/getblockstatus.c b/libnandfs/getblockstatus.c
new file mode 100644
index 00000000..0ebaf5e1
--- /dev/null
+++ b/libnandfs/getblockstatus.c
@@ -0,0 +1,27 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsgetblockstatus(Nandfs *nandfs, long absblock, int *magicfound, void **llsavep, LogfsLowLevelReadResult *result)
+{
+ NandfsTags tags;
+ char *errmsg;
+ ulong *llsave;
+
+ errmsg = nandfsreadpageauxiliary(nandfs, &tags, absblock, 0, 1, result);
+
+ *magicfound = tags.magic == LogfsMagic;
+
+ if (llsavep) {
+ llsave = nandfsrealloc(nil, sizeof(ulong));
+ if (llsave == nil)
+ return Enomem;
+ *llsave = tags.nerase;
+ *llsavep = llsave;
+ }
+
+ return errmsg;
+}
+
diff --git a/libnandfs/hamming31_26.c b/libnandfs/hamming31_26.c
new file mode 100644
index 00000000..a94262b7
--- /dev/null
+++ b/libnandfs/hamming31_26.c
@@ -0,0 +1,62 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+
+static unsigned long row4 = 0x001fffc0;
+static unsigned long row3 = 0x0fe03fc0;
+static unsigned long row2 = 0x71e3c3c0;
+static unsigned long row1 = 0xb66cccc0;
+static unsigned long row0 = 0xdab55540;
+
+static char map[] = {
+ -5, -4, 0, -3, 1, 2, 3, -2,
+ 4, 5, 6, 7, 8, 9, 10, -1, 11,
+ 12, 13, 14, 15, 16, 17, 18,
+ 19, 20, 21, 22, 23, 24, 25,
+};
+
+#define mashbits(rown) \
+ c = (in) & (rown); \
+ c ^= c >> 16; \
+ c ^= c >> 8; \
+ c ^= c >> 4; \
+ c ^= c >> 2; \
+ c = (c ^ (c >> 1)) & 1; \
+
+static uchar
+_nandfshamming31_26calcparity(ulong in)
+{
+ ulong c;
+ uchar out;
+ mashbits(row4); out = c;
+ mashbits(row3); out = (out << 1) | c;
+ mashbits(row2); out = (out << 1) | c;
+ mashbits(row1); out = (out << 1) | c;
+ mashbits(row0); out = (out << 1) | c;
+ return out;
+}
+
+ulong
+_nandfshamming31_26calc(ulong in)
+{
+ in &= 0xffffffc0;
+ return in | _nandfshamming31_26calcparity(in);
+}
+
+int
+_nandfshamming31_26correct(ulong *in)
+{
+ uchar eparity, parity;
+ ulong e;
+ eparity = _nandfshamming31_26calcparity(*in);
+ parity = (*in) & 0x1f;
+ e = eparity ^ parity;
+ if (e == 0)
+ return 0;
+ e--;
+ if (map[e] < 0)
+ return 1; // error in parity bits
+ e = map[e];
+ *in ^= 1 << (31 - e);
+ return 1;
+}
diff --git a/libnandfs/init.c b/libnandfs/init.c
new file mode 100644
index 00000000..ebfd6a67
--- /dev/null
+++ b/libnandfs/init.c
@@ -0,0 +1,102 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+uchar _nandfsvalidtags[] = {
+ LogfsTnone,
+ LogfsTboot,
+ LogfsTlog,
+ LogfsTdata,
+};
+
+int _nandfsvalidtagscount = nelem(_nandfsvalidtags);
+
+static int
+l2(long n)
+{
+ int i;
+ for (i = 0; i < 32; i++)
+ if ((1 << i) >= n)
+ return i;
+ return 0;
+}
+
+char *
+nandfsinit(void *magic, long rawsize, long rawblocksize,
+ char *(*read)(void *magic, void *buf, long nbytes, ulong offset),
+ char *(*write)(void *magic, void *buf, long nbytes, ulong offset),
+ char *(*erase)(void *magic, long blockaddr),
+ char *(*sync)(void *magic),
+ LogfsLowLevel **llp)
+{
+ Nandfs *nandfs;
+ nandfs = nandfsrealloc(nil, sizeof(*nandfs));
+ if (nandfs == nil)
+ return Enomem;
+ if (rawblocksize % NandfsFullSize)
+ return "unsupported block size";
+ if (rawsize % rawblocksize)
+ return "size not multiple of block size";
+ nandfs->read = read;
+ nandfs->write = write;
+ nandfs->erase = erase;
+ nandfs->sync = sync;
+ nandfs->magic = magic;
+ nandfs->limitblock = rawsize / rawblocksize;
+//print("rawsize %ld\n", rawsize);
+//print("rawblocksize %ld\n", rawblocksize);
+//print("limitblock %ld\n", nandfs->limitblock);
+ nandfs->rawblocksize = rawblocksize;
+ /* fill in upper interface */
+ nandfs->ll.pathbits = NandfsPathBits;
+ nandfs->ll.blocks = 0;
+ nandfs->ll.l2pagesize = NandfsL2PageSize;
+ nandfs->ll.l2pagesperblock = l2(rawblocksize / NandfsFullSize);
+ nandfs->ll.open = (LOGFSOPENFN *)nandfsopen;
+ nandfs->ll.getblocktag = (LOGFSGETBLOCKTAGFN *)nandfsgettag;
+ nandfs->ll.setblocktag = (LOGFSSETBLOCKTAGFN *)nandfssettag;
+ nandfs->ll.getblockpath = (LOGFSGETBLOCKPATHFN *)nandfsgetpath;
+ nandfs->ll.setblockpath = (LOGFSSETBLOCKPATHFN *)nandfssetpath;
+ nandfs->ll.getblockpartialformatstatus = (LOGFSGETBLOCKPARTIALFORMATSTATUSFN *)nandfsgetblockpartialformatstatus;
+ nandfs->ll.findfreeblock = (LOGFSFINDFREEBLOCKFN *)nandfsfindfreeblock;
+ nandfs->ll.readpagerange = (LOGFSREADPAGERANGEFN *)nandfsreadpagerange;
+ nandfs->ll.writepage = (LOGFSWRITEPAGEFN *)nandfswritepage;
+ nandfs->ll.readblock = (LOGFSREADBLOCKFN *)nandfsreadblock;
+ nandfs->ll.writeblock = (LOGFSWRITEBLOCKFN *)nandfswriteblock;
+ nandfs->ll.eraseblock = (LOGFSERASEBLOCKFN *)nandfseraseblock;
+ nandfs->ll.formatblock = (LOGFSFORMATBLOCKFN *)nandfsformatblock;
+ nandfs->ll.reformatblock = (LOGFSREFORMATBLOCKFN *)nandfsreformatblock;
+ nandfs->ll.markblockbad = (LOGFSMARKBLOCKBADFN *)nandfsmarkblockbad;
+ nandfs->ll.getbaseblock = (LOGFSGETBASEBLOCKFN *)nandfsgetbaseblock;
+ nandfs->ll.getblocksize = (LOGFSGETBLOCKSIZEFN *)nandfsgetblocksize;
+ nandfs->ll.calcrawaddress = (LOGFSCALCRAWADDRESSFN *)nandfscalcrawaddress;
+ nandfs->ll.getblockstatus = (LOGFSGETBLOCKSTATUSFN *)nandfsgetblockstatus;
+ nandfs->ll.calcformat = (LOGFSCALCFORMATFN *)nandfscalcformat;
+ nandfs->ll.getopenstatus = (LOGFSGETOPENSTATUSFN *)nandfsgetopenstatus;
+ nandfs->ll.free = (LOGFSFREEFN *)nandfsfree;
+ nandfs->ll.sync = (LOGFSSYNCFN *)nandfssync;
+ *llp = (LogfsLowLevel *)nandfs;
+ return nil;
+}
+
+void
+nandfsfree(Nandfs *nandfs)
+{
+ if (nandfs) {
+ nandfsfreemem(nandfs->blockdata);
+ nandfsfreemem(nandfs);
+ }
+}
+
+void
+nandfssetmagic(Nandfs *nandfs, void *magic)
+{
+ nandfs->magic = magic;
+}
+
+char *
+nandfssync(Nandfs *nandfs)
+{
+ return (*nandfs->sync)(nandfs->magic);
+}
diff --git a/libnandfs/local.h b/libnandfs/local.h
new file mode 100644
index 00000000..d5b4eecb
--- /dev/null
+++ b/libnandfs/local.h
@@ -0,0 +1,51 @@
+typedef struct NandfsBlockData {
+ ulong path;
+ short tag;
+ ulong nerase;
+ int partial;
+} NandfsBlockData;
+
+struct Nandfs {
+ LogfsLowLevel ll;
+ char *(*read)(void *magic, void *buf, long nbytes, ulong offset);
+ char *(*write)(void *magic, void *buf, long nbytes, ulong offset);
+ char *(*erase)(void *magic, long blockaddr);
+ char *(*sync)(void *magic);
+ void *magic;
+ long rawblocksize;
+ long baseblock;
+ long limitblock;
+ NandfsBlockData *blockdata;
+ int trace;
+ int worseblocks;
+ int printbad;
+};
+
+typedef struct NandfsAuxiliary {
+ uchar parth[4]; // ggpppppp pppppppp pppppppp pp1hhhhh (bigendian) self-protected
+ uchar tag; // self-protecting
+ uchar blockstatus; // self-protecting
+ uchar nerasemagicmsw[2]; // see nerasemagiclsw
+ uchar ecc2[3]; // self-protecting
+ uchar nerasemagiclsw[2]; // mmmmmm mmeeeeee eeeeeeeeee ee1hhhhh (bigendian) self-protected
+ uchar ecc1[3]; // self-protecting
+} NandfsAuxiliary;
+
+#define getbig2(p) (((p)[0] << 8) | (p)[1])
+#define getbig4(p) (((p)[0] << 24) | ((p)[1] << 16) | ((p)[2] << 8) | (p)[3])
+#define getlittle3(p) (((p)[2] << 16) | ((p)[1] << 8) | (p)[0])
+#define putlittle3(p, q) ((p)[0] = (q), (p)[1] = (q) >> 8, (p)[2] = (q) >> 16)
+#define putbig2(p, q) ((p)[0] = (q) >> 8, (p)[1] = (q))
+#define putbig4(p, q) ((p)[0] = (q) >> 24, (p)[1] = (q) >> 16, (p)[2] = (q) >> 8, (p)[3] = (q))
+
+LogfsLowLevelReadResult _nandfscorrectauxiliary(NandfsAuxiliary *hdr);
+
+extern uchar _nandfsvalidtags[];
+extern int _nandfsvalidtagscount;
+
+ulong _nandfshamming31_26calc(ulong in);
+int _nandfshamming31_26correct(ulong *in);
+
+void _nandfsextracttags(NandfsAuxiliary *hdr, NandfsTags *tags);
+
+extern char Enomem[], Eperm[], Eio[];
diff --git a/libnandfs/markblockbad.c b/libnandfs/markblockbad.c
new file mode 100644
index 00000000..6414c8d2
--- /dev/null
+++ b/libnandfs/markblockbad.c
@@ -0,0 +1,42 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsmarkabsblockbad(Nandfs *nandfs, long absblock)
+{
+ NandfsAuxiliary hdr;
+ int page;
+ int ppb;
+
+ memset(&hdr, 0xff, sizeof(hdr));
+ hdr.blockstatus = 0xf0; // late failure
+
+ ppb = 1 << nandfs->ll.l2pagesperblock;
+ for (page = 0; page < ppb; page++) {
+ char *errmsg = (*nandfs->write)(nandfs->magic, &hdr, sizeof(hdr), nandfs->rawblocksize * absblock + page * NandfsFullSize + NandfsPageSize);
+ if (errmsg && strcmp(errmsg, Eio) != 0)
+ return errmsg;
+ }
+
+ return nil;
+}
+
+char *
+nandfsmarkblockbad(Nandfs *nandfs, long block)
+{
+ char *errmsg;
+ errmsg = nandfsmarkabsblockbad(nandfs, block + nandfs->baseblock);
+ if (errmsg)
+ return errmsg;
+
+ if (nandfs->blockdata) {
+ NandfsBlockData *d;
+ d = &nandfs->blockdata[block];
+ d->tag = LogfsTbad;
+ }
+
+ return nil;
+}
+
diff --git a/libnandfs/mkfile b/libnandfs/mkfile
new file mode 100644
index 00000000..72bdde29
--- /dev/null
+++ b/libnandfs/mkfile
@@ -0,0 +1,33 @@
+<../mkconfig
+
+LIB=libnandfs.a
+
+OFILES= \
+ calcformat.$O\
+ correctauxilliary.$O\
+ ecc.$O\
+ eraseblock.$O\
+ extracttags.$O\
+ findfreeblock.$O\
+ formatblock.$O\
+ getblockstatus.$O\
+ hamming31_26.$O\
+ init.$O\
+ markblockbad.$O\
+ open.$O\
+ readblock.$O\
+ readpage.$O\
+ readpageauxilliary.$O\
+ reformatblock.$O\
+ setget.$O\
+ updatepage.$O\
+ writeblock.$O\
+ writepageauxilliary.$O\
+
+HFILES=\
+ $ROOT/include/nandfs.h \
+ $ROOT/include/logfs.h \
+ local.h
+
+<$ROOT/mkfiles/mksyslib-$SHELLTYPE
+
diff --git a/libnandfs/open.c b/libnandfs/open.c
new file mode 100644
index 00000000..12b49849
--- /dev/null
+++ b/libnandfs/open.c
@@ -0,0 +1,253 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsopen(Nandfs *nandfs, long base, long limit, int trace, int xcount, long *xdata)
+{
+ NandfsBlockData *blockdata = nil;
+ long u;
+ ulong badones, goodones;
+ char *errmsg;
+ long possiblebaseblock, possiblesize;
+ long baseblock, limitblock;
+ int ppb;
+
+ if (trace > 1)
+ print("nandfsopen: base %ld limit %ld ppb %d\n", base, limit, 1 << nandfs->ll.l2pagesperblock);
+
+ if (nandfs->blockdata)
+ return Eperm;
+
+ if (base % nandfs->rawblocksize)
+ return Ebadarg;
+ baseblock = base / nandfs->rawblocksize;
+
+ if (limit == 0)
+ limitblock = nandfs->limitblock;
+ else if (limit % nandfs->rawblocksize)
+ return Ebadarg;
+ else
+ limitblock = limit / nandfs->rawblocksize;
+
+ if (trace > 1)
+ print("nandfsopen: baseblock %ld limitblock %ld\n", baseblock, limitblock);
+
+ possiblebaseblock = 0;
+ possiblesize = 0;
+
+ /*
+ * search for Tboot block which will reveal the parameters
+ */
+ nandfs->baseblock = 0;
+
+ for (u = baseblock; u < limitblock; u++) {
+ NandfsTags tags;
+ LogfsLowLevelReadResult e;
+ int p;
+ int lim;
+
+ lim = xcount + 3;
+
+ for (p = 0; p < lim; p++) {
+ errmsg = nandfsreadpageauxiliary(nandfs, &tags, u, p, 1, &e);
+ if (errmsg)
+ goto error;
+ if (e != LogfsLowLevelReadResultOk || tags.magic != LogfsMagic || tags.tag != LogfsTboot)
+ break;
+ if (trace > 1)
+ print("block %lud/%d: 0x%.lux\n", u, p, tags.path);
+ switch (p) {
+ case 1:
+ possiblebaseblock = tags.path;
+ break;
+ case 2:
+ possiblesize = tags.path;
+ break;
+ default:
+ xdata[p - 3] = tags.path;
+ break;
+ }
+ }
+ if (p == lim)
+ break;
+ }
+
+ if (u >= limitblock) {
+ errmsg = "no valid boot blocks found";
+ goto error;
+ }
+
+ if (possiblebaseblock < baseblock
+ || possiblebaseblock >= limitblock
+ || possiblebaseblock + possiblesize > limitblock
+ || possiblesize == 0) {
+ errmsg = "embedded parameters out of range";
+ goto error;
+ }
+
+ baseblock = possiblebaseblock;
+ limitblock = possiblebaseblock + possiblesize;
+
+ if (trace > 0) {
+ int x;
+ print("nandfs filesystem detected: base %lud limit %lud",
+ baseblock, limitblock);
+ for (x = 0; x < xcount; x++)
+ print(" data%d %ld", x, xdata[x]);
+ print("\n");
+ }
+
+ blockdata = nandfsrealloc(nil, (limitblock - baseblock) * sizeof(NandfsBlockData));
+ if (blockdata == nil) {
+ errmsg = Enomem;
+ goto error;
+ }
+ /*
+ * sanity check
+ * check the partition until 10 good blocks have been found
+ * check that bad blocks represent 10% or less
+ */
+
+ badones = goodones = 0;
+ ppb = 1 << nandfs->ll.l2pagesperblock;
+ for (u = baseblock; u < limitblock; u++) {
+ LogfsLowLevelReadResult firste, laste;
+ NandfsTags firsttags, lasttags;
+ errmsg = nandfsreadpageauxiliary(nandfs, &firsttags, u, 0, 1, &firste);
+ if (errmsg)
+ goto error;
+ errmsg = nandfsreadpageauxiliary(nandfs, &lasttags, u, ppb - 1, 1, &laste);
+ if (errmsg)
+ goto error;
+ if (firste == LogfsLowLevelReadResultBad || laste == LogfsLowLevelReadResultBad)
+ continue;
+ if (firste == LogfsLowLevelReadResultOk && laste == LogfsLowLevelReadResultOk && firsttags.magic == LogfsMagic &&
+ lasttags.magic == LogfsMagic)
+ goodones++;
+ else
+ badones++;
+ if (badones == 0 && goodones >= 10)
+ break;
+ }
+
+ if (badones * 10 > goodones) {
+ errmsg = "most likely not a Log Filesystem";
+ goto error;
+ }
+
+ for (u = baseblock; u < limitblock; u++) {
+ int erased, partial;
+ LogfsLowLevelReadResult firste, laste;
+ NandfsTags firsttags, lasttags, newtags;
+ int markedbad;
+ errmsg = nandfsreadpageauxiliary(nandfs, &firsttags, u, 0, 1, &firste);
+ if (errmsg)
+ goto error;
+ errmsg = nandfsreadpageauxiliary(nandfs, &lasttags, u, ppb - 1, 1, &laste);
+ if (errmsg)
+ goto error;
+ if (trace > 1)
+ print("%lud: ", u);
+ if (firste == LogfsLowLevelReadResultBad || laste == LogfsLowLevelReadResultBad) {
+ if (trace > 1)
+ print("bad\n");
+ blockdata[u - baseblock].tag = LogfsTbad;
+ continue;
+ }
+ newtags = firsttags;
+ erased = 0;
+ partial = 0;
+ if (firsttags.tag != lasttags.tag) {
+ partial = 1;
+ if (trace > 1)
+ print("partially written\n");
+ /*
+ * partially written block
+ * if Tboot, then it is either
+ * a failure during logfsformat() - well, we never got started, so give up
+ * a failure during blocktransfer() - erase it as the transfer was not completed
+ * tell the difference by the presence of another block with the same path
+ * if Tnone, then it's a no brainer
+ * if anything else, leave alone
+ */
+ if (newtags.tag == LogfsTnone) {
+ newtags.tag = LogfsTnone;
+ newtags.path = NandfsPathMask;
+ errmsg = nandfseraseblock(nandfs, u, nil, &markedbad);
+ if (errmsg)
+ goto error;
+ if (markedbad) {
+ blockdata[u - baseblock].tag = LogfsTbad;
+ continue;
+ }
+ /* now erased */
+ erased = 1;
+ partial = 0;
+ }
+ }
+ if (!erased && !partial && firste == LogfsLowLevelReadResultAllOnes) {
+ if (trace > 1)
+ print("probably erased");
+ /*
+ * finding erased blocks at this stage is a rare event, so
+ * erase again just in case
+ */
+ newtags.tag = LogfsTnone;
+ newtags.path = NandfsPathMask;
+ newtags.nerase = 1; // what do I do here?
+ errmsg = nandfseraseblock(nandfs, u, nil, &markedbad);
+ if (errmsg)
+ goto error;
+ if (markedbad) {
+ blockdata[u - baseblock].tag = LogfsTbad;
+ continue;
+ }
+ erased = 1;
+ }
+ if (erased) {
+ newtags.magic = 'V';
+ errmsg = nandfseraseblock(nandfs, u, nil, &markedbad);
+ if (errmsg)
+ goto error;
+ if (markedbad) {
+ blockdata[u - baseblock].tag = LogfsTbad;
+ continue;
+ }
+ }
+ switch (newtags.tag) {
+ case LogfsTboot:
+ case LogfsTnone:
+ case LogfsTdata:
+ case LogfsTlog:
+ blockdata[u - baseblock].path = newtags.path;
+ blockdata[u - baseblock].tag = newtags.tag;
+ blockdata[u - baseblock].nerase = newtags.nerase;
+ blockdata[u - baseblock].partial = partial;
+ if (trace > 1)
+ print("%s 0x%.8lux %lud\n",
+ logfstagname(blockdata[u - baseblock].tag),
+ blockdata[u - baseblock].path,
+ blockdata[u - baseblock].nerase);
+ continue;
+ }
+ break;
+ }
+ nandfs->ll.blocks = u - baseblock;
+ nandfs->baseblock = baseblock;
+ nandfs->blockdata = nandfsrealloc(nil, nandfs->ll.blocks * sizeof(NandfsBlockData));
+ if (nandfs->blockdata == nil) {
+ errmsg = Enomem;
+ goto error;
+ }
+ nandfs->trace = trace;
+ memmove(nandfs->blockdata, blockdata, sizeof(*nandfs->blockdata) * nandfs->ll.blocks);
+ nandfsfreemem(blockdata);
+ if (trace > 0)
+ print("nandfsopen: success\n");
+ return nil;
+error:
+ nandfsfreemem(blockdata);
+ return errmsg;
+}
diff --git a/libnandfs/readblock.c b/libnandfs/readblock.c
new file mode 100644
index 00000000..cbf3222b
--- /dev/null
+++ b/libnandfs/readblock.c
@@ -0,0 +1,36 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsreadblock(Nandfs *nandfs, void *buf, long block, LogfsLowLevelReadResult *blocke)
+{
+ int p;
+ uchar *bp;
+ int ppb;
+
+ *blocke = LogfsLowLevelReadResultOk;
+ ppb = 1 << nandfs->ll.l2pagesperblock;
+ for (p = 0, bp = buf; p < ppb; p++, bp += NandfsPageSize) {
+ LogfsLowLevelReadResult e;
+ char *errmsg;
+ errmsg = nandfsreadpage(nandfs, bp, nil, block, p, nandfs->printbad, &e);
+ if (errmsg)
+ return errmsg;
+ switch (e) {
+ case LogfsLowLevelReadResultOk:
+ break;
+ case LogfsLowLevelReadResultSoftError:
+ if (*blocke == LogfsLowLevelReadResultOk)
+ *blocke = LogfsLowLevelReadResultSoftError;
+ break;
+ case LogfsLowLevelReadResultHardError:
+ if (*blocke == LogfsLowLevelReadResultOk || *blocke == LogfsLowLevelReadResultSoftError)
+ *blocke = LogfsLowLevelReadResultHardError;
+ break;
+ }
+ }
+
+ return nil;
+}
diff --git a/libnandfs/readpage.c b/libnandfs/readpage.c
new file mode 100644
index 00000000..9ae3ed43
--- /dev/null
+++ b/libnandfs/readpage.c
@@ -0,0 +1,52 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "nandecc.h"
+#include "local.h"
+
+char *
+nandfsreadpage(Nandfs *nandfs, void *buf, NandfsTags *tags, long block, int page, int reportbad, LogfsLowLevelReadResult *result)
+{
+ ulong ecc1, ecc2, storedecc1, storedecc2;
+ NandEccError e1, e2;
+ ulong rawoffset;
+ NandfsAuxiliary hdr;
+ char *errmsg;
+
+ rawoffset = nandfs->rawblocksize * (nandfs->baseblock + block) + NandfsFullSize * page;
+ errmsg = (*nandfs->read)(nandfs->magic, buf, NandfsPageSize, rawoffset);
+ if (errmsg)
+ return errmsg;
+ errmsg = (*nandfs->read)(nandfs->magic, &hdr, sizeof(hdr), rawoffset + NandfsPageSize);
+ if (errmsg)
+ return errmsg;
+ ecc1 = nandecc(buf);
+ ecc2 = nandecc((uchar *)buf + 256);
+ storedecc1 = getlittle3(hdr.ecc1);
+ storedecc2 = getlittle3(hdr.ecc2);
+ e1 = nandecccorrect(buf, ecc1, &storedecc1, reportbad);
+ e2 = nandecccorrect((uchar *)buf + 256, ecc2, &storedecc2, reportbad);
+ if (e1 == NandEccErrorBad || e2 == NandEccErrorBad)
+ *result = LogfsLowLevelReadResultHardError;
+ else if (e1 != NandEccErrorGood || e2 != NandEccErrorGood)
+ *result = LogfsLowLevelReadResultSoftError;
+ else
+ *result = LogfsLowLevelReadResultOk;
+ if (tags) {
+ *result = _nandfscorrectauxiliary(&hdr);
+ _nandfsextracttags(&hdr, tags);
+ }
+ return nil;
+}
+
+char *
+nandfsreadpagerange(Nandfs *nandfs, void *buf, long block, int page, int offset, int count, LogfsLowLevelReadResult *result)
+{
+ char *errmsg;
+ uchar tmpbuf[NandfsPageSize];
+ errmsg = nandfsreadpage(nandfs, tmpbuf, nil, block, page, 1, result);
+ if (errmsg == nil)
+ memcpy(buf, tmpbuf + offset, count);
+ return errmsg;
+}
+
diff --git a/libnandfs/readpageauxilliary.c b/libnandfs/readpageauxilliary.c
new file mode 100644
index 00000000..ba71d2b5
--- /dev/null
+++ b/libnandfs/readpageauxilliary.c
@@ -0,0 +1,35 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+static int
+countzeros(uchar byte)
+{
+ int b, count;
+ for (b = 0x80, count = 0; b; b>>= 1)
+ if ((byte & b) == 0)
+ count++;
+ return count;
+}
+
+char *
+nandfsreadpageauxiliary(Nandfs *nandfs, NandfsTags *tags, long block, int page, int correct, LogfsLowLevelReadResult *result)
+{
+ NandfsAuxiliary hdr;
+ char *rv;
+
+ rv = (*nandfs->read)(nandfs->magic, &hdr, sizeof(hdr), nandfs->rawblocksize * (nandfs->baseblock + block) + page * NandfsFullSize + NandfsPageSize);
+ if (rv)
+ return rv;
+ if (countzeros(hdr.blockstatus) > 2) {
+ *result = LogfsLowLevelReadResultBad;
+ return nil;
+ }
+ if (correct)
+ *result = _nandfscorrectauxiliary(&hdr);
+ else
+ *result = LogfsLowLevelReadResultOk;
+ _nandfsextracttags(&hdr, tags);
+ return nil;
+}
diff --git a/libnandfs/reformatblock.c b/libnandfs/reformatblock.c
new file mode 100644
index 00000000..c0d790bf
--- /dev/null
+++ b/libnandfs/reformatblock.c
@@ -0,0 +1,34 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfsreformatblock(Nandfs *nandfs, long block, uchar tag, ulong path, int xcount, long *xdata, void *llsave, int *markedbad)
+{
+ int bad;
+ char *errmsg;
+ NandfsBlockData *d;
+ long nerase;
+
+ if (nandfs->blockdata == nil)
+ return Eperm;
+
+ nerase = *(ulong *)llsave;
+
+ errmsg = nandfsformatblock(nandfs, block, tag, path,
+ nandfs->baseblock, nandfs->limitblock - nandfs->baseblock, xcount, xdata, &nerase, &bad);
+
+ if (markedbad)
+ *markedbad = bad;
+ if (errmsg)
+ return errmsg;
+
+ d = &nandfs->blockdata[block];
+ d->tag = bad ? LogfsTbad : tag;
+ d->path = path;
+ d->nerase = nerase;
+ d->partial = 0;
+
+ return nil;
+}
diff --git a/libnandfs/setget.c b/libnandfs/setget.c
new file mode 100644
index 00000000..4a25f494
--- /dev/null
+++ b/libnandfs/setget.c
@@ -0,0 +1,101 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+short
+nandfsgettag(Nandfs *nandfs, long block)
+{
+ if (nandfs->blockdata)
+ return nandfs->blockdata[block].tag;
+ return 0;
+}
+
+void
+nandfssettag(Nandfs *nandfs, long block, short tag)
+{
+ if (nandfs->blockdata) {
+ nandfs->blockdata[block].tag = tag;
+ if (tag == LogfsTworse)
+ nandfs->worseblocks = 1;
+ return;
+ }
+}
+
+long
+nandfsgetpath(Nandfs *nandfs, long block)
+{
+ if (nandfs->blockdata)
+ return nandfs->blockdata[block].path;
+ return 0;
+}
+
+void
+nandfssetpath(Nandfs *nandfs, long block, ulong path)
+{
+ if (nandfs->blockdata) {
+ nandfs->blockdata[block].path = path;
+ return;
+ }
+}
+
+long
+nandfsgetnerase(Nandfs *nandfs, long block)
+{
+ if (nandfs->blockdata)
+ return nandfs->blockdata[block].nerase;
+ return 0;
+}
+
+void
+nandfssetnerase(Nandfs *nandfs, long block, ulong nerase)
+{
+ if (nandfs->blockdata) {
+ nandfs->blockdata[block].nerase = nerase;
+ return;
+ }
+}
+
+int
+nandfsgetblockpartialformatstatus(Nandfs *nandfs, long block)
+{
+ if (nandfs->blockdata)
+ return nandfs->blockdata[block].partial;
+ return 0;
+}
+
+void
+nandfssetblockpartialformatstatus(Nandfs *nandfs, long block, int partial)
+{
+ if (nandfs->blockdata) {
+ nandfs->blockdata[block].partial = partial;
+ return;
+ }
+}
+
+long
+nandfsgetbaseblock(Nandfs *nandfs)
+{
+ return nandfs->baseblock;
+}
+
+int
+nandfsgetblocksize(Nandfs *nandfs)
+{
+ return 1 << (nandfs->ll.l2pagesperblock + NandfsL2PageSize);
+}
+
+ulong
+nandfscalcrawaddress(Nandfs *nandfs, long pblock, int dataoffset)
+{
+ int lpage, pageoffset;
+ lpage = dataoffset / NandfsPageSize;
+ pageoffset = dataoffset % NandfsPageSize;
+ return nandfs->rawblocksize * pblock + lpage * NandfsFullSize + pageoffset;
+}
+
+int
+nandfsgetopenstatus(Nandfs *nandfs)
+{
+ return nandfs->blockdata != nil;
+}
diff --git a/libnandfs/updatepage.c b/libnandfs/updatepage.c
new file mode 100644
index 00000000..afe70b79
--- /dev/null
+++ b/libnandfs/updatepage.c
@@ -0,0 +1,38 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "nandecc.h"
+#include "local.h"
+
+char *
+nandfsupdatepage(Nandfs *nandfs, void *buf, ulong path, uchar tag, long block, int page)
+{
+ uchar tbuf[NandfsFullSize];
+ ulong ecc1, ecc2;
+ ulong rawoffset;
+ NandfsAuxiliary *hdr;
+
+ rawoffset = (nandfs->baseblock + block) * nandfs->rawblocksize + page * NandfsFullSize;
+ memmove(tbuf, buf, NandfsPageSize);
+ ecc1 = nandecc(tbuf);
+ ecc2 = nandecc(tbuf + 256);
+ hdr = (NandfsAuxiliary *)(tbuf + NandfsPageSize);
+ memset(hdr, 0xff, sizeof(*hdr));
+ hdr->tag = tag;
+ if (path < NandfsPathMask) {
+ ulong tmp = _nandfshamming31_26calc(path << 6) | (1 << 5);
+ putbig4(hdr->parth, tmp);
+ }
+ putlittle3(hdr->ecc1, ecc1);
+ putlittle3(hdr->ecc2, ecc2);
+ return (*nandfs->write)(nandfs->magic, tbuf, sizeof(tbuf), rawoffset);
+}
+
+char *
+nandfswritepage(Nandfs *nandfs, void *buf, long block, int page)
+{
+ ulong writepath = nandfsgetpath(nandfs, block);
+ uchar writetag = nandfsgettag(nandfs, block);
+//print("block %ld writepath 0x%.8lux writetag 0x%.2ux\n", block, writepath, writetag);
+ return nandfsupdatepage(nandfs, buf, writepath, writetag, block, page);
+}
diff --git a/libnandfs/writeblock.c b/libnandfs/writeblock.c
new file mode 100644
index 00000000..0e4b9c3d
--- /dev/null
+++ b/libnandfs/writeblock.c
@@ -0,0 +1,47 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+char *
+nandfswriteblock(Nandfs *nandfs, void *buf, uchar tag, ulong path, int xcount, long *data, long block)
+{
+ int p;
+ char *errmsg;
+
+ ulong opath = nandfsgetpath(nandfs, block);
+ ulong writepath = (~opath | path) & NandfsPathMask;
+ uchar writetag = ~nandfsgettag(nandfs, block) | tag;
+ int ppb = 1 << nandfs->ll.l2pagesperblock;
+
+ for (p = 0; p < ppb; p++) {
+ ulong wp;
+ if (p > 0 && p <= 2 + xcount) {
+ switch (p) {
+ case 1:
+ wp = (~opath | nandfsgetbaseblock(nandfs)) & NandfsPathMask;
+ break;
+ case 2:
+ wp = (~opath | nandfs->ll.blocks) & NandfsPathMask;
+ break;
+ default:
+ wp = (~opath | data[p - 3]) & NandfsPathMask;
+ break;
+ }
+ }
+ else
+ wp = writepath;
+ errmsg = nandfsupdatepage(nandfs, buf, wp, writetag, block, p);
+ if (errmsg)
+ return errmsg;
+#ifdef LOGFSTEST
+ if (logfstest.partialupdate && p > 0) {
+ print("skipping pageupdate\n");
+ break;
+ }
+#endif
+ buf = (uchar *)buf + NandfsPageSize;
+ }
+
+ return nil;
+}
diff --git a/libnandfs/writepageauxilliary.c b/libnandfs/writepageauxilliary.c
new file mode 100644
index 00000000..6d2d6b87
--- /dev/null
+++ b/libnandfs/writepageauxilliary.c
@@ -0,0 +1,33 @@
+#include "lib9.h"
+#include "logfs.h"
+#include "nandfs.h"
+#include "local.h"
+
+/*
+ * update the tags in a page's auxiliary area
+ * only touch the fields if they contain some zeros, and compute the hamming codes
+ * as well
+ */
+
+char *
+nandfswritepageauxiliary(Nandfs *nandfs, NandfsTags *tags, long absblock, int page)
+{
+ NandfsAuxiliary hdr;
+ ulong tmp;
+ ushort htmp;
+
+ memset(&hdr, 0xff, sizeof(hdr));
+ if (tags->path < NandfsPathMask) {
+ tmp = _nandfshamming31_26calc((tags->path << 6)) | (1 << 5);
+ putbig4(hdr.parth, tmp);
+ }
+ if (tags->nerase < NandfsNeraseMask || tags->magic != 0xff) {
+ tmp = _nandfshamming31_26calc((tags->magic << 24) | (tags->nerase << 6)) | (1 << 5);
+ htmp = tmp >> 16;
+ putbig2(hdr.nerasemagicmsw, htmp);
+ putbig2(hdr.nerasemagiclsw, tmp);
+ }
+ if (tags->tag != 0xff)
+ hdr.tag = tags->tag;
+ return (*nandfs->write)(nandfs->magic, &hdr, sizeof(hdr), nandfs->rawblocksize * absblock + page * NandfsFullSize + NandfsPageSize);
+}