Files

256 lines
6.2 KiB
NASM

; tftpr.asm -- tftpc.asm retargeted for the real unit at 100.103.100.103.
; Server: 100.103.255.254:69; RRQ name: "xtext.coff".
;
; tftpc.asm -- minimal TFTP read client built on the firmware's socket API.
;
; The firmware's own tftp_open() calls a resolver that does not return when
; called from the rdebug process (dwt/notes/TCPIP.md), so this payload speaks
; RFC 783 directly: RRQ, DATA/ACK, 512-byte blocks, one file.
;
; socket = 3F5C9A00h socket(2,2,0) -> fd
; sendto = 3F5D3D70h sendto(fd, buf, len, flags, to, tolen) -> n
; recvfrom = 3F5D7D50h recvfrom(fd, buf, len, flags, from, fromlen) -> n
; close = 3F5E8930h close(fd)
; malloc = 3F701230h malloc(bytes) -> bit pointer
;
; The transfer is 600 bytes, so DATA 1 is 512 (full) and DATA 2 is 88
; (short); the short block ends the transfer. ACKs go to the sockaddr that
; recvfrom reports, not to a hard-coded port.
;
; Evidence cells (32-bit, bit addresses):
; 0187C000 stage (1 malloc, 2 socket, 3 receiving, 4 closed, 0FFh failed)
; 0187C020 fd
; 0187C040 destination buffer (malloc)
; 0187C060 total bytes fetched
; 0187C080 last recvfrom length
; 0187C0A0 last DATA block word as read from the wire
; 0187C0C0 marker 05A5A5A5h when the fetch completed
; 0187C200 entry SP
; 0187C220 entry A14
;
; Scratch (fixed bit addresses, above this image):
; 01875000 argument area
; 01876000 receive buffer (2048 bytes)
; 0187A000 recvfrom source sockaddr
; 0187A080 recvfrom sockaddr length
; 0187A100 ACK packet (4 bytes)
.text
_start:
setf 16, 0, 0
setf 32, 0, 1
mmtm sp, a0,a1,a2,a3,a4,a5,a6,a7
move sp, @0187C200h, 1
move a14, @0187C220h, 1
movi 1, a0, L
move a0, @0187C000h, 1
; ---- buf = malloc(010000h) ------------------------------------
move a14, -*sp, 1
movi 01875000h, a14, L
movi 010000h, a0, L
move a0, *a14+, 1
calla 3F701230h
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @0187C040h, 1
move a8, a11 ; dest cursor (callee-saved)
move a8, a8
jreq fail
movi 2, a0, L
move a0, @0187C000h, 1
; ---- fd = socket(2,2,0) --------------------------------------
movi 01875000h, a14, L
movi 0, a0, L
move a0, *a14+, 1
movi 2, a0, L
move a0, *a14+, 1
movi 2, a0, L
move a0, *a14+, 1
calla 3F5C9A00h
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @0187C020h, 1
move a8, a9 ; fd (callee-saved)
move a8, a8
jrlt fail
; ---- sendto(fd, RRQ, 28, 0, SADDR, 16) ------------------------
movi 01875000h, a14, L
movi 16, a0, L
move a0, *a14+, 1
movi SADDR, a0, L
move a0, *a14+, 1
movi 0, a0, L
move a0, *a14+, 1
movi 20, a0, L
move a0, *a14+, 1
movi RRQ, a0, L
move a0, *a14+, 1
move a9, *a14+, 1
calla 3F5D3D70h
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, a8
jrlt fail
movi 3, a0, L
move a0, @0187C000h, 1
sub a0, a0
move a0, @0187C060h, 1 ; total = 0
recv_loop:
; ---- recvfrom(fd, BUF, 2048, 0, &FROM, &FROMLEN) -------------
; arg5 is a POINTER to the socklen (as tftp_read passes it); the
; value is initialised to 16 first.
movi 16, a0, L
move a0, @0187A080h, 1 ; fromlen = 16
movi 01875000h, a14, L
movi 0187A080h, a0, L
move a0, *a14+, 1 ; arg5 = &fromlen
movi 0187A000h, a0, L
move a0, *a14+, 1 ; arg4 = &from
movi 0, a0, L
move a0, *a14+, 1 ; arg3 = flags
movi 00800h, a0, L
move a0, *a14+, 1 ; arg2 = 2048
movi 01876000h, a0, L
move a0, *a14+, 1 ; arg1 = receive buffer
move a9, *a14+, 1 ; arg0 = fd
calla 3F5D7D50h
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, @0187C080h, 1 ; last length
move a8, a8
jrle fail
; opcode: wire 00 03 -> little-endian word 0300h
move @01876000h, a0, 0
cmpi 0300h, a0
jrne fail ; only DATA is accepted
move @01876010h, a4, 0 ; block number, wire order
move a4, @0187C0A0h, 1
move a8, a3
subk 4, a3 ; payload length
; ---- total += payload, dest += payload -----------------------
move @0187C060h, a1, 1
add a3, a1
move a1, @0187C060h, 1 ; total += payload (bytes)
move a11, a6 ; copy destination
move a3, a2
sll 3, a2 ; bytes -> bits: pointers are bit addresses
add a2, a11 ; dest cursor advances now (do NOT add bytes)
; ---- copy payload from BUF+4 to a6, one byte at a time ------
; Interrupts are disabled for the copy so a 512-byte copy is atomic
; with respect to the firmware's ISRs.
dint
movi 01876020h, a5, L
move a3, a2
move a3, @0187C0E0h, 1 ; payload length
move a2, @0187C140h, 1 ; copy count
move a6, @0187C120h, 1 ; write start
move a11, @0187C100h, 1 ; cursor after advance
move a5, @0187C160h, 1 ; source
jreq copy_done
copy_bytes:
movb *a5, a0
movb a0, *a6
addk 8, a5 ; one byte
addk 8, a6
dsjs a2, copy_bytes
copy_done:
eint
; ---- ACK: 00 04 <block as received> --------------------------
movi 00400h, a0, L
move a0, @0187A100h, 0
move a4, @0187A110h, 0
movi 01875000h, a14, L
movi 16, a0, L
move a0, *a14+, 1
movi 0187A000h, a0, L
move a0, *a14+, 1
movi 0, a0, L
move a0, *a14+, 1
movi 4, a0, L
move a0, *a14+, 1
movi 0187A100h, a0, L
move a0, *a14+, 1
move a9, *a14+, 1
calla 3F5D3D70h
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
move a8, a8
jrlt fail
; short block ends the transfer; full block means read again
move @0187C080h, a2, 1
subk 4, a2
cmpi 512, a2
jrlt close_it
jruc recv_loop
close_it:
movi 4, a0, L
move a0, @0187C000h, 1
movi 01875000h, a14, L
move a9, *a14+, 1
calla 3F5E8930h ; close(fd)
move @0187C200h, sp, 1
move @0187C220h, a14, 1
setf 16, 0, 0
setf 32, 0, 1
movi 05A5A5A5h, a0, L
move a0, @0187C0C0h, 1
sub a8, a8
jruc epilogue
fail:
movi 0FFh, a0, L
move a0, @0187C000h, 1
movi 0FFFFFFFFh, a8, L
epilogue:
mmfm sp, a0,a1,a2,a3,a4,a5,a6,a7
rets
RRQ:
.word 00100h ; opcode 1 (00 01)
.word 07478h ; "xt"
.word 07865h ; "ex"
.word 02E74h ; "t."
.word 06F63h ; "co"
.word 06666h ; "ff"
.word 06F00h ; "\0o" (NUL ends the name, mode starts)
.word 07463h ; "ct"
.word 07465h ; "et"
.word 00000h ; "\0" + pad
SADDR:
.word 00002h ; sin_family = AF_INET
.word 04500h ; sin_port = htons(69)
.word 06764h ; 100.103
.word 0FEFFh ; .255.254
.word 0
.word 0
.word 0
.word 0
.end