174a4d8c2SCharles.Forsyth /*
274a4d8c2SCharles.Forsyth * basic Flash Translation Layer driver
374a4d8c2SCharles.Forsyth * see for instance the Intel technical paper
474a4d8c2SCharles.Forsyth * ``Understanding the Flash Translation Layer (FTL) Specification''
574a4d8c2SCharles.Forsyth * Order number 297816-001 (online at www.intel.com)
674a4d8c2SCharles.Forsyth *
774a4d8c2SCharles.Forsyth * a public driver by David Hinds, dhinds@allegro.stanford.edu
874a4d8c2SCharles.Forsyth * further helps with some details.
974a4d8c2SCharles.Forsyth *
1074a4d8c2SCharles.Forsyth * this driver uses the common simplification of never storing
1174a4d8c2SCharles.Forsyth * the VBM on the medium (a waste of precious flash!) but
1274a4d8c2SCharles.Forsyth * rather building it on the fly as the block maps are read.
1374a4d8c2SCharles.Forsyth *
1474a4d8c2SCharles.Forsyth * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@caldo.demon.co.uk)
1574a4d8c2SCharles.Forsyth * This driver may be used or adapted by anyone for any non-commercial purpose.
1674a4d8c2SCharles.Forsyth *
1774a4d8c2SCharles.Forsyth * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (charles@vitanuova.com)
1874a4d8c2SCharles.Forsyth *
1974a4d8c2SCharles.Forsyth * C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies
2074a4d8c2SCharles.Forsyth * to use this driver freely for any Inferno-related purposes whatever,
2174a4d8c2SCharles.Forsyth * including commercial applications.
2274a4d8c2SCharles.Forsyth *
2374a4d8c2SCharles.Forsyth * TO DO:
2474a4d8c2SCharles.Forsyth * check error handling details for get/put flash
2574a4d8c2SCharles.Forsyth * bad block handling
2674a4d8c2SCharles.Forsyth * reserved space in formatted size
2774a4d8c2SCharles.Forsyth * possibly block size as parameter
2874a4d8c2SCharles.Forsyth * fetch parameters from header on init
2974a4d8c2SCharles.Forsyth *
3074a4d8c2SCharles.Forsyth * Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited
3174a4d8c2SCharles.Forsyth * usage : ftl flashsize secsize inputfile outputfile
3274a4d8c2SCharles.Forsyth * outputfile will then be a ftl image of inputfile
3374a4d8c2SCharles.Forsyth * nb assumes the base address is zero
3474a4d8c2SCharles.Forsyth *
3574a4d8c2SCharles.Forsyth */
3674a4d8c2SCharles.Forsyth
3774a4d8c2SCharles.Forsyth #include <lib9.h>
3874a4d8c2SCharles.Forsyth
3974a4d8c2SCharles.Forsyth ulong flashsize, secsize;
4074a4d8c2SCharles.Forsyth char *flashm;
4174a4d8c2SCharles.Forsyth int trace = 0;
4274a4d8c2SCharles.Forsyth
4374a4d8c2SCharles.Forsyth #ifndef offsetof
4474a4d8c2SCharles.Forsyth #define offsetof(T,X) ((ulong)&(((T*)0)->X))
4574a4d8c2SCharles.Forsyth #endif
4674a4d8c2SCharles.Forsyth
4774a4d8c2SCharles.Forsyth typedef struct Ftl Ftl;
4874a4d8c2SCharles.Forsyth typedef struct Merase Merase;
4974a4d8c2SCharles.Forsyth typedef struct Terase Terase;
5074a4d8c2SCharles.Forsyth
5174a4d8c2SCharles.Forsyth enum {
5274a4d8c2SCharles.Forsyth Eshift = 18, /* 2^18=256k; log2(eraseunit) */
5374a4d8c2SCharles.Forsyth Flashseg = 1<<Eshift,
5474a4d8c2SCharles.Forsyth Bshift = 9, /* 2^9=512 */
5574a4d8c2SCharles.Forsyth Bsize = 1<<Bshift,
5674a4d8c2SCharles.Forsyth BAMoffset = 0x100,
5774a4d8c2SCharles.Forsyth Nolimit = ~0,
5874a4d8c2SCharles.Forsyth USABLEPCT = 95, /* release only this % to client */
5974a4d8c2SCharles.Forsyth
6074a4d8c2SCharles.Forsyth FTLDEBUG = 0
6174a4d8c2SCharles.Forsyth };
6274a4d8c2SCharles.Forsyth
6374a4d8c2SCharles.Forsyth /* erase unit header (defined by FTL specification) */
6474a4d8c2SCharles.Forsyth struct Merase {
6574a4d8c2SCharles.Forsyth uchar linktuple[5];
6674a4d8c2SCharles.Forsyth uchar orgtuple[10];
6774a4d8c2SCharles.Forsyth uchar nxfer;
6874a4d8c2SCharles.Forsyth uchar nerase[4];
6974a4d8c2SCharles.Forsyth uchar id[2];
7074a4d8c2SCharles.Forsyth uchar bshift;
7174a4d8c2SCharles.Forsyth uchar eshift;
7274a4d8c2SCharles.Forsyth uchar pstart[2];
7374a4d8c2SCharles.Forsyth uchar nunits[2];
7474a4d8c2SCharles.Forsyth uchar psize[4];
7574a4d8c2SCharles.Forsyth uchar vbmbase[4];
7674a4d8c2SCharles.Forsyth uchar nvbm[2];
7774a4d8c2SCharles.Forsyth uchar flags;
7874a4d8c2SCharles.Forsyth uchar code;
7974a4d8c2SCharles.Forsyth uchar serial[4];
8074a4d8c2SCharles.Forsyth uchar altoffset[4];
8174a4d8c2SCharles.Forsyth uchar bamoffset[4];
8274a4d8c2SCharles.Forsyth uchar rsv2[12];
8374a4d8c2SCharles.Forsyth };
8474a4d8c2SCharles.Forsyth #define ERASEHDRLEN 64
8574a4d8c2SCharles.Forsyth
8674a4d8c2SCharles.Forsyth enum {
8774a4d8c2SCharles.Forsyth /* special unit IDs */
8874a4d8c2SCharles.Forsyth XferID = 0xffff,
8974a4d8c2SCharles.Forsyth XferBusy = 0x7fff,
9074a4d8c2SCharles.Forsyth
9174a4d8c2SCharles.Forsyth /* special BAM addresses */
9274a4d8c2SCharles.Forsyth Bfree = 0xffffffff,
9374a4d8c2SCharles.Forsyth Bwriting = 0xfffffffe,
9474a4d8c2SCharles.Forsyth Bdeleted = 0,
9574a4d8c2SCharles.Forsyth
9674a4d8c2SCharles.Forsyth /* block types */
9774a4d8c2SCharles.Forsyth TypeShift = 7,
9874a4d8c2SCharles.Forsyth BlockType = (1<<TypeShift)-1,
9974a4d8c2SCharles.Forsyth ControlBlock = 0x30,
10074a4d8c2SCharles.Forsyth DataBlock = 0x40,
10174a4d8c2SCharles.Forsyth ReplacePage = 0x60,
10274a4d8c2SCharles.Forsyth BadBlock = 0x70,
10374a4d8c2SCharles.Forsyth };
10474a4d8c2SCharles.Forsyth
10574a4d8c2SCharles.Forsyth #define BTYPE(b) ((b) & BlockType)
10674a4d8c2SCharles.Forsyth #define BADDR(b) ((b) & ~BlockType)
10774a4d8c2SCharles.Forsyth #define BNO(va) (((ulong)(va))>>Bshift)
10874a4d8c2SCharles.Forsyth #define MKBAM(b,t) (((b)<<Bshift)|(t))
10974a4d8c2SCharles.Forsyth
11074a4d8c2SCharles.Forsyth struct Terase {
11174a4d8c2SCharles.Forsyth int x;
11274a4d8c2SCharles.Forsyth int id;
11374a4d8c2SCharles.Forsyth ulong offset;
11474a4d8c2SCharles.Forsyth ulong bamoffset;
11574a4d8c2SCharles.Forsyth ulong nbam;
11674a4d8c2SCharles.Forsyth ulong* bam;
11774a4d8c2SCharles.Forsyth ulong bamx;
11874a4d8c2SCharles.Forsyth ulong nfree;
11974a4d8c2SCharles.Forsyth ulong nused;
12074a4d8c2SCharles.Forsyth ulong ndead;
12174a4d8c2SCharles.Forsyth ulong nbad;
12274a4d8c2SCharles.Forsyth ulong nerase;
12374a4d8c2SCharles.Forsyth };
12474a4d8c2SCharles.Forsyth
12574a4d8c2SCharles.Forsyth struct Ftl {
12674a4d8c2SCharles.Forsyth ulong base; /* base of flash region */
12774a4d8c2SCharles.Forsyth ulong size; /* size of flash region */
12874a4d8c2SCharles.Forsyth ulong segsize; /* size of flash segment (erase unit) */
12974a4d8c2SCharles.Forsyth int eshift; /* log2(erase-unit-size) */
13074a4d8c2SCharles.Forsyth int bshift; /* log2(bsize) */
13174a4d8c2SCharles.Forsyth int bsize;
13274a4d8c2SCharles.Forsyth int nunit; /* number of segments (erase units) */
13374a4d8c2SCharles.Forsyth Terase** unit;
13474a4d8c2SCharles.Forsyth int lastx; /* index in unit of last allocation */
13574a4d8c2SCharles.Forsyth int xfer; /* index in unit of current transfer unit (-1 if none) */
13674a4d8c2SCharles.Forsyth ulong nfree; /* total free space in blocks */
13774a4d8c2SCharles.Forsyth ulong nblock; /* total space in blocks */
13874a4d8c2SCharles.Forsyth ulong rwlimit; /* user-visible block limit (`formatted size') */
13974a4d8c2SCharles.Forsyth ulong* vbm; /* virtual block map */
14074a4d8c2SCharles.Forsyth ulong fstart; /* address of first block of data in a segment */
14174a4d8c2SCharles.Forsyth int trace; /* (debugging) trace of read/write actions */
14274a4d8c2SCharles.Forsyth int detach; /* free Ftl on last close */
14374a4d8c2SCharles.Forsyth
14474a4d8c2SCharles.Forsyth /* scavenging variables */
14574a4d8c2SCharles.Forsyth int needspace;
14674a4d8c2SCharles.Forsyth int hasproc;
14774a4d8c2SCharles.Forsyth };
14874a4d8c2SCharles.Forsyth
14974a4d8c2SCharles.Forsyth enum {
15074a4d8c2SCharles.Forsyth /* Ftl.detach */
15174a4d8c2SCharles.Forsyth Detached = 1, /* detach on close */
15274a4d8c2SCharles.Forsyth Deferred /* scavenger must free it */
15374a4d8c2SCharles.Forsyth };
15474a4d8c2SCharles.Forsyth
15574a4d8c2SCharles.Forsyth /* little endian */
15674a4d8c2SCharles.Forsyth #define GET2(p) (((p)[1]<<8)|(p)[0])
15774a4d8c2SCharles.Forsyth #define GET4(p) (((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
15874a4d8c2SCharles.Forsyth #define PUT2(p,v) (((p)[1]=(v)>>8),((p)[0]=(v)))
15974a4d8c2SCharles.Forsyth #define PUT4(p,v) (((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v)))
16074a4d8c2SCharles.Forsyth
16174a4d8c2SCharles.Forsyth static Ftl *ftls;
16274a4d8c2SCharles.Forsyth
16374a4d8c2SCharles.Forsyth static ulong allocblk(Ftl*);
16474a4d8c2SCharles.Forsyth static void eraseflash(Ftl*, ulong);
16574a4d8c2SCharles.Forsyth static void erasefree(Terase*);
16674a4d8c2SCharles.Forsyth static void eraseinit(Ftl*, ulong, int, int);
16774a4d8c2SCharles.Forsyth static Terase* eraseload(Ftl*, int, ulong);
16874a4d8c2SCharles.Forsyth static void ftlfree(Ftl*);
16974a4d8c2SCharles.Forsyth static void getflash(Ftl*, void*, ulong, long);
17074a4d8c2SCharles.Forsyth static int mapblk(Ftl*, ulong, Terase**, ulong*);
17174a4d8c2SCharles.Forsyth static Ftl* mkftl(char*, ulong, ulong, int, char*);
17274a4d8c2SCharles.Forsyth static void putbam(Ftl*, Terase*, int, ulong);
17374a4d8c2SCharles.Forsyth static void putflash(Ftl*, ulong, void*, long);
17474a4d8c2SCharles.Forsyth static int scavenge(Ftl*);
17574a4d8c2SCharles.Forsyth
17674a4d8c2SCharles.Forsyth static void
ftlstat(int sz)17774a4d8c2SCharles.Forsyth ftlstat(int sz)
17874a4d8c2SCharles.Forsyth {
17974a4d8c2SCharles.Forsyth print("0x%lux:0x%ux:0x%lux\n", ftls->rwlimit*Bsize, sz, flashsize);
18074a4d8c2SCharles.Forsyth print("%lud:%d:%lud in 512b blocks\n", ftls->rwlimit, sz>>Bshift, flashsize>>Bshift);
18174a4d8c2SCharles.Forsyth }
18274a4d8c2SCharles.Forsyth
18374a4d8c2SCharles.Forsyth static long
ftlread(void * buf,long n,ulong offset)18474a4d8c2SCharles.Forsyth ftlread(void *buf, long n, ulong offset)
18574a4d8c2SCharles.Forsyth {
18674a4d8c2SCharles.Forsyth Ftl *ftl;
18774a4d8c2SCharles.Forsyth Terase *e;
18874a4d8c2SCharles.Forsyth int nb;
18974a4d8c2SCharles.Forsyth uchar *a;
19074a4d8c2SCharles.Forsyth ulong pb;
19174a4d8c2SCharles.Forsyth
19274a4d8c2SCharles.Forsyth if(n <= 0 || n%Bsize || offset%Bsize) {
19374a4d8c2SCharles.Forsyth fprint(2, "bad read\n");
19474a4d8c2SCharles.Forsyth exits("1");
19574a4d8c2SCharles.Forsyth }
19674a4d8c2SCharles.Forsyth ftl = ftls;
19774a4d8c2SCharles.Forsyth nb = n/Bsize;
19874a4d8c2SCharles.Forsyth offset /= Bsize;
19974a4d8c2SCharles.Forsyth if(offset >= ftl->rwlimit)
20074a4d8c2SCharles.Forsyth return 0;
20174a4d8c2SCharles.Forsyth if(offset+nb > ftl->rwlimit)
20274a4d8c2SCharles.Forsyth nb = ftl->rwlimit - offset;
20374a4d8c2SCharles.Forsyth a = buf;
20474a4d8c2SCharles.Forsyth for(n = 0; n < nb; n++){
20574a4d8c2SCharles.Forsyth if(mapblk(ftl, offset+n, &e, &pb))
20674a4d8c2SCharles.Forsyth getflash(ftl, a, e->offset + pb*Bsize, Bsize);
20774a4d8c2SCharles.Forsyth else
20874a4d8c2SCharles.Forsyth memset(a, 0, Bsize);
20974a4d8c2SCharles.Forsyth a += Bsize;
21074a4d8c2SCharles.Forsyth }
21174a4d8c2SCharles.Forsyth return a-(uchar*)buf;
212*d67b7dadSforsyth /* not reached */
21374a4d8c2SCharles.Forsyth }
21474a4d8c2SCharles.Forsyth
21574a4d8c2SCharles.Forsyth static long
ftlwrite(void * buf,long n,ulong offset)21674a4d8c2SCharles.Forsyth ftlwrite(void *buf, long n, ulong offset)
21774a4d8c2SCharles.Forsyth {
21874a4d8c2SCharles.Forsyth int ns, nb;
21974a4d8c2SCharles.Forsyth uchar *a;
22074a4d8c2SCharles.Forsyth Terase *e, *oe;
22174a4d8c2SCharles.Forsyth ulong ob, v;
22274a4d8c2SCharles.Forsyth Ftl *ftl;
22374a4d8c2SCharles.Forsyth
22474a4d8c2SCharles.Forsyth if(n <= 0)
22574a4d8c2SCharles.Forsyth return 0;
22674a4d8c2SCharles.Forsyth ftl = ftls;
22774a4d8c2SCharles.Forsyth if(n <= 0 || n%Bsize || offset%Bsize) {
22874a4d8c2SCharles.Forsyth fprint(2, "bad write\n");
22974a4d8c2SCharles.Forsyth exits("1");
23074a4d8c2SCharles.Forsyth }
23174a4d8c2SCharles.Forsyth nb = n/Bsize;
23274a4d8c2SCharles.Forsyth offset /= Bsize;
23374a4d8c2SCharles.Forsyth if(offset >= ftl->rwlimit)
23474a4d8c2SCharles.Forsyth return 0;
23574a4d8c2SCharles.Forsyth if(offset+nb > ftl->rwlimit)
23674a4d8c2SCharles.Forsyth nb = ftl->rwlimit - offset;
23774a4d8c2SCharles.Forsyth a = buf;
23874a4d8c2SCharles.Forsyth for(n = 0; n < nb; n++){
23974a4d8c2SCharles.Forsyth ns = 0;
24074a4d8c2SCharles.Forsyth while((v = allocblk(ftl)) == 0)
24174a4d8c2SCharles.Forsyth if(!scavenge(ftl) || ++ns > 3){
24274a4d8c2SCharles.Forsyth print("ftl: flash memory full\n");
24374a4d8c2SCharles.Forsyth }
24474a4d8c2SCharles.Forsyth if(!mapblk(ftl, offset+n, &oe, &ob))
24574a4d8c2SCharles.Forsyth oe = nil;
24674a4d8c2SCharles.Forsyth e = ftl->unit[v>>16];
24774a4d8c2SCharles.Forsyth v &= 0xffff;
24874a4d8c2SCharles.Forsyth putflash(ftl, e->offset + v*Bsize, a, Bsize);
24974a4d8c2SCharles.Forsyth putbam(ftl, e, v, MKBAM(offset+n, DataBlock));
25074a4d8c2SCharles.Forsyth /* both old and new block references exist in this window (can't be closed?) */
25174a4d8c2SCharles.Forsyth ftl->vbm[offset+n] = (e->x<<16) | v;
25274a4d8c2SCharles.Forsyth if(oe != nil){
25374a4d8c2SCharles.Forsyth putbam(ftl, oe, ob, Bdeleted);
25474a4d8c2SCharles.Forsyth oe->ndead++;
25574a4d8c2SCharles.Forsyth }
25674a4d8c2SCharles.Forsyth a += Bsize;
25774a4d8c2SCharles.Forsyth }
25874a4d8c2SCharles.Forsyth return a-(uchar*)buf;
259*d67b7dadSforsyth /* not reached */
26074a4d8c2SCharles.Forsyth }
26174a4d8c2SCharles.Forsyth
26274a4d8c2SCharles.Forsyth static Ftl *
mkftl(char * fname,ulong base,ulong size,int eshift,char * op)26374a4d8c2SCharles.Forsyth mkftl(char *fname, ulong base, ulong size, int eshift, char *op)
26474a4d8c2SCharles.Forsyth {
26574a4d8c2SCharles.Forsyth int i, j, nov, segblocks;
26674a4d8c2SCharles.Forsyth ulong limit;
26774a4d8c2SCharles.Forsyth Terase *e;
26874a4d8c2SCharles.Forsyth Ftl *ftl;
26974a4d8c2SCharles.Forsyth
27074a4d8c2SCharles.Forsyth ftl = malloc(sizeof(*ftl));
27174a4d8c2SCharles.Forsyth if(ftl == nil) {
27274a4d8c2SCharles.Forsyth fprint(2, "out of memory\n");
27374a4d8c2SCharles.Forsyth exits("1");
27474a4d8c2SCharles.Forsyth }
27574a4d8c2SCharles.Forsyth ftl->lastx = 0;
27674a4d8c2SCharles.Forsyth ftl->detach = 0;
27774a4d8c2SCharles.Forsyth ftl->needspace = 0;
27874a4d8c2SCharles.Forsyth ftl->hasproc = 0;
27974a4d8c2SCharles.Forsyth ftl->trace = 0;
28074a4d8c2SCharles.Forsyth limit = flashsize;
28174a4d8c2SCharles.Forsyth if(size == Nolimit)
28274a4d8c2SCharles.Forsyth size = limit-base;
28374a4d8c2SCharles.Forsyth if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) {
28474a4d8c2SCharles.Forsyth fprint(2, "bad flash space parameters");
28574a4d8c2SCharles.Forsyth exits("1");
28674a4d8c2SCharles.Forsyth }
28774a4d8c2SCharles.Forsyth if(FTLDEBUG || ftl->trace || trace)
28874a4d8c2SCharles.Forsyth print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit);
28974a4d8c2SCharles.Forsyth ftl->base = base;
29074a4d8c2SCharles.Forsyth ftl->size = size;
29174a4d8c2SCharles.Forsyth ftl->bshift = Bshift;
29274a4d8c2SCharles.Forsyth ftl->bsize = Bsize;
29374a4d8c2SCharles.Forsyth ftl->eshift = eshift;
29474a4d8c2SCharles.Forsyth ftl->segsize = 1<<eshift;
29574a4d8c2SCharles.Forsyth ftl->nunit = size>>eshift;
29674a4d8c2SCharles.Forsyth nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks per segment (header, and BAM itself) */
29774a4d8c2SCharles.Forsyth ftl->fstart = nov;
29874a4d8c2SCharles.Forsyth segblocks = ftl->segsize/Bsize - nov;
29974a4d8c2SCharles.Forsyth ftl->nblock = ftl->nunit*segblocks;
30074a4d8c2SCharles.Forsyth if(ftl->nblock >= 0x10000)
30174a4d8c2SCharles.Forsyth ftl->nblock = 0x10000;
30274a4d8c2SCharles.Forsyth ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm));
30374a4d8c2SCharles.Forsyth ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit));
30474a4d8c2SCharles.Forsyth if(ftl->vbm == nil || ftl->unit == nil) {
30574a4d8c2SCharles.Forsyth fprint(2, "out of mem");
30674a4d8c2SCharles.Forsyth exits("1");
30774a4d8c2SCharles.Forsyth }
30874a4d8c2SCharles.Forsyth for(i=0; i<ftl->nblock; i++)
30974a4d8c2SCharles.Forsyth ftl->vbm[i] = 0;
31074a4d8c2SCharles.Forsyth if(strcmp(op, "format") == 0){
31174a4d8c2SCharles.Forsyth for(i=0; i<ftl->nunit-1; i++)
31274a4d8c2SCharles.Forsyth eraseinit(ftl, i*ftl->segsize, i, 1);
31374a4d8c2SCharles.Forsyth eraseinit(ftl, i*ftl->segsize, XferID, 1);
31474a4d8c2SCharles.Forsyth }
31574a4d8c2SCharles.Forsyth ftl->xfer = -1;
31674a4d8c2SCharles.Forsyth for(i=0; i<ftl->nunit; i++){
31774a4d8c2SCharles.Forsyth e = eraseload(ftl, i, i*ftl->segsize);
31874a4d8c2SCharles.Forsyth if(e == nil){
31974a4d8c2SCharles.Forsyth print("ftl: logical segment %d: bad format\n", i);
32074a4d8c2SCharles.Forsyth continue;
32174a4d8c2SCharles.Forsyth }
32274a4d8c2SCharles.Forsyth if(e->id == XferBusy){
32374a4d8c2SCharles.Forsyth e->nerase++;
32474a4d8c2SCharles.Forsyth eraseinit(ftl, e->offset, XferID, e->nerase);
32574a4d8c2SCharles.Forsyth e->id = XferID;
32674a4d8c2SCharles.Forsyth }
32774a4d8c2SCharles.Forsyth for(j=0; j<ftl->nunit; j++)
32874a4d8c2SCharles.Forsyth if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){
32974a4d8c2SCharles.Forsyth print("ftl: duplicate erase unit #%x\n", e->id);
33074a4d8c2SCharles.Forsyth erasefree(e);
33174a4d8c2SCharles.Forsyth e = nil;
33274a4d8c2SCharles.Forsyth break;
33374a4d8c2SCharles.Forsyth }
33474a4d8c2SCharles.Forsyth if(e){
33574a4d8c2SCharles.Forsyth ftl->unit[e->x] = e;
33674a4d8c2SCharles.Forsyth if(e->id == XferID)
33774a4d8c2SCharles.Forsyth ftl->xfer = e->x;
33874a4d8c2SCharles.Forsyth if (FTLDEBUG || ftl->trace || trace)
33974a4d8c2SCharles.Forsyth print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n",
34074a4d8c2SCharles.Forsyth e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase);
34174a4d8c2SCharles.Forsyth }
34274a4d8c2SCharles.Forsyth }
34374a4d8c2SCharles.Forsyth if(ftl->xfer < 0 && ftl->nunit <= 0 || ftl->xfer >= 0 && ftl->nunit <= 1) {
34474a4d8c2SCharles.Forsyth fprint(2, "no valid flash data units");
34574a4d8c2SCharles.Forsyth exits("1");
34674a4d8c2SCharles.Forsyth }
34774a4d8c2SCharles.Forsyth if(ftl->xfer < 0)
34874a4d8c2SCharles.Forsyth print("ftl: no transfer unit: device is WORM\n");
34974a4d8c2SCharles.Forsyth else
35074a4d8c2SCharles.Forsyth ftl->nblock -= segblocks; /* discount transfer segment */
35174a4d8c2SCharles.Forsyth if(ftl->nblock >= 1000)
35274a4d8c2SCharles.Forsyth ftl->rwlimit = ftl->nblock-100; /* TO DO: variable reserve */
35374a4d8c2SCharles.Forsyth else
35474a4d8c2SCharles.Forsyth ftl->rwlimit = ftl->nblock*USABLEPCT/100;
35574a4d8c2SCharles.Forsyth return ftl;
35674a4d8c2SCharles.Forsyth }
35774a4d8c2SCharles.Forsyth
35874a4d8c2SCharles.Forsyth static void
ftlfree(Ftl * ftl)35974a4d8c2SCharles.Forsyth ftlfree(Ftl *ftl)
36074a4d8c2SCharles.Forsyth {
36174a4d8c2SCharles.Forsyth if(ftl != nil){
36274a4d8c2SCharles.Forsyth free(ftl->unit);
36374a4d8c2SCharles.Forsyth free(ftl->vbm);
36474a4d8c2SCharles.Forsyth free(ftl);
36574a4d8c2SCharles.Forsyth }
36674a4d8c2SCharles.Forsyth }
36774a4d8c2SCharles.Forsyth
36874a4d8c2SCharles.Forsyth /*
36974a4d8c2SCharles.Forsyth * this simple greedy algorithm weighted by nerase does seem to lead
37074a4d8c2SCharles.Forsyth * to even wear of erase units (cf. the eNVy file system)
37174a4d8c2SCharles.Forsyth */
37274a4d8c2SCharles.Forsyth static Terase *
bestcopy(Ftl * ftl)37374a4d8c2SCharles.Forsyth bestcopy(Ftl *ftl)
37474a4d8c2SCharles.Forsyth {
37574a4d8c2SCharles.Forsyth Terase *e, *be;
37674a4d8c2SCharles.Forsyth int i;
37774a4d8c2SCharles.Forsyth
37874a4d8c2SCharles.Forsyth be = nil;
37974a4d8c2SCharles.Forsyth for(i=0; i<ftl->nunit; i++)
38074a4d8c2SCharles.Forsyth if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad &&
38174a4d8c2SCharles.Forsyth (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead))
38274a4d8c2SCharles.Forsyth be = e;
38374a4d8c2SCharles.Forsyth return be;
38474a4d8c2SCharles.Forsyth }
38574a4d8c2SCharles.Forsyth
38674a4d8c2SCharles.Forsyth static int
copyunit(Ftl * ftl,Terase * from,Terase * to)38774a4d8c2SCharles.Forsyth copyunit(Ftl *ftl, Terase *from, Terase *to)
38874a4d8c2SCharles.Forsyth {
38974a4d8c2SCharles.Forsyth int i, nb;
39074a4d8c2SCharles.Forsyth uchar id[2];
39174a4d8c2SCharles.Forsyth ulong *bam;
39274a4d8c2SCharles.Forsyth uchar *buf;
39374a4d8c2SCharles.Forsyth ulong v, bno;
39474a4d8c2SCharles.Forsyth
39574a4d8c2SCharles.Forsyth if(FTLDEBUG || ftl->trace || trace)
39674a4d8c2SCharles.Forsyth print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset);
39774a4d8c2SCharles.Forsyth to->nbam = 0;
39874a4d8c2SCharles.Forsyth free(to->bam);
39974a4d8c2SCharles.Forsyth to->bam = nil;
40074a4d8c2SCharles.Forsyth buf = malloc(Bsize);
40174a4d8c2SCharles.Forsyth if(buf == nil)
40274a4d8c2SCharles.Forsyth return 0;
40374a4d8c2SCharles.Forsyth PUT2(id, XferBusy);
40474a4d8c2SCharles.Forsyth putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
40574a4d8c2SCharles.Forsyth /* make new BAM */
40674a4d8c2SCharles.Forsyth nb = from->nbam*sizeof(*to->bam);
40774a4d8c2SCharles.Forsyth bam = malloc(nb);
40874a4d8c2SCharles.Forsyth if(bam == nil) {
40974a4d8c2SCharles.Forsyth fprint(2, "nomem\n");
41074a4d8c2SCharles.Forsyth exits("1");
41174a4d8c2SCharles.Forsyth }
41274a4d8c2SCharles.Forsyth memmove(bam, from->bam, nb);
41374a4d8c2SCharles.Forsyth to->nused = 0;
41474a4d8c2SCharles.Forsyth to->nbad = 0;
41574a4d8c2SCharles.Forsyth to->nfree = 0;
41674a4d8c2SCharles.Forsyth to->ndead = 0;
41774a4d8c2SCharles.Forsyth for(i = 0; i < from->nbam; i++)
41874a4d8c2SCharles.Forsyth switch(bam[i]){
41974a4d8c2SCharles.Forsyth case Bwriting:
42074a4d8c2SCharles.Forsyth case Bdeleted:
42174a4d8c2SCharles.Forsyth case Bfree:
42274a4d8c2SCharles.Forsyth bam[i] = Bfree;
42374a4d8c2SCharles.Forsyth to->nfree++;
42474a4d8c2SCharles.Forsyth break;
42574a4d8c2SCharles.Forsyth default:
42674a4d8c2SCharles.Forsyth switch(bam[i]&BlockType){
42774a4d8c2SCharles.Forsyth default:
42874a4d8c2SCharles.Forsyth case BadBlock: /* it isn't necessarily bad in this unit */
42974a4d8c2SCharles.Forsyth to->nfree++;
43074a4d8c2SCharles.Forsyth bam[i] = Bfree;
43174a4d8c2SCharles.Forsyth break;
43274a4d8c2SCharles.Forsyth case DataBlock:
43374a4d8c2SCharles.Forsyth case ReplacePage:
43474a4d8c2SCharles.Forsyth v = bam[i];
43574a4d8c2SCharles.Forsyth bno = BNO(v & ~BlockType);
43674a4d8c2SCharles.Forsyth if(i < ftl->fstart || bno >= ftl->nblock){
43774a4d8c2SCharles.Forsyth print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v);
43874a4d8c2SCharles.Forsyth to->nfree++;
43974a4d8c2SCharles.Forsyth bam[i] = Bfree;
44074a4d8c2SCharles.Forsyth break;
44174a4d8c2SCharles.Forsyth }
44274a4d8c2SCharles.Forsyth getflash(ftl, buf, from->offset+i*Bsize, Bsize);
44374a4d8c2SCharles.Forsyth putflash(ftl, to->offset+i*Bsize, buf, Bsize);
44474a4d8c2SCharles.Forsyth to->nused++;
44574a4d8c2SCharles.Forsyth break;
44674a4d8c2SCharles.Forsyth case ControlBlock:
44774a4d8c2SCharles.Forsyth to->nused++;
44874a4d8c2SCharles.Forsyth break;
44974a4d8c2SCharles.Forsyth }
45074a4d8c2SCharles.Forsyth }
45174a4d8c2SCharles.Forsyth for(i=0; i<from->nbam; i++){
45274a4d8c2SCharles.Forsyth uchar *p = (uchar*)&bam[i];
45374a4d8c2SCharles.Forsyth v = bam[i];
45474a4d8c2SCharles.Forsyth if(v != Bfree && ftl->trace > 1)
45574a4d8c2SCharles.Forsyth print("to[%d]=#%lux\n", i, v);
45674a4d8c2SCharles.Forsyth PUT4(p, v);
45774a4d8c2SCharles.Forsyth }
45874a4d8c2SCharles.Forsyth putflash(ftl, to->bamoffset, bam, nb); /* BUG: PUT4 */
45974a4d8c2SCharles.Forsyth for(i=0; i<from->nbam; i++){
46074a4d8c2SCharles.Forsyth uchar *p = (uchar*)&bam[i];
46174a4d8c2SCharles.Forsyth v = bam[i];
46274a4d8c2SCharles.Forsyth PUT4(p, v);
46374a4d8c2SCharles.Forsyth }
46474a4d8c2SCharles.Forsyth to->id = from->id;
46574a4d8c2SCharles.Forsyth PUT2(id, to->id);
46674a4d8c2SCharles.Forsyth putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
46774a4d8c2SCharles.Forsyth to->nbam = from->nbam;
46874a4d8c2SCharles.Forsyth to->bam = bam;
46974a4d8c2SCharles.Forsyth ftl->nfree += to->nfree - from->nfree;
47074a4d8c2SCharles.Forsyth free(buf);
47174a4d8c2SCharles.Forsyth return 1;
47274a4d8c2SCharles.Forsyth }
47374a4d8c2SCharles.Forsyth
47474a4d8c2SCharles.Forsyth static int
mustscavenge(void * a)47574a4d8c2SCharles.Forsyth mustscavenge(void *a)
47674a4d8c2SCharles.Forsyth {
47774a4d8c2SCharles.Forsyth return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred;
47874a4d8c2SCharles.Forsyth }
47974a4d8c2SCharles.Forsyth
48074a4d8c2SCharles.Forsyth static int
donescavenge(void * a)48174a4d8c2SCharles.Forsyth donescavenge(void *a)
48274a4d8c2SCharles.Forsyth {
48374a4d8c2SCharles.Forsyth return ((Ftl*)a)->needspace == 0;
48474a4d8c2SCharles.Forsyth }
48574a4d8c2SCharles.Forsyth
48674a4d8c2SCharles.Forsyth static void
scavengeproc(void * arg)48774a4d8c2SCharles.Forsyth scavengeproc(void *arg)
48874a4d8c2SCharles.Forsyth {
48974a4d8c2SCharles.Forsyth Ftl *ftl;
49074a4d8c2SCharles.Forsyth int i;
49174a4d8c2SCharles.Forsyth Terase *e, *ne;
49274a4d8c2SCharles.Forsyth
49374a4d8c2SCharles.Forsyth ftl = arg;
49474a4d8c2SCharles.Forsyth if(mustscavenge(ftl)){
49574a4d8c2SCharles.Forsyth if(ftl->detach == Deferred){
49674a4d8c2SCharles.Forsyth ftlfree(ftl);
49774a4d8c2SCharles.Forsyth fprint(2, "scavenge out of memory\n");
49874a4d8c2SCharles.Forsyth exits("1");
49974a4d8c2SCharles.Forsyth }
50074a4d8c2SCharles.Forsyth if(FTLDEBUG || ftl->trace || trace)
50174a4d8c2SCharles.Forsyth print("ftl: scavenge %ld\n", ftl->nfree);
50274a4d8c2SCharles.Forsyth e = bestcopy(ftl);
50374a4d8c2SCharles.Forsyth if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne)
50474a4d8c2SCharles.Forsyth goto Fail;
50574a4d8c2SCharles.Forsyth if(copyunit(ftl, e, ne)){
50674a4d8c2SCharles.Forsyth i = ne->x; ne->x = e->x; e->x = i;
50774a4d8c2SCharles.Forsyth ftl->unit[ne->x] = ne;
50874a4d8c2SCharles.Forsyth ftl->unit[e->x] = e;
50974a4d8c2SCharles.Forsyth ftl->xfer = e->x;
51074a4d8c2SCharles.Forsyth e->id = XferID;
51174a4d8c2SCharles.Forsyth e->nbam = 0;
51274a4d8c2SCharles.Forsyth free(e->bam);
51374a4d8c2SCharles.Forsyth e->bam = nil;
51474a4d8c2SCharles.Forsyth e->bamx = 0;
51574a4d8c2SCharles.Forsyth e->nerase++;
51674a4d8c2SCharles.Forsyth eraseinit(ftl, e->offset, XferID, e->nerase);
51774a4d8c2SCharles.Forsyth }
51874a4d8c2SCharles.Forsyth Fail:
51974a4d8c2SCharles.Forsyth if(FTLDEBUG || ftl->trace || trace)
52074a4d8c2SCharles.Forsyth print("ftl: end scavenge %ld\n", ftl->nfree);
52174a4d8c2SCharles.Forsyth ftl->needspace = 0;
52274a4d8c2SCharles.Forsyth }
52374a4d8c2SCharles.Forsyth }
52474a4d8c2SCharles.Forsyth
52574a4d8c2SCharles.Forsyth static int
scavenge(Ftl * ftl)52674a4d8c2SCharles.Forsyth scavenge(Ftl *ftl)
52774a4d8c2SCharles.Forsyth {
52874a4d8c2SCharles.Forsyth if(ftl->xfer < 0 || bestcopy(ftl) == nil)
52974a4d8c2SCharles.Forsyth return 0; /* you worm! */
53074a4d8c2SCharles.Forsyth
53174a4d8c2SCharles.Forsyth if(!ftl->hasproc){
53274a4d8c2SCharles.Forsyth ftl->hasproc = 1;
53374a4d8c2SCharles.Forsyth }
53474a4d8c2SCharles.Forsyth ftl->needspace = 1;
53574a4d8c2SCharles.Forsyth
53674a4d8c2SCharles.Forsyth scavengeproc(ftls);
53774a4d8c2SCharles.Forsyth
53874a4d8c2SCharles.Forsyth return ftl->nfree;
53974a4d8c2SCharles.Forsyth }
54074a4d8c2SCharles.Forsyth
54174a4d8c2SCharles.Forsyth static void
putbam(Ftl * ftl,Terase * e,int n,ulong entry)54274a4d8c2SCharles.Forsyth putbam(Ftl *ftl, Terase *e, int n, ulong entry)
54374a4d8c2SCharles.Forsyth {
54474a4d8c2SCharles.Forsyth uchar b[4];
54574a4d8c2SCharles.Forsyth
54674a4d8c2SCharles.Forsyth e->bam[n] = entry;
54774a4d8c2SCharles.Forsyth PUT4(b, entry);
54874a4d8c2SCharles.Forsyth putflash(ftl, e->bamoffset + n*4, b, 4);
54974a4d8c2SCharles.Forsyth }
55074a4d8c2SCharles.Forsyth
55174a4d8c2SCharles.Forsyth static ulong
allocblk(Ftl * ftl)55274a4d8c2SCharles.Forsyth allocblk(Ftl *ftl)
55374a4d8c2SCharles.Forsyth {
55474a4d8c2SCharles.Forsyth Terase *e;
55574a4d8c2SCharles.Forsyth int i, j;
55674a4d8c2SCharles.Forsyth
55774a4d8c2SCharles.Forsyth i = ftl->lastx;
55874a4d8c2SCharles.Forsyth do{
55974a4d8c2SCharles.Forsyth e = ftl->unit[i];
56074a4d8c2SCharles.Forsyth if(e != nil && e->id != XferID && e->nfree){
56174a4d8c2SCharles.Forsyth ftl->lastx = i;
56274a4d8c2SCharles.Forsyth for(j=e->bamx; j<e->nbam; j++)
56374a4d8c2SCharles.Forsyth if(e->bam[j] == Bfree){
56474a4d8c2SCharles.Forsyth putbam(ftl, e, j, Bwriting);
56574a4d8c2SCharles.Forsyth ftl->nfree--;
56674a4d8c2SCharles.Forsyth e->nfree--;
56774a4d8c2SCharles.Forsyth e->bamx = j+1;
56874a4d8c2SCharles.Forsyth return (e->x<<16) | j;
56974a4d8c2SCharles.Forsyth }
57074a4d8c2SCharles.Forsyth e->nfree = 0;
57174a4d8c2SCharles.Forsyth print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree);
57274a4d8c2SCharles.Forsyth }
57374a4d8c2SCharles.Forsyth if(++i >= ftl->nunit)
57474a4d8c2SCharles.Forsyth i = 0;
57574a4d8c2SCharles.Forsyth }while(i != ftl->lastx);
57674a4d8c2SCharles.Forsyth return 0;
57774a4d8c2SCharles.Forsyth }
57874a4d8c2SCharles.Forsyth
57974a4d8c2SCharles.Forsyth static int
mapblk(Ftl * ftl,ulong bno,Terase ** ep,ulong * bp)58074a4d8c2SCharles.Forsyth mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp)
58174a4d8c2SCharles.Forsyth {
58274a4d8c2SCharles.Forsyth ulong v;
58374a4d8c2SCharles.Forsyth int x;
58474a4d8c2SCharles.Forsyth
58574a4d8c2SCharles.Forsyth if(bno < ftl->nblock){
58674a4d8c2SCharles.Forsyth v = ftl->vbm[bno];
58774a4d8c2SCharles.Forsyth if(v == 0 || v == ~0)
58874a4d8c2SCharles.Forsyth return 0;
58974a4d8c2SCharles.Forsyth x = v>>16;
59074a4d8c2SCharles.Forsyth if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){
59174a4d8c2SCharles.Forsyth print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x);
59274a4d8c2SCharles.Forsyth return 0;
59374a4d8c2SCharles.Forsyth }
59474a4d8c2SCharles.Forsyth *ep = ftl->unit[x];
59574a4d8c2SCharles.Forsyth *bp = v & 0xFFFF;
59674a4d8c2SCharles.Forsyth return 1;
59774a4d8c2SCharles.Forsyth }
59874a4d8c2SCharles.Forsyth return 0;
59974a4d8c2SCharles.Forsyth }
60074a4d8c2SCharles.Forsyth
60174a4d8c2SCharles.Forsyth static void
eraseinit(Ftl * ftl,ulong offset,int id,int nerase)60274a4d8c2SCharles.Forsyth eraseinit(Ftl *ftl, ulong offset, int id, int nerase)
60374a4d8c2SCharles.Forsyth {
60474a4d8c2SCharles.Forsyth union {
60574a4d8c2SCharles.Forsyth Merase m;
60674a4d8c2SCharles.Forsyth uchar block[ERASEHDRLEN];
60774a4d8c2SCharles.Forsyth } *m;
60874a4d8c2SCharles.Forsyth uchar *bam, *p;
60974a4d8c2SCharles.Forsyth int i, nov;
61074a4d8c2SCharles.Forsyth
61174a4d8c2SCharles.Forsyth nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize; /* number of overhead blocks (header, and BAM itself) */
61274a4d8c2SCharles.Forsyth if(nov*Bsize >= ftl->segsize) {
61374a4d8c2SCharles.Forsyth fprint(2, "ftl -- too small for files");
61474a4d8c2SCharles.Forsyth exits("1");
61574a4d8c2SCharles.Forsyth }
61674a4d8c2SCharles.Forsyth eraseflash(ftl, offset);
61774a4d8c2SCharles.Forsyth m = malloc(sizeof(*m));
61874a4d8c2SCharles.Forsyth if(m == nil) {
61974a4d8c2SCharles.Forsyth fprint(2, "nomem\n");
62074a4d8c2SCharles.Forsyth exits("1");
62174a4d8c2SCharles.Forsyth }
62274a4d8c2SCharles.Forsyth memset(m, 0xFF, sizeof(*m));
62374a4d8c2SCharles.Forsyth m->m.linktuple[0] = 0x13;
62474a4d8c2SCharles.Forsyth m->m.linktuple[1] = 0x3;
62574a4d8c2SCharles.Forsyth memmove(m->m.linktuple+2, "CIS", 3);
62674a4d8c2SCharles.Forsyth m->m.orgtuple[0] = 0x46;
62774a4d8c2SCharles.Forsyth m->m.orgtuple[1] = 0x57;
62874a4d8c2SCharles.Forsyth m->m.orgtuple[2] = 0x00;
62974a4d8c2SCharles.Forsyth memmove(m->m.orgtuple+3, "FTL100", 7);
63074a4d8c2SCharles.Forsyth m->m.nxfer = 1;
63174a4d8c2SCharles.Forsyth PUT4(m->m.nerase, nerase);
63274a4d8c2SCharles.Forsyth PUT2(m->m.id, id);
63374a4d8c2SCharles.Forsyth m->m.bshift = ftl->bshift;
63474a4d8c2SCharles.Forsyth m->m.eshift = ftl->eshift;
63574a4d8c2SCharles.Forsyth PUT2(m->m.pstart, 0);
63674a4d8c2SCharles.Forsyth PUT2(m->m.nunits, ftl->nunit);
63774a4d8c2SCharles.Forsyth PUT4(m->m.psize, ftl->size - nov*Bsize);
63874a4d8c2SCharles.Forsyth PUT4(m->m.vbmbase, 0xffffffff); /* we always calculate the VBM */
63974a4d8c2SCharles.Forsyth PUT2(m->m.nvbm, 0);
64074a4d8c2SCharles.Forsyth m->m.flags = 0;
64174a4d8c2SCharles.Forsyth m->m.code = 0xFF;
64274a4d8c2SCharles.Forsyth memmove(m->m.serial, "Inf1", 4);
64374a4d8c2SCharles.Forsyth PUT4(m->m.altoffset, 0);
64474a4d8c2SCharles.Forsyth PUT4(m->m.bamoffset, BAMoffset);
64574a4d8c2SCharles.Forsyth putflash(ftl, offset, m, ERASEHDRLEN);
64674a4d8c2SCharles.Forsyth free(m);
64774a4d8c2SCharles.Forsyth if(id == XferID)
64874a4d8c2SCharles.Forsyth return;
64974a4d8c2SCharles.Forsyth nov *= 4; /* now bytes of BAM */
65074a4d8c2SCharles.Forsyth bam = malloc(nov);
65174a4d8c2SCharles.Forsyth if(bam == nil) {
65274a4d8c2SCharles.Forsyth fprint(2, "nomem");
65374a4d8c2SCharles.Forsyth exits("1");
65474a4d8c2SCharles.Forsyth }
65574a4d8c2SCharles.Forsyth for(i=0; i<nov; i += 4){
65674a4d8c2SCharles.Forsyth p = bam+i;
65774a4d8c2SCharles.Forsyth PUT4(p, ControlBlock); /* reserve them */
65874a4d8c2SCharles.Forsyth }
65974a4d8c2SCharles.Forsyth putflash(ftl, offset+BAMoffset, bam, nov);
66074a4d8c2SCharles.Forsyth free(bam);
66174a4d8c2SCharles.Forsyth }
66274a4d8c2SCharles.Forsyth
66374a4d8c2SCharles.Forsyth static Terase *
eraseload(Ftl * ftl,int x,ulong offset)66474a4d8c2SCharles.Forsyth eraseload(Ftl *ftl, int x, ulong offset)
66574a4d8c2SCharles.Forsyth {
66674a4d8c2SCharles.Forsyth union {
66774a4d8c2SCharles.Forsyth Merase m;
66874a4d8c2SCharles.Forsyth uchar block[ERASEHDRLEN];
66974a4d8c2SCharles.Forsyth } *m;
67074a4d8c2SCharles.Forsyth Terase *e;
67174a4d8c2SCharles.Forsyth uchar *p;
67274a4d8c2SCharles.Forsyth int i, nbam;
67374a4d8c2SCharles.Forsyth ulong bno, v;
67474a4d8c2SCharles.Forsyth
67574a4d8c2SCharles.Forsyth m = malloc(sizeof(*m));
67674a4d8c2SCharles.Forsyth if(m == nil) {
67774a4d8c2SCharles.Forsyth fprint(2, "nomem");
67874a4d8c2SCharles.Forsyth exits("1");
67974a4d8c2SCharles.Forsyth }
68074a4d8c2SCharles.Forsyth getflash(ftl, m, offset, ERASEHDRLEN);
68174a4d8c2SCharles.Forsyth if(memcmp(m->m.orgtuple+3, "FTL100", 7) != 0 ||
68274a4d8c2SCharles.Forsyth memcmp(m->m.serial, "Inf1", 4) != 0){
68374a4d8c2SCharles.Forsyth free(m);
68474a4d8c2SCharles.Forsyth return nil;
68574a4d8c2SCharles.Forsyth }
68674a4d8c2SCharles.Forsyth e = malloc(sizeof(*e));
68774a4d8c2SCharles.Forsyth if(e == nil){
68874a4d8c2SCharles.Forsyth free(m);
68974a4d8c2SCharles.Forsyth fprint(2, "nomem");
69074a4d8c2SCharles.Forsyth exits("1");
69174a4d8c2SCharles.Forsyth }
69274a4d8c2SCharles.Forsyth e->x = x;
69374a4d8c2SCharles.Forsyth e->id = GET2(m->m.id);
69474a4d8c2SCharles.Forsyth e->offset = offset;
69574a4d8c2SCharles.Forsyth e->bamoffset = GET4(m->m.bamoffset);
69674a4d8c2SCharles.Forsyth e->nerase = GET4(m->m.nerase);
69774a4d8c2SCharles.Forsyth e->bamx = 0;
69874a4d8c2SCharles.Forsyth e->nfree = 0;
69974a4d8c2SCharles.Forsyth e->nused = 0;
70074a4d8c2SCharles.Forsyth e->ndead = 0;
70174a4d8c2SCharles.Forsyth e->nbad = 0;
70274a4d8c2SCharles.Forsyth free(m);
70374a4d8c2SCharles.Forsyth if(e->bamoffset != BAMoffset){
70474a4d8c2SCharles.Forsyth free(e);
70574a4d8c2SCharles.Forsyth return nil;
70674a4d8c2SCharles.Forsyth }
70774a4d8c2SCharles.Forsyth e->bamoffset += offset;
70874a4d8c2SCharles.Forsyth if(e->id == XferID || e->id == XferBusy){
70974a4d8c2SCharles.Forsyth e->bam = nil;
71074a4d8c2SCharles.Forsyth e->nbam = 0;
71174a4d8c2SCharles.Forsyth return e;
71274a4d8c2SCharles.Forsyth }
71374a4d8c2SCharles.Forsyth nbam = ftl->segsize/Bsize;
71474a4d8c2SCharles.Forsyth e->bam = malloc(nbam*sizeof(*e->bam));
71574a4d8c2SCharles.Forsyth e->nbam = nbam;
71674a4d8c2SCharles.Forsyth getflash(ftl, e->bam, e->bamoffset, nbam*4);
71774a4d8c2SCharles.Forsyth /* scan BAM to build VBM */
71874a4d8c2SCharles.Forsyth e->bamx = 0;
71974a4d8c2SCharles.Forsyth for(i=0; i<nbam; i++){
72074a4d8c2SCharles.Forsyth p = (uchar*)&e->bam[i];
72174a4d8c2SCharles.Forsyth e->bam[i] = v = GET4(p);
72274a4d8c2SCharles.Forsyth if(v == Bwriting || v == Bdeleted)
72374a4d8c2SCharles.Forsyth e->ndead++;
72474a4d8c2SCharles.Forsyth else if(v == Bfree){
72574a4d8c2SCharles.Forsyth if(e->bamx == 0)
72674a4d8c2SCharles.Forsyth e->bamx = i;
72774a4d8c2SCharles.Forsyth e->nfree++;
72874a4d8c2SCharles.Forsyth ftl->nfree++;
72974a4d8c2SCharles.Forsyth }else{
73074a4d8c2SCharles.Forsyth switch(v & BlockType){
73174a4d8c2SCharles.Forsyth case ControlBlock:
73274a4d8c2SCharles.Forsyth break;
73374a4d8c2SCharles.Forsyth case DataBlock:
73474a4d8c2SCharles.Forsyth /* add to VBM */
73574a4d8c2SCharles.Forsyth if(v & (1<<31))
73674a4d8c2SCharles.Forsyth break; /* negative => VBM page, ignored */
73774a4d8c2SCharles.Forsyth bno = BNO(v & ~BlockType);
73874a4d8c2SCharles.Forsyth if(i < ftl->fstart || bno >= ftl->nblock){
73974a4d8c2SCharles.Forsyth print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v);
74074a4d8c2SCharles.Forsyth e->nbad++;
74174a4d8c2SCharles.Forsyth break;
74274a4d8c2SCharles.Forsyth }
74374a4d8c2SCharles.Forsyth ftl->vbm[bno] = (e->x<<16) | i;
74474a4d8c2SCharles.Forsyth e->nused++;
74574a4d8c2SCharles.Forsyth break;
74674a4d8c2SCharles.Forsyth case ReplacePage:
74774a4d8c2SCharles.Forsyth /* replacement VBM page; ignored */
74874a4d8c2SCharles.Forsyth break;
74974a4d8c2SCharles.Forsyth default:
75074a4d8c2SCharles.Forsyth print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v);
75174a4d8c2SCharles.Forsyth case BadBlock:
75274a4d8c2SCharles.Forsyth e->nbad++;
75374a4d8c2SCharles.Forsyth break;
75474a4d8c2SCharles.Forsyth }
75574a4d8c2SCharles.Forsyth }
75674a4d8c2SCharles.Forsyth }
75774a4d8c2SCharles.Forsyth return e;
75874a4d8c2SCharles.Forsyth }
75974a4d8c2SCharles.Forsyth
76074a4d8c2SCharles.Forsyth static void
erasefree(Terase * e)76174a4d8c2SCharles.Forsyth erasefree(Terase *e)
76274a4d8c2SCharles.Forsyth {
76374a4d8c2SCharles.Forsyth free(e->bam);
76474a4d8c2SCharles.Forsyth free(e);
76574a4d8c2SCharles.Forsyth }
76674a4d8c2SCharles.Forsyth
76774a4d8c2SCharles.Forsyth static void
eraseflash(Ftl * ftl,ulong offset)76874a4d8c2SCharles.Forsyth eraseflash(Ftl *ftl, ulong offset)
76974a4d8c2SCharles.Forsyth {
77074a4d8c2SCharles.Forsyth offset += ftl->base;
77174a4d8c2SCharles.Forsyth if(FTLDEBUG || ftl->trace || trace)
77274a4d8c2SCharles.Forsyth print("ftl: erase seg @#%lux\n", offset);
77374a4d8c2SCharles.Forsyth memset(flashm+offset, 0xff, secsize);
77474a4d8c2SCharles.Forsyth }
77574a4d8c2SCharles.Forsyth
77674a4d8c2SCharles.Forsyth static void
putflash(Ftl * ftl,ulong offset,void * buf,long n)77774a4d8c2SCharles.Forsyth putflash(Ftl *ftl, ulong offset, void *buf, long n)
77874a4d8c2SCharles.Forsyth {
77974a4d8c2SCharles.Forsyth offset += ftl->base;
78074a4d8c2SCharles.Forsyth if(ftl->trace || trace)
78174a4d8c2SCharles.Forsyth print("ftl: write(#%lux, %ld)\n", offset, n);
78274a4d8c2SCharles.Forsyth memcpy(flashm+offset, buf, n);
78374a4d8c2SCharles.Forsyth }
78474a4d8c2SCharles.Forsyth
78574a4d8c2SCharles.Forsyth static void
getflash(Ftl * ftl,void * buf,ulong offset,long n)78674a4d8c2SCharles.Forsyth getflash(Ftl *ftl, void *buf, ulong offset, long n)
78774a4d8c2SCharles.Forsyth {
78874a4d8c2SCharles.Forsyth offset += ftl->base;
78974a4d8c2SCharles.Forsyth if(ftl->trace || trace)
79074a4d8c2SCharles.Forsyth print("ftl: read(#%lux, %ld)\n", offset, n);
79174a4d8c2SCharles.Forsyth memcpy(buf, flashm+offset, n);
79274a4d8c2SCharles.Forsyth }
79374a4d8c2SCharles.Forsyth
79474a4d8c2SCharles.Forsyth #define BUFSIZE 8192
79574a4d8c2SCharles.Forsyth
79674a4d8c2SCharles.Forsyth void
main(int argc,char ** argv)79774a4d8c2SCharles.Forsyth main(int argc, char **argv)
79874a4d8c2SCharles.Forsyth {
79974a4d8c2SCharles.Forsyth int k, r, sz, offset = 0;
80074a4d8c2SCharles.Forsyth char *buf, *buf1;
80174a4d8c2SCharles.Forsyth int fd1, fd2;
80274a4d8c2SCharles.Forsyth
80374a4d8c2SCharles.Forsyth if (argc != 5) {
80474a4d8c2SCharles.Forsyth fprint(2, "usage: %s flashsize secsize kfsfile flashfile\n", argv[0]);
80574a4d8c2SCharles.Forsyth exits("1");
80674a4d8c2SCharles.Forsyth }
80774a4d8c2SCharles.Forsyth flashsize = strtol(argv[1], nil, 0);
80874a4d8c2SCharles.Forsyth secsize = strtol(argv[2], nil , 0);
80974a4d8c2SCharles.Forsyth fd1 = open(argv[3], OREAD);
81074a4d8c2SCharles.Forsyth fd2 = create(argv[4], OWRITE, 0644);
81174a4d8c2SCharles.Forsyth if (fd1 < 0 || fd2 < 0) {
81274a4d8c2SCharles.Forsyth fprint(2, "bad io files\n");
81374a4d8c2SCharles.Forsyth exits("1");
81474a4d8c2SCharles.Forsyth }
81574a4d8c2SCharles.Forsyth if(secsize == 0 || secsize > flashsize || secsize&(secsize-1) || 0&(secsize-1) || flashsize == 0 || flashsize != Nolimit && flashsize&(secsize-1)) {
81674a4d8c2SCharles.Forsyth fprint(2, "bad sizes\n");
81774a4d8c2SCharles.Forsyth exits("1");
81874a4d8c2SCharles.Forsyth }
81974a4d8c2SCharles.Forsyth for(k=0; k<32 && (1<<k) != secsize; k++)
82074a4d8c2SCharles.Forsyth ;
82174a4d8c2SCharles.Forsyth flashm = malloc(flashsize);
82274a4d8c2SCharles.Forsyth buf = malloc(BUFSIZE);
82374a4d8c2SCharles.Forsyth if (flashm == nil) {
82474a4d8c2SCharles.Forsyth fprint(2, "no mem for flash\n");
82574a4d8c2SCharles.Forsyth exits("1");
82674a4d8c2SCharles.Forsyth }
82774a4d8c2SCharles.Forsyth ftls = mkftl("FLASH", 0, Nolimit, k, "format");
82874a4d8c2SCharles.Forsyth for (;;) {
82974a4d8c2SCharles.Forsyth r = read(fd1, buf, BUFSIZE);
83074a4d8c2SCharles.Forsyth if (r <= 0)
83174a4d8c2SCharles.Forsyth break;
83274a4d8c2SCharles.Forsyth if (ftlwrite(buf, r, offset) != r) {
83374a4d8c2SCharles.Forsyth fprint(2, "ftlwrite failed - input file too big\n");
83474a4d8c2SCharles.Forsyth exits("1");
83574a4d8c2SCharles.Forsyth }
83674a4d8c2SCharles.Forsyth offset += r;
83774a4d8c2SCharles.Forsyth }
83874a4d8c2SCharles.Forsyth write(fd2, flashm, flashsize);
83974a4d8c2SCharles.Forsyth close(fd1);
84074a4d8c2SCharles.Forsyth close(fd2);
84174a4d8c2SCharles.Forsyth ftlstat(offset);
84274a4d8c2SCharles.Forsyth /* ftls = mkftl("FLASH", 0, Nolimit, k, "init"); */
84374a4d8c2SCharles.Forsyth sz = offset;
84474a4d8c2SCharles.Forsyth offset = 0;
84574a4d8c2SCharles.Forsyth buf1 = malloc(BUFSIZE);
84674a4d8c2SCharles.Forsyth fd1 = open(argv[3], OREAD);
84774a4d8c2SCharles.Forsyth for (;;) {
84874a4d8c2SCharles.Forsyth r = read(fd1, buf1, BUFSIZE);
84974a4d8c2SCharles.Forsyth if (r <= 0)
85074a4d8c2SCharles.Forsyth break;
85174a4d8c2SCharles.Forsyth if (ftlread(buf, r, offset) != r) {
85274a4d8c2SCharles.Forsyth fprint(2, "ftlread failed\n");
85374a4d8c2SCharles.Forsyth exits("1");
85474a4d8c2SCharles.Forsyth }
85574a4d8c2SCharles.Forsyth if (memcmp(buf, buf1, r) != 0) {
85674a4d8c2SCharles.Forsyth fprint(2, "bad read\n");
85774a4d8c2SCharles.Forsyth exits("1");
85874a4d8c2SCharles.Forsyth }
85974a4d8c2SCharles.Forsyth offset += r;
86074a4d8c2SCharles.Forsyth }
86174a4d8c2SCharles.Forsyth close(fd1);
86274a4d8c2SCharles.Forsyth if (offset != sz) {
86374a4d8c2SCharles.Forsyth fprint(2, "bad final offset\n");
86474a4d8c2SCharles.Forsyth exits("1");
86574a4d8c2SCharles.Forsyth }
86674a4d8c2SCharles.Forsyth exits("0");
86774a4d8c2SCharles.Forsyth }
868