shithub: purgatorio

ref: 606901dc5da9cb09acb5593c5cf74ce1b52ca6e2
dir: /appl/cmd/mpc/qflash.b/

View raw version
implement Writeflash;

include "sys.m";
	sys: Sys;

include "draw.m";

include "string.m";
	str: String;

Writeflash: module
{
	init:	fn(nil: ref Draw->Context, args: list of string);
};

Region: adt {
	base:	int;
	limit:	int;
};

# could come from file or #F/flash/flashctl
FLASHSEG: con 256*1024;
kernelregion := Region(FLASHSEG, FLASHSEG+2*FLASHSEG);
bootregion := Region(0, FLASHSEG);

stderr: ref Sys->FD;
prog := "qflash";
damaged := 0;

usage()
{
	sys->fprint(stderr, "Usage: %s [-b] [-o offset] [-f flashdev] file\n", prog);
	exit;
}

err(s: string)
{
	sys->fprint(stderr, "%s: %s", prog, s);
	if(!damaged)
		sys->fprint(stderr, "; flash not modified\n");
	else
		sys->fprint(stderr, "; flash might now be invalid\n");
	exit;
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;
	sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);
	stderr = sys->fildes(2);
	if(args != nil){
		prog = hd args;
		args = tl args;
	}
	str = load String String->PATH;
	if(str == nil)
		err(sys->sprint("can't load %s: %r", String->PATH));
	region := kernelregion;
	flash := "#F/flash/flash";
	offset := 0;
	save := 0;
	
	for(; args != nil && (hd args)[0] == '-'; args = tl args)
		case hd args {
		"-b" =>
			region = bootregion;
			offset = 16r100 - 8*4;	# size of exec header
			save = 1;
		"-h" =>
			region.limit += FLASHSEG;
		"-f" =>
			if(tl args == nil)
				usage();
			flash = hd args;
			args = tl args;
		"-o" =>
			if(tl args == nil)
				usage();
			args = tl args;
			s := hd args;
			v: int;
			rs: string;
			if(str->prefix("16r", s))
				(v, rs) = str->toint(s[3:], 16);
			else if(str->prefix("0x", s))
				(v, rs) = str->toint(s[2:], 16);
			else if(str->prefix("0", s))
				(v, rs) = str->toint(s[1:], 8);
			else
				(v, rs) = str->toint(s, 10);
			if(v < 0 || len rs != 0)
				err(sys->sprint("bad offset: %s", s));
			offset = v;
		"-s" =>
			save = 1;
		* =>
			usage();
		}
	if(args == nil)
		usage();
	fname := hd args;
	fd := sys->open(fname, Sys->OREAD);
	if(fd == nil)
		err(sys->sprint("can't open %s: %r", fname));
	(r, dir) := sys->fstat(fd);
	if(r < 0)
		err(sys->sprint("can't stat %s: %r", fname));
	length := int dir.length;
	avail := region.limit - (region.base+offset);
	if(length > avail)
		err(sys->sprint("%s contents %ud bytes, exceeds flash region %ud bytes", fname, length, avail));
	# check fname's contents...
	where := region.base+offset;
	saved: list of (int, array of byte);
	if(save){
		saved = saveflash(flash, region.base, where) :: saved;
		saved = saveflash(flash, where+length, region.limit) :: saved;
	}
	for(i := (region.base+offset)/FLASHSEG; i < region.limit/FLASHSEG; i++)
		erase(flash, i);
	out := sys->open(flash, Sys->OWRITE);
	if(out == nil)
		err(sys->sprint("can't open %s for writing: %r", flash));
	if(sys->seek(out, big where, 0) != big where)
		err(sys->sprint("can't seek to #%6.6ux on flash: %r", where));
	if(length)
		sys->print("writing %ud bytes to %s at #%6.6ux\n", length, flash, where);
	buf := array[Sys->ATOMICIO] of byte;
	total := 0;
	while((n := sys->read(fd, buf, len buf)) > 0) {
		if(total+n > avail)
			err(sys->sprint("file %s too big for region of %ud bytes", fname, avail));
		r = sys->write(out, buf, n);
		damaged = 1;
		if(r != n){
			if(r < 0)
				err(sys->sprint("error writing %s at byte %ud: %r", flash, total));
			else
				err(sys->sprint("short write on %s at byte %ud", flash, total));
		}
		total += n;
	}
	if(n < 0)
		err(sys->sprint("error reading %s: %r", fname));
	sys->print("wrote %ud bytes from %s to flash %s (#%6.6ux-#%6.6ux)\n", total, fname, flash, region.base, region.base+total);
	for(l := saved; l != nil; l = tl l){
		(addr, data) := hd l;
		n = len data;
		if(n == 0)
			continue;
		sys->print("restoring %ud bytes at #%6.6ux\n", n, addr);
		if(sys->seek(out, big addr, 0) != big addr)
			err(sys->sprint("can't seek to #%6.6ux on %s: %r", addr, flash));
		r = sys->write(out, data, n);
		if(r < 0)
			err(sys->sprint("error writing %s: %r", flash));
		else if(r != n)
			err(sys->sprint("short write on %s at byte %ud/%ud", flash, r, n));
		else
			sys->print("restored %ud bytes at #%6.6ux\n", n, addr);
	}
}

erase(flash: string, seg: int)
{
	ctl := sys->open(flash+"ctl", Sys->OWRITE);
	if(ctl == nil)
		err(sys->sprint("can't open %sctl: %r\n", flash));
	if(sys->fprint(ctl, "erase %ud", seg*FLASHSEG) < 0)
		err(sys->sprint("can't erase flash %s segment %d: %r\n", flash, seg));
}

saveflash(flash: string, base: int, limit: int): (int, array of byte)
{
	fd := sys->open(flash, Sys->OREAD);
	if(fd == nil)
		err(sys->sprint("can't open %s for reading: %r", flash));
	nb := limit - base;
	if(nb <= 0)
		return (base, nil);
	if(sys->seek(fd, big base, 0) != big base)
		err(sys->sprint("can't seek to #%6.6ux to save flash contents: %r", base));
	saved := array[nb] of byte;
	if(sys->read(fd, saved, len saved) != len saved)
		err(sys->sprint("can't read flash #%6.6ux to #%6.6ux: %r", base, limit));
	sys->print("saved %ud bytes at #%6.6ux\n", len saved, base);
	return (base, saved);
}