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