xref: /inferno-os/utils/ftl/ftl.c (revision 9dc22068e29604f4b484e746112a9a4efe6fd57f)
1 /*
2  * basic Flash Translation Layer driver
3  *	see for instance the Intel technical paper
4  *	``Understanding the Flash Translation Layer (FTL) Specification''
5  *	Order number 297816-001 (online at www.intel.com)
6  *
7  * a public driver by David Hinds, dhinds@allegro.stanford.edu
8  * further helps with some details.
9  *
10  * this driver uses the common simplification of never storing
11  * the VBM on the medium (a waste of precious flash!) but
12  * rather building it on the fly as the block maps are read.
13  *
14  * Plan 9 driver (c) 1997 by C H Forsyth (forsyth@caldo.demon.co.uk)
15  *	This driver may be used or adapted by anyone for any non-commercial purpose.
16  *
17  * adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (charles@vitanuova.com)
18  *
19  * C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies
20  * to use this driver freely for any Inferno-related purposes whatever,
21  * including commercial applications.
22  *
23  * TO DO:
24  *	check error handling details for get/put flash
25  *	bad block handling
26  *	reserved space in formatted size
27  *	possibly block size as parameter
28  *	fetch parameters from header on init
29  *
30  * Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited
31  * 	usage : ftl flashsize secsize inputfile outputfile
32  * outputfile will then be a ftl image of inputfile
33  * nb assumes the base address is zero
34  *
35  */
36 
37 #include <lib9.h>
38 
39 ulong flashsize, secsize;
40 char *flashm;
41 int trace = 0;
42 
43 #ifndef offsetof
44 #define offsetof(T,X) ((ulong)&(((T*)0)->X))
45 #endif
46 
47 typedef struct Ftl Ftl;
48 typedef struct Merase Merase;
49 typedef struct Terase Terase;
50 
51 enum {
52 	Eshift = 18,	/* 2^18=256k; log2(eraseunit) */
53 	Flashseg = 1<<Eshift,
54 	Bshift = 9,		/* 2^9=512 */
55 	Bsize = 1<<Bshift,
56 	BAMoffset = 0x100,
57 	Nolimit = ~0,
58 	USABLEPCT = 95,	/* release only this % to client */
59 
60 	FTLDEBUG = 0
61 };
62 
63 /* erase unit header (defined by FTL specification) */
64 struct Merase {
65 	uchar	linktuple[5];
66 	uchar	orgtuple[10];
67 	uchar	nxfer;
68 	uchar	nerase[4];
69 	uchar	id[2];
70 	uchar	bshift;
71 	uchar	eshift;
72 	uchar	pstart[2];
73 	uchar	nunits[2];
74 	uchar	psize[4];
75 	uchar	vbmbase[4];
76 	uchar	nvbm[2];
77 	uchar	flags;
78 	uchar	code;
79 	uchar	serial[4];
80 	uchar	altoffset[4];
81 	uchar	bamoffset[4];
82 	uchar	rsv2[12];
83 };
84 #define	ERASEHDRLEN	64
85 
86 enum {
87 	/* special unit IDs */
88 	XferID = 0xffff,
89 	XferBusy = 0x7fff,
90 
91 	/* special BAM addresses */
92 	Bfree = 0xffffffff,
93 	Bwriting = 0xfffffffe,
94 	Bdeleted = 0,
95 
96 	/* block types */
97 	TypeShift = 7,
98 	BlockType = (1<<TypeShift)-1,
99 	ControlBlock = 0x30,
100 	DataBlock = 0x40,
101 	ReplacePage = 0x60,
102 	BadBlock = 0x70,
103 };
104 
105 #define	BTYPE(b)	((b) & BlockType)
106 #define	BADDR(b)	((b) & ~BlockType)
107 #define	BNO(va)	(((ulong)(va))>>Bshift)
108 #define	MKBAM(b,t)	(((b)<<Bshift)|(t))
109 
110 struct Terase {
111 	int	x;
112 	int	id;
113 	ulong	offset;
114 	ulong	bamoffset;
115 	ulong	nbam;
116 	ulong*	bam;
117 	ulong	bamx;
118 	ulong	nfree;
119 	ulong	nused;
120 	ulong	ndead;
121 	ulong	nbad;
122 	ulong	nerase;
123 };
124 
125 struct Ftl {
126 	ulong	base;	/* base of flash region */
127 	ulong	size;	/* size of flash region */
128 	ulong	segsize;	/* size of flash segment (erase unit) */
129 	int	eshift;	/* log2(erase-unit-size) */
130 	int	bshift;	/* log2(bsize) */
131 	int	bsize;
132 	int	nunit;	/* number of segments (erase units) */
133 	Terase**	unit;
134 	int	lastx;	/* index in unit of last allocation */
135 	int	xfer;		/* index in unit of current transfer unit (-1 if none) */
136 	ulong	nfree;	/* total free space in blocks */
137 	ulong	nblock;	/* total space in blocks */
138 	ulong	rwlimit;	/* user-visible block limit (`formatted size') */
139 	ulong*	vbm;		/* virtual block map */
140 	ulong	fstart;	/* address of first block of data in a segment */
141 	int	trace;	/* (debugging) trace of read/write actions */
142 	int	detach;	/* free Ftl on last close */
143 
144 	/* scavenging variables */
145 	int	needspace;
146 	int	hasproc;
147 };
148 
149 enum {
150 	/* Ftl.detach */
151 	Detached = 1,	/* detach on close */
152 	Deferred	/* scavenger must free it */
153 };
154 
155 /* little endian */
156 #define	GET2(p)	(((p)[1]<<8)|(p)[0])
157 #define	GET4(p)	(((((((p)[3]<<8)|(p)[2])<<8)|(p)[1])<<8)|(p)[0])
158 #define	PUT2(p,v)	(((p)[1]=(v)>>8),((p)[0]=(v)))
159 #define	PUT4(p,v)	(((p)[3]=(v)>>24),((p)[2]=(v)>>16),((p)[1]=(v)>>8),((p)[0]=(v)))
160 
161 static	Ftl	*ftls;
162 
163 static	ulong	allocblk(Ftl*);
164 static	void	eraseflash(Ftl*, ulong);
165 static	void	erasefree(Terase*);
166 static	void	eraseinit(Ftl*, ulong, int, int);
167 static	Terase*	eraseload(Ftl*, int, ulong);
168 static	void	ftlfree(Ftl*);
169 static	void	getflash(Ftl*, void*, ulong, long);
170 static	int	mapblk(Ftl*, ulong, Terase**, ulong*);
171 static	Ftl*	mkftl(char*, ulong, ulong, int, char*);
172 static	void	putbam(Ftl*, Terase*, int, ulong);
173 static	void	putflash(Ftl*, ulong, void*, long);
174 static	int	scavenge(Ftl*);
175 
176 static void
177 ftlstat(int sz)
178 {
179 	print("0x%lux:0x%ux:0x%lux\n", ftls->rwlimit*Bsize, sz, flashsize);
180 	print("%lud:%d:%lud in 512b blocks\n", ftls->rwlimit, sz>>Bshift, flashsize>>Bshift);
181 }
182 
183 static long
184 ftlread(void *buf, long n, ulong offset)
185 {
186 	Ftl *ftl;
187 	Terase *e;
188 	int nb;
189 	uchar *a;
190 	ulong pb;
191 
192 		if(n <= 0 || n%Bsize || offset%Bsize) {
193 			fprint(2, "bad read\n");
194 			exits("1");
195 		}
196 		ftl = ftls;
197 		nb = n/Bsize;
198 		offset /= Bsize;
199 		if(offset >= ftl->rwlimit)
200 			return 0;
201 		if(offset+nb > ftl->rwlimit)
202 			nb = ftl->rwlimit - offset;
203 		a = buf;
204 		for(n = 0; n < nb; n++){
205 			if(mapblk(ftl, offset+n, &e, &pb))
206 				getflash(ftl, a, e->offset + pb*Bsize, Bsize);
207 			else
208 				memset(a, 0, Bsize);
209 			a += Bsize;
210 		}
211 		return a-(uchar*)buf;
212 	return 0;		/* not reached */
213 }
214 
215 static long
216 ftlwrite(void *buf, long n, ulong offset)
217 {
218 	int ns, nb;
219 	uchar *a;
220 	Terase *e, *oe;
221 	ulong ob, v;
222 	Ftl *ftl;
223 
224 	if(n <= 0)
225 		return 0;
226 		ftl = ftls;
227 		if(n <= 0 || n%Bsize || offset%Bsize) {
228 			fprint(2, "bad write\n");
229 			exits("1");
230 		}
231 		nb = n/Bsize;
232 		offset /= Bsize;
233 		if(offset >= ftl->rwlimit)
234 			return 0;
235 		if(offset+nb > ftl->rwlimit)
236 			nb = ftl->rwlimit - offset;
237 		a = buf;
238 		for(n = 0; n < nb; n++){
239 			ns = 0;
240 			while((v = allocblk(ftl)) == 0)
241 				if(!scavenge(ftl) || ++ns > 3){
242 					print("ftl: flash memory full\n");
243 				}
244 			if(!mapblk(ftl, offset+n, &oe, &ob))
245 				oe = nil;
246 			e = ftl->unit[v>>16];
247 			v &= 0xffff;
248 			putflash(ftl, e->offset + v*Bsize, a, Bsize);
249 			putbam(ftl, e, v, MKBAM(offset+n, DataBlock));
250 			/* both old and new block references exist in this window (can't be closed?) */
251 			ftl->vbm[offset+n] = (e->x<<16) | v;
252 			if(oe != nil){
253 				putbam(ftl, oe, ob, Bdeleted);
254 				oe->ndead++;
255 			}
256 			a += Bsize;
257 		}
258 		return a-(uchar*)buf;
259 	return 0;		/* not reached */
260 }
261 
262 static Ftl *
263 mkftl(char *fname, ulong base, ulong size, int eshift, char *op)
264 {
265 	int i, j, nov, segblocks;
266 	ulong limit;
267 	Terase *e;
268 	Ftl *ftl;
269 
270 	ftl = malloc(sizeof(*ftl));
271 	if(ftl == nil) {
272 		fprint(2, "out of memory\n");
273 		exits("1");
274 	}
275 	ftl->lastx = 0;
276 	ftl->detach = 0;
277 	ftl->needspace = 0;
278 	ftl->hasproc = 0;
279 	ftl->trace = 0;
280 	limit = flashsize;
281 	if(size == Nolimit)
282 		size = limit-base;
283 	if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) {
284 		fprint(2, "bad flash space parameters");
285 		exits("1");
286 	}
287 	if(FTLDEBUG || ftl->trace || trace)
288 		print("%s flash %s #%lux:#%lux limit #%lux\n", op, fname, base, size, limit);
289 	ftl->base = base;
290 	ftl->size = size;
291 	ftl->bshift = Bshift;
292 	ftl->bsize = Bsize;
293 	ftl->eshift = eshift;
294 	ftl->segsize = 1<<eshift;
295 	ftl->nunit = size>>eshift;
296 	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks per segment (header, and BAM itself) */
297 	ftl->fstart = nov;
298 	segblocks = ftl->segsize/Bsize - nov;
299 	ftl->nblock = ftl->nunit*segblocks;
300 	if(ftl->nblock >= 0x10000)
301 		ftl->nblock = 0x10000;
302 	ftl->vbm = malloc(ftl->nblock*sizeof(*ftl->vbm));
303 	ftl->unit = malloc(ftl->nunit*sizeof(*ftl->unit));
304 	if(ftl->vbm == nil || ftl->unit == nil) {
305 		fprint(2, "out of mem");
306 		exits("1");
307 	}
308 	for(i=0; i<ftl->nblock; i++)
309 		ftl->vbm[i] = 0;
310 	if(strcmp(op, "format") == 0){
311 		for(i=0; i<ftl->nunit-1; i++)
312 			eraseinit(ftl, i*ftl->segsize, i, 1);
313 		eraseinit(ftl, i*ftl->segsize, XferID, 1);
314 	}
315 	ftl->xfer = -1;
316 	for(i=0; i<ftl->nunit; i++){
317 		e = eraseload(ftl, i, i*ftl->segsize);
318 		if(e == nil){
319 			print("ftl: logical segment %d: bad format\n", i);
320 			continue;
321 		}
322 		if(e->id == XferBusy){
323 			e->nerase++;
324 			eraseinit(ftl, e->offset, XferID, e->nerase);
325 			e->id = XferID;
326 		}
327 		for(j=0; j<ftl->nunit; j++)
328 			if(ftl->unit[j] != nil && ftl->unit[j]->id == e->id){
329 				print("ftl: duplicate erase unit #%x\n", e->id);
330 				erasefree(e);
331 				e = nil;
332 				break;
333 			}
334 		if(e){
335 			ftl->unit[e->x] = e;
336 			if(e->id == XferID)
337 				ftl->xfer = e->x;
338 			if (FTLDEBUG || ftl->trace || trace)
339 				print("ftl: unit %d:#%x used %lud free %lud dead %lud bad %lud nerase %lud\n",
340 					e->x, e->id, e->nused, e->nfree, e->ndead, e->nbad, e->nerase);
341 		}
342 	}
343 	if(ftl->xfer < 0 && ftl->nunit <= 0 || ftl->xfer >= 0 && ftl->nunit <= 1) {
344 		fprint(2, "no valid flash data units");
345 		exits("1");
346 	}
347 	if(ftl->xfer < 0)
348 		print("ftl: no transfer unit: device is WORM\n");
349 	else
350 		ftl->nblock -= segblocks;	/* discount transfer segment */
351 	if(ftl->nblock >= 1000)
352 		ftl->rwlimit = ftl->nblock-100;	/* TO DO: variable reserve */
353 	else
354 		ftl->rwlimit = ftl->nblock*USABLEPCT/100;
355 	return ftl;
356 }
357 
358 static void
359 ftlfree(Ftl *ftl)
360 {
361 	if(ftl != nil){
362 		free(ftl->unit);
363 		free(ftl->vbm);
364 		free(ftl);
365 	}
366 }
367 
368 /*
369  * this simple greedy algorithm weighted by nerase does seem to lead
370  * to even wear of erase units (cf. the eNVy file system)
371  */
372 static Terase *
373 bestcopy(Ftl *ftl)
374 {
375 	Terase *e, *be;
376 	int i;
377 
378 	be = nil;
379 	for(i=0; i<ftl->nunit; i++)
380 		if((e = ftl->unit[i]) != nil && e->id != XferID && e->id != XferBusy && e->ndead+e->nbad &&
381 		    (be == nil || e->nerase <= be->nerase && e->ndead >= be->ndead))
382 			be = e;
383 	return be;
384 }
385 
386 static int
387 copyunit(Ftl *ftl, Terase *from, Terase *to)
388 {
389 	int i, nb;
390 	uchar id[2];
391 	ulong *bam;
392 	uchar *buf;
393 	ulong v, bno;
394 
395 	if(FTLDEBUG || ftl->trace || trace)
396 		print("ftl: copying %d (#%lux) to #%lux\n", from->id, from->offset, to->offset);
397 	to->nbam = 0;
398 	free(to->bam);
399 	to->bam = nil;
400 	buf = malloc(Bsize);
401 	if(buf == nil)
402 		return 0;
403 	PUT2(id, XferBusy);
404 	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
405 	/* make new BAM */
406 	nb = from->nbam*sizeof(*to->bam);
407 	bam = malloc(nb);
408 	if(bam == nil) {
409 		fprint(2, "nomem\n");
410 		exits("1");
411 	}
412 	memmove(bam, from->bam, nb);
413 	to->nused = 0;
414 	to->nbad = 0;
415 	to->nfree = 0;
416 	to->ndead = 0;
417 	for(i = 0; i < from->nbam; i++)
418 		switch(bam[i]){
419 		case Bwriting:
420 		case Bdeleted:
421 		case Bfree:
422 			bam[i] = Bfree;
423 			to->nfree++;
424 			break;
425 		default:
426 			switch(bam[i]&BlockType){
427 			default:
428 			case BadBlock:	/* it isn't necessarily bad in this unit */
429 				to->nfree++;
430 				bam[i] = Bfree;
431 				break;
432 			case DataBlock:
433 			case ReplacePage:
434 				v = bam[i];
435 				bno = BNO(v & ~BlockType);
436 				if(i < ftl->fstart || bno >= ftl->nblock){
437 					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", from->x, from->id, i, v);
438 					to->nfree++;
439 					bam[i] = Bfree;
440 					break;
441 				}
442 				getflash(ftl, buf, from->offset+i*Bsize, Bsize);
443 				putflash(ftl, to->offset+i*Bsize, buf, Bsize);
444 				to->nused++;
445 				break;
446 			case ControlBlock:
447 				to->nused++;
448 				break;
449 			}
450 		}
451 	for(i=0; i<from->nbam; i++){
452 		uchar *p = (uchar*)&bam[i];
453 		v = bam[i];
454 		if(v != Bfree && ftl->trace > 1)
455 			print("to[%d]=#%lux\n", i, v);
456 		PUT4(p, v);
457 	}
458 	putflash(ftl, to->bamoffset, bam, nb);	/* BUG: PUT4 */
459 	for(i=0; i<from->nbam; i++){
460 		uchar *p = (uchar*)&bam[i];
461 		v = bam[i];
462 		PUT4(p, v);
463 	}
464 	to->id = from->id;
465 	PUT2(id, to->id);
466 	putflash(ftl, to->offset+offsetof(Merase,id[0]), id, 2);
467 	to->nbam = from->nbam;
468 	to->bam = bam;
469 	ftl->nfree += to->nfree - from->nfree;
470 	free(buf);
471 	return 1;
472 }
473 
474 static int
475 mustscavenge(void *a)
476 {
477 	return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred;
478 }
479 
480 static int
481 donescavenge(void *a)
482 {
483 	return ((Ftl*)a)->needspace == 0;
484 }
485 
486 static void
487 scavengeproc(void *arg)
488 {
489 	Ftl *ftl;
490 	int i;
491 	Terase *e, *ne;
492 
493 	ftl = arg;
494 	if(mustscavenge(ftl)){
495 		if(ftl->detach == Deferred){
496 			ftlfree(ftl);
497 			fprint(2, "scavenge out of memory\n");
498 			exits("1");
499 		}
500 		if(FTLDEBUG || ftl->trace || trace)
501 			print("ftl: scavenge %ld\n", ftl->nfree);
502 		e = bestcopy(ftl);
503 		if(e == nil || ftl->xfer < 0 || (ne = ftl->unit[ftl->xfer]) == nil || ne->id != XferID || e == ne)
504 			goto Fail;
505 		if(copyunit(ftl, e, ne)){
506 			i = ne->x; ne->x = e->x; e->x = i;
507 			ftl->unit[ne->x] = ne;
508 			ftl->unit[e->x] = e;
509 			ftl->xfer = e->x;
510 			e->id = XferID;
511 			e->nbam = 0;
512 			free(e->bam);
513 			e->bam = nil;
514 			e->bamx = 0;
515 			e->nerase++;
516 			eraseinit(ftl, e->offset, XferID, e->nerase);
517 		}
518 	Fail:
519 		if(FTLDEBUG || ftl->trace || trace)
520 			print("ftl: end scavenge %ld\n", ftl->nfree);
521 		ftl->needspace = 0;
522 	}
523 }
524 
525 static int
526 scavenge(Ftl *ftl)
527 {
528 	if(ftl->xfer < 0 || bestcopy(ftl) == nil)
529 		return 0;	/* you worm! */
530 
531 	if(!ftl->hasproc){
532 		ftl->hasproc = 1;
533 	}
534 	ftl->needspace = 1;
535 
536 	scavengeproc(ftls);
537 
538 	return ftl->nfree;
539 }
540 
541 static void
542 putbam(Ftl *ftl, Terase *e, int n, ulong entry)
543 {
544 	uchar b[4];
545 
546 	e->bam[n] = entry;
547 	PUT4(b, entry);
548 	putflash(ftl, e->bamoffset + n*4, b, 4);
549 }
550 
551 static ulong
552 allocblk(Ftl *ftl)
553 {
554 	Terase *e;
555 	int i, j;
556 
557 	i = ftl->lastx;
558 	do{
559 		e = ftl->unit[i];
560 		if(e != nil && e->id != XferID && e->nfree){
561 			ftl->lastx = i;
562 			for(j=e->bamx; j<e->nbam; j++)
563 				if(e->bam[j] == Bfree){
564 					putbam(ftl, e, j, Bwriting);
565 					ftl->nfree--;
566 					e->nfree--;
567 					e->bamx = j+1;
568 					return (e->x<<16) | j;
569 				}
570 			e->nfree = 0;
571 			print("ftl: unit %d:#%x nfree %ld but not free in BAM\n", e->x, e->id, e->nfree);
572 		}
573 		if(++i >= ftl->nunit)
574 			i = 0;
575 	}while(i != ftl->lastx);
576 	return 0;
577 }
578 
579 static int
580 mapblk(Ftl *ftl, ulong bno, Terase **ep, ulong *bp)
581 {
582 	ulong v;
583 	int x;
584 
585 	if(bno < ftl->nblock){
586 		v = ftl->vbm[bno];
587 		if(v == 0 || v == ~0)
588 			return 0;
589 		x = v>>16;
590 		if(x >= ftl->nunit || x == ftl->xfer || ftl->unit[x] == nil){
591 			print("ftl: corrupt format: bad block mapping %lud -> unit #%x\n", bno, x);
592 			return 0;
593 		}
594 		*ep = ftl->unit[x];
595 		*bp = v & 0xFFFF;
596 		return 1;
597 	}
598 	return 0;
599 }
600 
601 static void
602 eraseinit(Ftl *ftl, ulong offset, int id, int nerase)
603 {
604 	union {
605 		Merase	m;
606 		uchar	block[ERASEHDRLEN];
607 	} *m;
608 	uchar *bam, *p;
609 	int i, nov;
610 
611 	nov = ((ftl->segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	/* number of overhead blocks (header, and BAM itself) */
612 	if(nov*Bsize >= ftl->segsize) {
613 		fprint(2, "ftl -- too small for files");
614 		exits("1");
615 	}
616 	eraseflash(ftl, offset);
617 	m = malloc(sizeof(*m));
618 	if(m == nil) {
619 		fprint(2, "nomem\n");
620 		exits("1");
621 	}
622 	memset(m, 0xFF, sizeof(*m));
623 	m->m.linktuple[0] = 0x13;
624 	m->m.linktuple[1] = 0x3;
625 	memmove(m->m.linktuple+2, "CIS", 3);
626 	m->m.orgtuple[0] = 0x46;
627 	m->m.orgtuple[1] = 0x57;
628 	m->m.orgtuple[2] = 0x00;
629 	memmove(m->m.orgtuple+3, "FTL100", 7);
630 	m->m.nxfer = 1;
631 	PUT4(m->m.nerase, nerase);
632 	PUT2(m->m.id, id);
633 	m->m.bshift = ftl->bshift;
634 	m->m.eshift = ftl->eshift;
635 	PUT2(m->m.pstart, 0);
636 	PUT2(m->m.nunits, ftl->nunit);
637 	PUT4(m->m.psize, ftl->size - nov*Bsize);
638 	PUT4(m->m.vbmbase, 0xffffffff);	/* we always calculate the VBM */
639 	PUT2(m->m.nvbm, 0);
640 	m->m.flags = 0;
641 	m->m.code = 0xFF;
642 	memmove(m->m.serial, "Inf1", 4);
643 	PUT4(m->m.altoffset, 0);
644 	PUT4(m->m.bamoffset, BAMoffset);
645 	putflash(ftl, offset, m, ERASEHDRLEN);
646 	free(m);
647 	if(id == XferID)
648 		return;
649 	nov *= 4;	/* now bytes of BAM */
650 	bam = malloc(nov);
651 	if(bam == nil) {
652 		fprint(2, "nomem");
653 		exits("1");
654 	}
655 	for(i=0; i<nov; i += 4){
656 		p = bam+i;
657 		PUT4(p, ControlBlock);	/* reserve them */
658 	}
659 	putflash(ftl, offset+BAMoffset, bam, nov);
660 	free(bam);
661 }
662 
663 static Terase *
664 eraseload(Ftl *ftl, int x, ulong offset)
665 {
666 	union {
667 		Merase	m;
668 		uchar	block[ERASEHDRLEN];
669 	} *m;
670 	Terase *e;
671 	uchar *p;
672 	int i, nbam;
673 	ulong bno, v;
674 
675 	m = malloc(sizeof(*m));
676 	if(m == nil) {
677 		fprint(2, "nomem");
678 		exits("1");
679 	}
680 	getflash(ftl, m, offset, ERASEHDRLEN);
681 	if(memcmp(m->m.orgtuple+3, "FTL100", 7) != 0 ||
682 	   memcmp(m->m.serial, "Inf1", 4) != 0){
683 		free(m);
684 		return nil;
685 	}
686 	e = malloc(sizeof(*e));
687 	if(e == nil){
688 		free(m);
689 		fprint(2, "nomem");
690 		exits("1");
691 	}
692 	e->x = x;
693 	e->id = GET2(m->m.id);
694 	e->offset = offset;
695 	e->bamoffset = GET4(m->m.bamoffset);
696 	e->nerase = GET4(m->m.nerase);
697 	e->bamx = 0;
698 	e->nfree = 0;
699 	e->nused = 0;
700 	e->ndead = 0;
701 	e->nbad = 0;
702 	free(m);
703 	if(e->bamoffset != BAMoffset){
704 		free(e);
705 		return nil;
706 	}
707 	e->bamoffset += offset;
708 	if(e->id == XferID || e->id == XferBusy){
709 		e->bam = nil;
710 		e->nbam = 0;
711 		return e;
712 	}
713 	nbam = ftl->segsize/Bsize;
714 	e->bam = malloc(nbam*sizeof(*e->bam));
715 	e->nbam = nbam;
716 	getflash(ftl, e->bam, e->bamoffset, nbam*4);
717 	/* scan BAM to build VBM */
718 	e->bamx = 0;
719 	for(i=0; i<nbam; i++){
720 		p = (uchar*)&e->bam[i];
721 		e->bam[i] = v = GET4(p);
722 		if(v == Bwriting || v == Bdeleted)
723 			e->ndead++;
724 		else if(v == Bfree){
725 			if(e->bamx == 0)
726 				e->bamx = i;
727 			e->nfree++;
728 			ftl->nfree++;
729 		}else{
730 			switch(v & BlockType){
731 			case ControlBlock:
732 				break;
733 			case DataBlock:
734 				/* add to VBM */
735 				if(v & (1<<31))
736 					break;	/* negative => VBM page, ignored */
737 				bno = BNO(v & ~BlockType);
738 				if(i < ftl->fstart || bno >= ftl->nblock){
739 					print("ftl: unit %d:#%x bad bam[%d]=#%lux\n", e->x, e->id, i, v);
740 					e->nbad++;
741 					break;
742 				}
743 				ftl->vbm[bno] = (e->x<<16) | i;
744 				e->nused++;
745 				break;
746 			case ReplacePage:
747 				/* replacement VBM page; ignored */
748 				break;
749 			default:
750 				print("ftl: unit %d:#%x bad bam[%d]=%lux\n", e->x, e->id, i, v);
751 			case BadBlock:
752 				e->nbad++;
753 				break;
754 			}
755 		}
756 	}
757 	return e;
758 }
759 
760 static void
761 erasefree(Terase *e)
762 {
763 	free(e->bam);
764 	free(e);
765 }
766 
767 static void
768 eraseflash(Ftl *ftl, ulong offset)
769 {
770 	offset += ftl->base;
771 	if(FTLDEBUG || ftl->trace || trace)
772 		print("ftl: erase seg @#%lux\n", offset);
773 	memset(flashm+offset, 0xff, secsize);
774 }
775 
776 static void
777 putflash(Ftl *ftl, ulong offset, void *buf, long n)
778 {
779 	offset += ftl->base;
780 	if(ftl->trace || trace)
781 		print("ftl: write(#%lux, %ld)\n", offset, n);
782 	memcpy(flashm+offset, buf, n);
783 }
784 
785 static void
786 getflash(Ftl *ftl, void *buf, ulong offset, long n)
787 {
788 	offset += ftl->base;
789 	if(ftl->trace || trace)
790 		print("ftl: read(#%lux, %ld)\n", offset, n);
791 	memcpy(buf, flashm+offset, n);
792 }
793 
794 #define BUFSIZE 8192
795 
796 void
797 main(int argc, char **argv)
798 {
799 	int k, r, sz, offset = 0;
800 	char *buf, *buf1;
801 	int fd1, fd2;
802 
803 	if (argc != 5) {
804 		fprint(2, "usage: %s flashsize secsize kfsfile flashfile\n", argv[0]);
805 		exits("1");
806 	}
807 	flashsize = strtol(argv[1], nil, 0);
808 	secsize = strtol(argv[2], nil , 0);
809 	fd1 = open(argv[3], OREAD);
810 	fd2 = create(argv[4], OWRITE, 0644);
811 	if (fd1 < 0 || fd2 < 0) {
812 		fprint(2, "bad io files\n");
813 		exits("1");
814 	}
815 	if(secsize == 0 || secsize > flashsize || secsize&(secsize-1) || 0&(secsize-1) || flashsize == 0 || flashsize != Nolimit && flashsize&(secsize-1)) {
816 		fprint(2, "bad sizes\n");
817 		exits("1");
818 	}
819 	for(k=0; k<32 && (1<<k) != secsize; k++)
820 			;
821 	flashm = malloc(flashsize);
822 	buf = malloc(BUFSIZE);
823 	if (flashm == nil) {
824 		fprint(2, "no mem for flash\n");
825 		exits("1");
826 	}
827 	ftls = mkftl("FLASH", 0, Nolimit, k, "format");
828 	for (;;) {
829 		r = read(fd1, buf, BUFSIZE);
830 		if (r <= 0)
831 			break;
832 		if (ftlwrite(buf, r, offset) != r) {
833 			fprint(2, "ftlwrite failed - input file too big\n");
834 			exits("1");
835 		}
836 		offset += r;
837 	}
838 	write(fd2, flashm, flashsize);
839 	close(fd1);
840 	close(fd2);
841 	ftlstat(offset);
842 	/* ftls = mkftl("FLASH", 0, Nolimit, k, "init"); */
843 	sz = offset;
844 	offset = 0;
845 	buf1 = malloc(BUFSIZE);
846 	fd1 = open(argv[3], OREAD);
847 	for (;;) {
848 		r = read(fd1, buf1, BUFSIZE);
849 		if (r <= 0)
850 			break;
851 		if (ftlread(buf, r, offset) != r) {
852 			fprint(2, "ftlread failed\n");
853 			exits("1");
854 		}
855 		if (memcmp(buf, buf1, r) != 0) {
856 			fprint(2, "bad read\n");
857 			exits("1");
858 		}
859 		offset += r;
860 	}
861 	close(fd1);
862 	if (offset != sz) {
863 		fprint(2, "bad final offset\n");
864 		exits("1");
865 	}
866 	exits("0");
867 }
868