summaryrefslogtreecommitdiff
path: root/appl/cmd/mpc/qflash.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/mpc/qflash.b')
-rw-r--r--appl/cmd/mpc/qflash.b188
1 files changed, 188 insertions, 0 deletions
diff --git a/appl/cmd/mpc/qflash.b b/appl/cmd/mpc/qflash.b
new file mode 100644
index 00000000..13c77ce8
--- /dev/null
+++ b/appl/cmd/mpc/qflash.b
@@ -0,0 +1,188 @@
+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);
+}