forsyth20121205-174063a80cc13 years agoHistory
911 lines · 19.7 KB · Brainfuck
RawDownload
## basic Flash Translation Layer driver#	see for instance the Intel technical paper#	``Understanding the Flash Translation Layer (FTL) Specification''#	Order number 297816-001 (online at www.intel.com)## a public driver by David Hinds, dhinds@allegro.stanford.edu# further helps with some details.## this driver uses the common simplification of never storing# the VBM on the medium (a waste of precious flash!) but# rather building it on the fly as the block maps are read.## Plan 9 driver (c) 1997 by C H Forsyth (forsyth@caldo.demon.co.uk)#	This driver may be used or adapted by anyone for any non-commercial purpose.## adapted for Inferno 1998 by C H Forsyth, Vita Nuova Limited, York, England (byteles@vitanuova.com)## C H Forsyth and Vita Nuova Limited expressly allow Lucent Technologies# to use this driver freely for any Inferno-related purposes whatever,# including commercial applications.## TO DO:#	check error handling details for get/put flash#	bad block handling#	reserved space in formatted size#	possibly block size as parameter#	fetch parameters from header on init## Adapted to a ftl formatter for Inferno 2000 by J R Firth, Vita Nuova Limited#	usage : ftl flashsize secsize inputfile outputfile# outputfile will then be a ftl image of inputfile# nb assumes the base address is zero## Converted to limbo for Inferno 2000 by JR Firth, Vita Nuova Holdings Limited# implement Ftlimage; include "sys.m";include "draw.m"; sys : Sys;	OREAD, OWRITE, FD, open, create, read, write, print, fprint : import sys; Ftlimage : module{	init : fn(nil : ref Draw->Context, argv : list of string);}; stderr : ref FD; flashsize, secsize : int;flashm : array of byte;trace : int = 0; Eshift : con 18;			# 2^18=256k; log2(eraseunit)Flashseg : con 1<<Eshift;Bshift : con 9;			# 2^9=512Bsize : con 1<<Bshift;BAMoffset : con 16r100;Nolimit : con ~0;USABLEPCT : con 95;	# release only this % to client FTLDEBUG : con 0; # erase unit header (defined by FTL specification)# offsets into MeraseO_LINKTUPLE : con 0;O_ORGTUPLE : con 5;O_NXFER : con 15;O_NERASE : con 16;O_ID : con 20;O_BSHIFT : con 22;O_ESHIFT : con 23;O_PSTART : con 24;O_NUNITS : con 26;O_PSIZE : con 28;O_VBMBASE : con 32;O_NVBM : con 36;O_FLAGS : con 38;O_CODE : con 39;O_SERIAL : con 40;O_ALTOFFSET : con 44;O_BAMOFFSET : con 48;O_RSV2 : con 52; ERASEHDRLEN : con	64; # special unit IDsXferID : con 16rffff;XferBusy : con 16r7fff; # special BAM addressesBfree : con -1;	#16rffffffffBwriting : con -2; #16rfffffffeBdeleted : con 0; # block typesTypeShift : con 7;BlockType : con (1<<TypeShift)-1;ControlBlock : con 16r30;DataBlock : con 16r40;ReplacePage : con 16r60;BadBlock : con 16r70; BNO(va : int) : int{	return va>>Bshift;}MKBAM(b : int,t : int) : int{	return (b<<Bshift)|t;} Terase : adt {	x : int;	id : int;	offset : int;	bamoffset : int;	nbam : int;	bam : array of byte;	bamx : int;	nfree : int;	nused : int;	ndead : int;	nbad : int;	nerase : int;}; Ftl : adt {	base : int;		# base of flash region 	size : int;		# size of flash region 	segsize : int;	# size of flash segment (erase unit) 	eshift : int;	# log2(erase-unit-size) 	bshift : int;	# log2(bsize) 	bsize : int;	nunit : int;		# number of segments (erase units) 	unit : array of ref Terase;	lastx : int;		# index in unit of last allocation 	xfer : int;		# index in unit of current transfer unit (-1 if none) 	nfree : int;		# total free space in blocks 	nblock : int;	# total space in blocks 	rwlimit : int;	# user-visible block limit (`formatted size') 	vbm : array of int;		# virtual block map	fstart : int;		# address of first block of data in a segment 	trace : int;		# (debugging) trace of read/write actions 	detach : int;	# free Ftl on last close  	# scavenging variables 	needspace : int;	hasproc : int;}; # Ftl.detach Detached : con 1;	# detach on close Deferred : con 2;	# scavenger must free it  ftls : ref Ftl; ftlstat(sz : int){	print("16r%x:16r%x:16r%x\n", ftls.rwlimit*Bsize, sz, flashsize);	print("%d:%d:%d in 512b blocks\n", ftls.rwlimit, sz>>Bshift, flashsize>>Bshift);}	 ftlread(buf : array of byte, n : int, offset : int) : int{	ftl : ref Ftl;	e : ref Terase;	nb : int;	a : int;	pb : int;	mapb : int; 	if(n <= 0 || n%Bsize || offset%Bsize) {		fprint(stderr, "ftl: bad read\n");		exit;	}	ftl = ftls;	nb = n/Bsize;	offset /= Bsize;	if(offset >= ftl.rwlimit)		return 0;	if(offset+nb > ftl.rwlimit)		nb = ftl.rwlimit - offset;	a = 0;	for(n = 0; n < nb; n++){		(mapb, e, pb) = mapblk(ftl, offset+n);		if(mapb)			getflash(ftl, buf[a:], e.offset + pb*Bsize, Bsize);		else			memset(buf[a:], 0, Bsize);		a += Bsize;	}	return a;} ftlwrite(buf : array of byte, n : int, offset : int) : int{	ns, nb : int;	a : int;	e, oe : ref Terase;	ob, v : int;	ftl : ref Ftl;	mapb : int; 	if(n <= 0)		return 0;	ftl = ftls;	if(n <= 0 || n%Bsize || offset%Bsize) {		fprint(stderr, "ftl: bad write\n");		exit;	}	nb = n/Bsize;	offset /= Bsize;	if(offset >= ftl.rwlimit)		return 0;	if(offset+nb > ftl.rwlimit)		nb = ftl.rwlimit - offset;	a = 0;	for(n = 0; n < nb; n++){		ns = 0;		while((v = allocblk(ftl)) == 0)			if(!scavenge(ftl) || ++ns > 3){				fprint(stderr, "ftl: flash memory full\n");			}		(mapb, oe, ob) = mapblk(ftl, offset+n);		if(!mapb)			oe = nil;		e = ftl.unit[v>>16];		v &= 16rffff;		putflash(ftl, e.offset + v*Bsize, buf[a:], Bsize);		putbam(ftl, e, v, MKBAM(offset+n, DataBlock));		# both old and new block references exist in this window (can't be closed?) 		ftl.vbm[offset+n] = (e.x<<16) | v;		if(oe != nil){			putbam(ftl, oe, ob, Bdeleted);			oe.ndead++;		}		a += Bsize;	}	return a;} mkftl(fname : string, base : int, size : int, eshift : int, op : string) : ref Ftl{	i, j, nov, segblocks : int;	limit : int;	e : ref Terase; 	ftl := ref Ftl;	ftl.lastx = 0;	ftl.detach = 0;	ftl.needspace = 0;	ftl.hasproc = 0;	ftl.trace = 0;	limit = flashsize;	if(size == Nolimit)		size = limit-base;	if(base >= limit || size > limit || base+size > limit || eshift < 8 || (1<<eshift) > size) {		fprint(stderr, "bad flash space parameters");		exit;	}	if(FTLDEBUG || ftl.trace || trace)		print("%s flash %s #%x:#%x limit #%x\n", op, fname, base, size, limit);	ftl.base = base;	ftl.size = size;	ftl.bshift = Bshift;	ftl.bsize = Bsize;	ftl.eshift = eshift;	ftl.segsize = 1<<eshift;	ftl.nunit = size>>eshift;	nov = ((ftl.segsize/Bsize)*4 + BAMoffset + Bsize - 1)/Bsize;	# number of overhead blocks per segment (header, and BAM itself) 	ftl.fstart = nov;	segblocks = ftl.segsize/Bsize - nov;	ftl.nblock = ftl.nunit*segblocks;	if(ftl.nblock >= 16r10000)		ftl.nblock = 16r10000;	ftl.vbm = array[ftl.nblock] of int; 	ftl.unit = array[ftl.nunit] of ref Terase;	if(ftl.vbm == nil || ftl.unit == nil) {		fprint(stderr, "out of mem");		exit;	}	for(i=0; i<ftl.nblock; i++)		ftl.vbm[i] = 0;	if(op == "format"){		for(i=0; i<ftl.nunit-1; i++)			eraseinit(ftl, i*ftl.segsize, i, 1);		eraseinit(ftl, i*ftl.segsize, XferID, 1);	}	ftl.xfer = -1;	for(i=0; i<ftl.nunit; i++){		e = eraseload(ftl, i, i*ftl.segsize);		if(e == nil){			fprint(stderr, "ftl: logical segment %d: bad format\n", i);			continue;		}		if(e.id == XferBusy){			e.nerase++;			eraseinit(ftl, e.offset, XferID, e.nerase);			e.id = XferID;		}		for(j=0; j<ftl.nunit; j++)			if(ftl.unit[j] != nil && ftl.unit[j].id == e.id){				fprint(stderr, "ftl: duplicate erase unit #%x\n", e.id);				erasefree(e);				e = nil;				break;			}		if(e != nil){			ftl.unit[e.x] = e;			if(e.id == XferID)				ftl.xfer = e.x;			if (FTLDEBUG || ftl.trace || trace)				fprint(stderr, "ftl: unit %d:#%x used %d free %d dead %d bad %d nerase %d\n",					e.x, e.id, e.nused, e.nfree, e.ndead, e.nbad, e.nerase);		}	}	if(ftl.xfer < 0 && ftl.nunit <= 0 || ftl.xfer >= 0 && ftl.nunit <= 1) {		fprint(stderr, "ftl: no valid flash data units");		exit;	}	if(ftl.xfer < 0)		fprint(stderr, "ftl: no transfer unit: device is WORM\n");	else		ftl.nblock -= segblocks;	# discount transfer segment 	if(ftl.nblock >= 1000)		ftl.rwlimit = ftl.nblock-100;	# TO DO: variable reserve 	else		ftl.rwlimit = ftl.nblock*USABLEPCT/100;	return ftl;} ftlfree(ftl : ref Ftl){	if(ftl != nil){		ftl.unit = nil;		ftl.vbm = nil;		ftl = nil;	}} ## this simple greedy algorithm weighted by nerase does seem to lead# to even wear of erase units (cf. the eNVy file system)# bestcopy(ftl : ref Ftl) : ref Terase{	e, be : ref Terase;	i : int; 	be = nil;	for(i=0; i<ftl.nunit; i++)		if((e = ftl.unit[i]) != nil && e.id != XferID && e.id != XferBusy && e.ndead+e.nbad &&		    (be == nil || e.nerase <= be.nerase && e.ndead >= be.ndead))			be = e;	return be;} copyunit(ftl : ref Ftl, from : ref Terase, too : ref Terase) : int{	i, nb : int;	id := array[2] of byte;	bam : array of byte;	buf : array of byte;	v, bno : int; 	if(FTLDEBUG || ftl.trace || trace)		print("ftl: copying %d (#%x) to #%x\n", from.id, from.offset, too.offset);	too.nbam = 0;	too.bam = nil;	bam = nil;	buf = array[Bsize] of byte;	if(buf == nil)		return 0;	PUT2(id, XferBusy);	putflash(ftl, too.offset+O_ID, id, 2);	# make new BAM 	nb = from.nbam*4;	bam = array[nb] of byte;	memmove(bam, from.bam, nb);	too.nused = 0;	too.nbad = 0;	too.nfree = 0;	too.ndead = 0;	for(i = 0; i < from.nbam; i++)		bv := GET4(bam[4*i:]);		case(bv){		Bwriting or		Bdeleted or		Bfree =>			PUT4(bam[4*i:], Bfree);			too.nfree++;			break;		* =>			case(bv&BlockType){			DataBlock or			ReplacePage =>				v = bv;				bno = BNO(v & ~BlockType);				if(i < ftl.fstart || bno >= ftl.nblock){					print("ftl: unit %d:#%x bad bam[%d]=#%x\n", from.x, from.id, i, v);					too.nfree++;					PUT4(bam[4*i:], Bfree);					break;				}				getflash(ftl, buf, from.offset+i*Bsize, Bsize);				putflash(ftl, too.offset+i*Bsize, buf, Bsize);				too.nused++;				break;			ControlBlock =>				too.nused++;				break;			* =>				# case BadBlock:	# it isn't necessarily bad in this unit 				too.nfree++;				PUT4(bam[4*i:], Bfree);				break;			}		}	# for(i=0; i<from.nbam; i++){	#	v = GET4(bam[4*i:]);	#	if(v != Bfree && ftl.trace > 1)	#		print("to[%d]=#%x\n", i, v);	#	PUT4(bam[4*i:], v);	# }	putflash(ftl, too.bamoffset, bam, nb);	# BUG: PUT4 ? IS IT ?	# for(i=0; i<from.nbam; i++){	#	v = GET4(bam[4*i:]);	#	PUT4(bam[4*i:], v);	# }	too.id = from.id;	PUT2(id, too.id);	putflash(ftl, too.offset+O_ID, id, 2);	too.nbam = from.nbam;	too.bam = bam;	ftl.nfree += too.nfree - from.nfree;	buf = nil;	return 1;} mustscavenge(a : ref Ftl) : int{	return a.needspace || a.detach == Deferred;} donescavenge(a : ref Ftl) : int{	return a.needspace == 0;} scavengeproc(arg : ref Ftl){	ftl : ref Ftl;	i : int;	e, ne : ref Terase; 	ftl = arg;	if(mustscavenge(ftl)){		if(ftl.detach == Deferred){			ftlfree(ftl);			fprint(stderr, "scavenge out of memory\n");			exit;		}		if(FTLDEBUG || ftl.trace || trace)			print("ftl: scavenge %d\n", ftl.nfree);		e = bestcopy(ftl);		if(e == nil || ftl.xfer < 0 || (ne = ftl.unit[ftl.xfer]) == nil || ne.id != XferID || e == ne)			;		else if(copyunit(ftl, e, ne)){			i = ne.x; ne.x = e.x; e.x = i;			ftl.unit[ne.x] = ne;			ftl.unit[e.x] = e;			ftl.xfer = e.x;			e.id = XferID;			e.nbam = 0;			e.bam = nil;			e.bamx = 0;			e.nerase++;			eraseinit(ftl, e.offset, XferID, e.nerase);		}		if(FTLDEBUG || ftl.trace || trace)			print("ftl: end scavenge %d\n", ftl.nfree);		ftl.needspace = 0;	}} scavenge(ftl : ref Ftl) : int{	if(ftl.xfer < 0 || bestcopy(ftl) == nil)		return 0;	# you worm!  	if(!ftl.hasproc){		ftl.hasproc = 1;	}	ftl.needspace = 1; 

Lines 1–500 of 911.