Author: wmb Date: 2009-11-29 20:58:20 +0100 (Sun, 29 Nov 2009) New Revision: 1525
Added: cpu/x86/pc/olpc/xmodem-reflash.fth Log: Checked in xmodem-reflash.fth . It can be downloaded serially to provide a reflash-over-serial capability.
Added: cpu/x86/pc/olpc/xmodem-reflash.fth =================================================================== --- cpu/x86/pc/olpc/xmodem-reflash.fth (rev 0) +++ cpu/x86/pc/olpc/xmodem-reflash.fth 2009-11-29 19:58:20 UTC (rev 1525) @@ -0,0 +1,470 @@ +\ See license at end of file +purpose: Reflash OLPC Open Firmware using XMODEM serial download for the image + +\ Interface definitions to splice xmodem into Open Firmware + +true value serial-on? +: serial-off ( -- ) + serial-on? if + fallback-in-ih remove-input + fallback-out-ih remove-output + false to serial-on? + then +; +: serial-on ( -- ) + serial-on? 0= if + fallback-in-ih add-input + fallback-out-ih add-output + true to serial-on? + then +; + +\ alias m-key? ukey? +alias m-key ukey +alias m-emit uemit + +: m-init ( -- ) + h# 07 h# 3fa pc! \ Clear and enable FIFOs + h# 80 h# 3fb pc! \ Select baud divisor port + h# 00 h# 3f9 pc! \ High divisor for 115200 baud + h# 01 h# 3f8 pc! \ Low divisor for 115200 baud + h# 3 h# 3fb pc! \ 8n1 +; + +\ : panel-button? ( -- flag ) key? ; +: panel-button? ( -- flag ) false ; \ We don't want to abort +: panel-d. ( n -- ) + #out @ >r + push-decimal + <# u# u# u# ( bs hold bs hold bs hold ) u#> type + pop-base + r> #out ! +; + +: panel-msg: create ", does> count type ; + +" Abort" panel-msg: abrt-msg \ Abrt +" Cancel" panel-msg: can-msg \ CAn +: crc-msg ; \ " Xmodem-CRC" panel-msg: crc-msg \ crc +: done-msg ; \ " Done" panel-msg: done-msg \ donE +" Timeout" panel-msg: timeout-msg \ tout +" "rr " panel-msg: r0-msg \ Start of packet +" "rR " panel-msg: r1-msg \ Start of packet big +" " panel-msg: r2-msg \ Inside try-receive + +" Error" panel-msg: giveup-msg \ Err +" Loading diags " panel-msg: upld-msg \ UPLd +" OF" panel-msg: of-msg \ OF + +: bogus-char ( c -- ) ." Bogus: " .x ; +: ignore-char ( c -- ) ." Ignoring: " .x ; + +: ms>ticks ( ms -- ticks ) ; + +variable timer-init + +: timed-in ( -- true | char false ) \ get a character unless timeout + get-msecs timer-init @ + ( time-limit ) + begin ( time-limit ) + ukey? if drop ukey false exit then ( time-limit ) + dup get-msecs - 0< until + drop true +; + + +\ --- + +\ CRC-16 table +base @ hex +create crc16tab + 0000 w, 1021 w, 2042 w, 3063 w, 4084 w, 50a5 w, 60c6 w, 70e7 w, + 8108 w, 9129 w, a14a w, b16b w, c18c w, d1ad w, e1ce w, f1ef w, + 1231 w, 0210 w, 3273 w, 2252 w, 52b5 w, 4294 w, 72f7 w, 62d6 w, + 9339 w, 8318 w, b37b w, a35a w, d3bd w, c39c w, f3ff w, e3de w, + 2462 w, 3443 w, 0420 w, 1401 w, 64e6 w, 74c7 w, 44a4 w, 5485 w, + a56a w, b54b w, 8528 w, 9509 w, e5ee w, f5cf w, c5ac w, d58d w, + 3653 w, 2672 w, 1611 w, 0630 w, 76d7 w, 66f6 w, 5695 w, 46b4 w, + b75b w, a77a w, 9719 w, 8738 w, f7df w, e7fe w, d79d w, c7bc w, + 48c4 w, 58e5 w, 6886 w, 78a7 w, 0840 w, 1861 w, 2802 w, 3823 w, + c9cc w, d9ed w, e98e w, f9af w, 8948 w, 9969 w, a90a w, b92b w, + 5af5 w, 4ad4 w, 7ab7 w, 6a96 w, 1a71 w, 0a50 w, 3a33 w, 2a12 w, + dbfd w, cbdc w, fbbf w, eb9e w, 9b79 w, 8b58 w, bb3b w, ab1a w, + 6ca6 w, 7c87 w, 4ce4 w, 5cc5 w, 2c22 w, 3c03 w, 0c60 w, 1c41 w, + edae w, fd8f w, cdec w, ddcd w, ad2a w, bd0b w, 8d68 w, 9d49 w, + 7e97 w, 6eb6 w, 5ed5 w, 4ef4 w, 3e13 w, 2e32 w, 1e51 w, 0e70 w, + ff9f w, efbe w, dfdd w, cffc w, bf1b w, af3a w, 9f59 w, 8f78 w, + 9188 w, 81a9 w, b1ca w, a1eb w, d10c w, c12d w, f14e w, e16f w, + 1080 w, 00a1 w, 30c2 w, 20e3 w, 5004 w, 4025 w, 7046 w, 6067 w, + 83b9 w, 9398 w, a3fb w, b3da w, c33d w, d31c w, e37f w, f35e w, + 02b1 w, 1290 w, 22f3 w, 32d2 w, 4235 w, 5214 w, 6277 w, 7256 w, + b5ea w, a5cb w, 95a8 w, 8589 w, f56e w, e54f w, d52c w, c50d w, + 34e2 w, 24c3 w, 14a0 w, 0481 w, 7466 w, 6447 w, 5424 w, 4405 w, + a7db w, b7fa w, 8799 w, 97b8 w, e75f w, f77e w, c71d w, d73c w, + 26d3 w, 36f2 w, 0691 w, 16b0 w, 6657 w, 7676 w, 4615 w, 5634 w, + d94c w, c96d w, f90e w, e92f w, 99c8 w, 89e9 w, b98a w, a9ab w, + 5844 w, 4865 w, 7806 w, 6827 w, 18c0 w, 08e1 w, 3882 w, 28a3 w, + cb7d w, db5c w, eb3f w, fb1e w, 8bf9 w, 9bd8 w, abbb w, bb9a w, + 4a75 w, 5a54 w, 6a37 w, 7a16 w, 0af1 w, 1ad0 w, 2ab3 w, 3a92 w, + fd2e w, ed0f w, dd6c w, cd4d w, bdaa w, ad8b w, 9de8 w, 8dc9 w, + 7c26 w, 6c07 w, 5c64 w, 4c45 w, 3ca2 w, 2c83 w, 1ce0 w, 0cc1 w, + ef1f w, ff3e w, cf5d w, df7c w, af9b w, bfba w, 8fd9 w, 9ff8 w, + 6e17 w, 7e36 w, 4e55 w, 5e74 w, 2e93 w, 3eb2 w, 0ed1 w, 1ef0 w, +base ! + +: updcrc ( crc c -- crc' c ) + dup rot ( c c crc ) + wbsplit >r ( c c low r: high ) + bwjoin ( c low|c r: high ) + crc16tab r> wa+ w@ ( c low|c table-entry ) + xor swap ( crc' c ) +; + +: crc-send ( crc adr len -- crc' ) + bounds ?do i c@ updcrc m-emit loop +; + +\ Assumes 0<len<64K +: crc-receive ( crc adr len timeout -- true | crc' false ) + timer-init ! + bounds ?do timed-in if drop true unloop exit then i c! loop +; + +: checksum-send ( sum adr len -- sum' ) + bounds ?do i c@ dup m-emit + loop +; + + + +\ --- + +purpose: X/YMODEM protocol for serial uploads and downloads + +\ Xmodem protocol file transfer to and from memory +\ Commands: +\ send ( adr len -- ) +\ receive ( adr maxlen -- adr len ) + +\ Interface to the serial line: +\ +\ m-key? -- flag +\ Flag is true if a character is available on the serial line +\ m-key -- char +\ Gets a character from the serial line +\ m-emit char -- +\ Puts the character out on the serial line. + +variable buf-start +variable buf-end +variable mem-start +variable mem-end + +: putc ( char -- ) buf-start @ c! 1 buf-start +! ; + +: end-delay ( -- ) d# 200 ms ; + + +vocabulary modem +only forth also modem also modem definitions +base @ decimal + +\ Common to both sending and receiving +0 value crc? 0 value big? \ 0 value streaming? +variable #control-z's +d# 128 constant 128by +d# 1024 constant 1k +variable sector# +variable checksum +variable #errors 4 constant max#errors variable #naks +: /sector ( -- n ) big? if 1k else 128by then ; + +\ ASCII control characters + 0 constant nul + 1 constant soh \ Start of header; 128-byte packets + 2 constant stx \ Start of header; 1024-byte packets + 4 constant eot + 6 constant ack +d# 21 constant nak +d# 24 constant can + +: timeout! ( ms -- ) ms>ticks timer-init ! ; +: timeout: \ name ( milliseconds -- ) + create , + does> @ timeout! +; +d# 3000 timeout: short-timeout d# 6000 timeout: long-timeout +d# 60,000 timeout: initial-timeout +short-timeout + +: gobble ( -- ) \ eat characters until they stop coming + d# 100 timeout! begin timed-in 0= while drop repeat long-timeout +; + +variable done? +: rx-abort ( -- ) end-delay 2 done? ! ; +: tx-abort ( -- ) end-delay 2 done? ! true abort" aborted" ; + +\ It would be nice to use control C, but some operating systems don't pass it +: ?interrupt ( -- ) \ aborts if user types control Z + panel-button? if can m-emit abrt-msg tx-abort then +; + +\ Receiving + +: receive-setup ( adr maxlen -- ) + 1 sector# ! #naks off #control-z's off +; +: receive-error ( -- ) \ eat rest of packet and send a nak + gobble + 1 #naks +! #naks @ max#errors > if + can m-emit giveup-msg rx-abort + then + nak m-emit +; + +: receive-data ( adr len -- error? ) + 0 -rot bounds ( chk endadr startadr ) + crc? if ( crc endadr startadr ) + ?do timed-in throw updcrc i c! loop ( crc ) + timed-in throw timed-in throw ( crc high low ) + swap bwjoin <> ( error? ) + else ( sum endadr startadr ) + ?do timed-in throw dup i c! + loop ( sum ) + h# ff and timed-in throw <> ( error? ) + then ( error? ) +; +variable got-sector# +: try-receive ( adr maxlen -- adr maxlen actual-len ) + ( packet OK return: none ) + ( retry return: throws -1 ) + ( done return: throws 1 ) + ( abort return: throws 2 ) +\ begin + timed-in throw + case + soh of false to big? r0-msg endof \ expected... + stx of true to big? r1-msg endof \ expected... + -1 of timeout-msg -1 throw endof + nul of 1 throw endof \ XXX check this + can of can-msg 2 throw endof + eot of done-msg ack m-emit 1 throw endof + ( default) bogus-char -1 throw + endcase ( adr maxlen ) +\ again + + /sector < if 2 throw then ( adr ) + timed-in throw ( adr sec# ) + timed-in throw ( adr sec# ~sec# ) + h# ff xor over <> throw ( adr sec# ) + got-sector# ! ( adr ) + /sector receive-data throw ( ) + + ack m-emit + sector# @ panel-d. + 1 sector# +! \ Expected sector# + + #naks off + /sector ( actual ) +; +: !receive-packet ( adr maxlen -- adr maxlen actual-len ) + r2-msg + begin ( adr maxlen ) + 2dup ['] try-receive catch case ( adr maxlen [ actual 0 | x x n ] ) + \ The usual case: successful packet reception + 0 of ( adr maxlen actual-len ) exit endof + + ?interrupt + + \ Retryable error + -1 of ( adr maxlen x x ) 2drop receive-error endof + + \ Handle termination conditions at a higher level + ( default: adr maxlen x x n ) throw + endcase ( adr maxlen ) + again +; +: (receive) ( adr0 maxlen -- adr0 len ) + receive-setup ( adr0 maxlen ) + gobble nak m-emit ( adr0 maxlen ) + 2dup ( adr0 maxlen adr0 maxlen ) + begin dup 0> while ( adr0 maxlen adr remlen ) + ['] !receive-packet catch case + 0 of ( adr0 maxlen adr remlen actual-len ) \ Packet ok + /string ( adr0 maxlen adr' remlen' ) + endof + 1 of ( adr0 maxlen adr remlen ) \ Normal end of transmission + nip - exit ( adr0 len ) + endof + ( default ) can m-emit abrt-msg throw + endcase + repeat ( adr0 maxlen adr remlen ) + can m-emit of-msg end-delay ( adr0 maxlen adr remlen ) + nip - ( adr0 len ) +; + +\ Sending +modem definitions + +: bail-out ( -- ) can m-emit giveup-msg tx-abort ; +: wait-ack ( -- proceed? ) \ wait for ack or can +[ifdef] streaming? \ YMODEM-g + streaming? if + m-key? if m-key can = if bail-out then then + true exit + then +[then] + + #errors off + begin + ?interrupt + timed-in if + 1 #errors +! #errors @ max#errors > if bail-out then + timeout-msg false exit + then + case + ack of #naks off true exit endof + can of can-msg tx-abort endof + nak of + 1 #naks +! #naks @ max#errors > if bail-out then + false exit + endof + + \ If we get a C, restart + [char] C of sector# @ 1 <> if [char] C bogus-char then endof + + ( default) dup bogus-char + endcase + again +; +: start-receiver ( -- ) \ wait for nak + gobble + upld-msg + sector# off + #naks off false to crc? + initial-timeout + begin + timed-in if timeout-msg tx-abort exit then + case + can of can-msg tx-abort endof + nak of true endof + [char] C of true to crc? crc-msg true endof +[ifdef] streaming? + [char] G of true to streaming? true to crc? true endof +[then] + nul of false endof \ Startup transients generate nulls + ( default) dup ignore-char false swap + endcase + until + gobble long-timeout +; + +: pad ( -- b ) control Z sector# @ 0<> and ; +\ Send without confirmation +: send-packet ( adr len big? -- ) + if 1k stx else 128by soh then ( adr len /sec start ) + m-emit ( adr len /sec ) + + \ Sector number + sector# @ dup m-emit h# ff xor m-emit ( adr len /sec ) + + over - 0 2swap ( #pad 0 adr len ) + crc? if ( #pad 0 adr len ) + crc-send ( #pad crc ) + swap 0 ?do pad updcrc m-emit loop ( crc' ) + 0 updcrc updcrc drop ( crc' ) + wbsplit m-emit m-emit ( ) + else ( #pad 0 adr len ) + checksum-send ( #pad sum ) + swap 0 ?do pad dup m-emit + loop ( sum' ) + m-emit ( ) + then ( ) +; + +\ Send until delivery confirmed +: deliver-packet ( adr len big? -- ) + sector# @ panel-d. + begin 3dup send-packet wait-ack until + 3drop +; + +: end-data ( -- ) + begin eot m-emit wait-ack until \ End the protocol + done-msg end-delay +; + +: sx ( adr len -- ) + m-init + start-receiver ( adr len ) + begin dup 0> while ( adr len ) + 1 sector# +! ( adr len ) + 2dup /sector min ( adr len adr /this ) + tuck big? deliver-packet ( adr len /this ) + /string ( adr' len' ) + repeat ( adr len ) + 2drop ( ) + + end-data +; + +\ Info format: +\ <filename>NUL<decimal_size>[[ <decimal_modtime>] <octal_permsissions.]NUL... +: send-file ( adr len name$ -- ) + m-init + start-receiver + here place ( adr len ) + " "(00)" here $cat ( adr len ) + push-decimal dup (.) pop-base ( adr len len$ ) + here $cat here count ( adr len batch$ ) + false deliver-packet ( adr len ) + sx +; + +: sb-end ( -- ) start-receiver " "(00)" false deliver-packet end-delay ; + +: sb ( adr len name$ -- ) send-file sb-end ; + +forth definitions + +alias sx sx +: xmodem-receive ( adr maxlen -- adr len ) + serial-off cursor-off + ['] (receive) catch ( adr maxlen throw-code ) + serial-on cursor-on ( adr maxlen throw-code ) + abort" XMODEM reception aborted" +; + +: xmodem-reflash ( -- ) + ?enough-power + ." Send the firmware image using the XMODEM (Checksum) protocol" cr + flash-buf /flash xmodem-receive nip ( actual-len ) + ?image-valid true to file-loaded? + reflash +; +.( Type 'xmodem-reflash' to begin XMODEM reception) cr + +only forth also definitions + +base ! + +\ LICENSE_BEGIN +\ Copyright (c) 2009 FirmWorks +\ +\ Permission is hereby granted, free of charge, to any person obtaining +\ a copy of this software and associated documentation files (the +\ "Software"), to deal in the Software without restriction, including +\ without limitation the rights to use, copy, modify, merge, publish, +\ distribute, sublicense, and/or sell copies of the Software, and to +\ permit persons to whom the Software is furnished to do so, subject to +\ the following conditions: +\ +\ The above copyright notice and this permission notice shall be +\ included in all copies or substantial portions of the Software. +\ +\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +\ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +\ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +\ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +\ +\ LICENSE_END
openfirmware@openfirmware.info