ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/mpc/qflash.b/
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);
}