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
ftlstat(int sz)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
ftlread(void * buf,long n,ulong offset)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 /* not reached */
213 }
214
215 static long
ftlwrite(void * buf,long n,ulong offset)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 /* not reached */
260 }
261
262 static Ftl *
mkftl(char * fname,ulong base,ulong size,int eshift,char * op)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
ftlfree(Ftl * ftl)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 *
bestcopy(Ftl * ftl)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
copyunit(Ftl * ftl,Terase * from,Terase * to)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
mustscavenge(void * a)475 mustscavenge(void *a)
476 {
477 return ((Ftl*)a)->needspace || ((Ftl*)a)->detach == Deferred;
478 }
479
480 static int
donescavenge(void * a)481 donescavenge(void *a)
482 {
483 return ((Ftl*)a)->needspace == 0;
484 }
485
486 static void
scavengeproc(void * arg)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
scavenge(Ftl * ftl)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
putbam(Ftl * ftl,Terase * e,int n,ulong entry)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
allocblk(Ftl * ftl)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
mapblk(Ftl * ftl,ulong bno,Terase ** ep,ulong * bp)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
eraseinit(Ftl * ftl,ulong offset,int id,int nerase)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 *
eraseload(Ftl * ftl,int x,ulong offset)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
erasefree(Terase * e)761 erasefree(Terase *e)
762 {
763 free(e->bam);
764 free(e);
765 }
766
767 static void
eraseflash(Ftl * ftl,ulong offset)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
putflash(Ftl * ftl,ulong offset,void * buf,long n)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
getflash(Ftl * ftl,void * buf,ulong offset,long n)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
main(int argc,char ** argv)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