openwrt/NeufBoxV4/flashimage.py

327 lines
9.2 KiB
Python

#!/usr/bin/env python
# may 2009 (c) avd <avd@patatrac.info>
"""
flashimage for nb4
Easily flash an nb4 with an full image of 8MB
and a network connection.
Usage:
./flashimage.py <network interface> <full image>
The nb4 need to be in download mode (press service button
approximately 5 seconds when box booting until it is
blinking blue).
This program uses the download mode of the CFE soft
for the NB4 box. This program send request for flashing
all the flash AFTER the CFE part.
Obviously, you can use it without risking to destroy your
nb4 box because there will be the CFE to recover.
this program remove all the things after CFE. Think to save your config before use it.
This program expect a full image of 8MB (CFE + MAIN + JFFS2 + RESCUE + DSL + NV) but the CFE soft on the nb4 in download mode and without EALL request jump the first 64KB of the image (CFE part) thus does not write the CFE part on the flash.
So, if you don't have the CFE part and you want build a full image, you can remplace the 64KB of the beginning of the full image by any data.
"""
import sys
import string
import struct
import socket
import time
import os
# defs
ETH_ADDR_BROADCAST = '\xff\xff\xff\xff\xff\xff'
CMD_VERSION = 0x0000
CMD_REQUEST = 0x0001
CMD_DATA = 0x0002
CMD_RESET = 0x0003
CMD_VERIFY = 0x0004
# based on http://dev.efixo.net/browser/trunk/openwrt/target/linux/brcm63xx/files-2.6.21/include/asm-mips/mach-bcm63xx/nb4/box/partition.h
# mapping for 2.x (not used for now)
NB_CFE_OFFSET = 0x0
NB_CFE_SIZE = 65536
NB_MAIN_OFFSET = NB_CFE_OFFSET + NB_CFE_SIZE
NB_MAIN_SIZE = 5570560
NB_JFFS2_OFFSET = NB_MAIN_OFFSET + NB_MAIN_SIZE
NB_JFFS2_SIZE = 655360
NB_RESCUE_OFFSET = NB_JFFS2_OFFSET + NB_JFFS2_SIZE
NB_RESCUE_SIZE = 1572864
NB_DSL_OFFSET = NB_RESCUE_OFFSET + NB_RESCUE_SIZE
NB_DSL_SIZE = 458752
NB_NV_OFFSET = NB_DSL_OFFSET + NB_DSL_SIZE
NB_NV_SIZE = 65535
NB_TOTAL_SIZE = 8388608
counter_wsequence = 0x2300
class Dlcpkt:
'''dlc packet - stock values in network order'''
dstaddr = None
srcaddr = None
sap = None
wcmd = None
wsequence = None
woffset = None
wsegment = None
wlen = None
bdata = None
hdr_len = 24
data_len = 24
def __init__(self):
self.bzero()
def __str__(self):
return self.dstaddr + self.srcaddr + self.sap + self.wcmd + self.wsequence + self.woffset + self.wsegment + self.wlen + self.bdata
def bzero(self):
self.dstaddr = '\x00\x00\x00\x00\x00\x00'
self.srcaddr = '\x00\x00\x00\x00\x00\x00'
self.sap = '\x88\x88'
self.wcmd = '\x00\x00'
self.wsequence = '\x00\x00'
self.woffset = '\x00\x00'
self.wsegment = '\x00\x00'
self.wlen = '\x00\x00'
self.bdata = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
def pack(self):
return self.str()
def unpack(self, data):
self.dstaddr = data[:6]
self.srcaddr = data[6:12]
self.sap = data[12:14]
self.wcmd = data[14:16]
self.wsequence = data[16:18]
self.woffset = data[18:20]
self.wsegment = data[20:22]
self.wlen = data[22:24]
self.bdata = data[24:]
def eth_ntoa(raw):
# Convert binary data into a string.
return ":".join(["%02X" % (ord(ch),) for ch in raw])
def eth_aton(buffer):
addr =''
temp = string.split(buffer,':')
buffer = string.join(temp,'')
# Split up the hex values and pack.
for i in range(0, len(buffer), 2):
addr = ''.join([addr,struct.pack('>B', int(buffer[i: i + 2], 16))],)
return addr
def i16ton(i16):
''' Convert 16 bit integer to network '''
raw = struct.pack('H', i16)
return raw
def hex2dec(hex):
return int(hex, 16)
def dec2hex(dec):
return "%X" % n
def request(s, dlcpkt):
s.send(str(dlcpkt))
resp = s.recv(48);
return resp
def send_file(s, dlcpkt_w, file):
global counter_wsequence
progress_list = [ '|', '/', '-', '\\' ]
progress_index = 0
wsegment_h = 0x0000
woffset_h = 0x0000
counter = 1
dlcpkt_r = Dlcpkt()
dlcpkt_w.wcmd = i16ton(CMD_DATA)
buf_len = 0x0200
filesize = os.path.getsize(file)
f = open(file, "r")
print ' > send %s (size=%d)' % (file, filesize)
print ' (please wait while the box erasing the flash from 0x00010000 to 0x007fffff before flashing ...)'
block = f.read(buf_len)
while block != '':
dlcpkt_w.bdata = block
dlcpkt_w.wsequence = i16ton(counter_wsequence)
dlcpkt_w.wlen = i16ton(len(block))
dlcpkt_w.wsegment = i16ton(wsegment_h)
dlcpkt_w.woffset = i16ton(woffset_h)
s.send(str(dlcpkt_w))
# check
resp = s.recv(48);
dlcpkt_r.unpack(resp)
if dlcpkt_r.wsequence != dlcpkt_w.wsequence:
print 'FAILED.'
break
else:
sys.stdout.write('\r' + progress_list[progress_index] + " %02.f%%" % ( ((counter * len(block)) / (filesize * 1.0)) * 100 ))
sys.stdout.flush()
progress_index = (progress_index + 1) % len(progress_list)
### INCR ###
# final address = segment<<4 + offset
wsegment_h = (wsegment_h + (len(block)/0x10)) % (0x10000)
woffset_h = (wsegment_h & 0x000f)
counter_wsequence = (counter_wsequence + 1) % (0x10000)
counter = counter + 1
# new read
block = f.read(buf_len)
# don't be too speedy
time.sleep(0.002)
print ' '
f.close()
###### MAIN ######
if len(sys.argv) < 3:
print 'Usage: %s <network interface> <full image>' % (sys.argv[0])
sys.exit(0)
# stock args
dev = sys.argv[1]
firm = sys.argv[2]
# check firmware first
try:
firmsize = os.path.getsize(firm)
except Exception, e:
sys.stderr.write("%s\n" % (str(e)));
sys.exit(1)
if firmsize > NB_TOTAL_SIZE:
sys.stderr.write("Error, The size of the firmware should not exceed %d bytes otherwise the CFE might be compromised (%s - %d bytes)\n" % (NB_TOTAL_SIZE, firm, firmsize));
sys.exit(1)
if firmsize != NB_TOTAL_SIZE:
print 'This program expects a full image of %d bytes (CFE + MAIN + JFFS2 + RESCUE + DSL + NV)' % (NB_TOTAL_SIZE,)
print 'Your image has a size of %d bytes' % (firmsize)
ch = raw_input('Continue anyway ? (if you do not know what you do, do not continue !) (y|N) ')
if ch != 'y':
sys.exit(0)
print '+++++++++++++++++++++++++++++++++++++++++++++++'
print ' Image: %s' % (firm)
print ' > Size: %d bytes' % (firmsize,)
print '+++++++++++++++++++++++++++++++++++++++++++++++'
# open socket RAW
try:
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW)
except Exception, e:
sys.stderr.write("Error while creating socket: %s\n" % str(e))
sys.exit(1)
# bind on device
s.bind((dev,0x8888))
# get the mac addr
my_eth_addr_r = s.getsockname()[-1]
print "%s ethernet address: %s" % (dev, eth_ntoa(my_eth_addr_r))
# write and read packet
dlcpkt_w = Dlcpkt()
dlcpkt_r = Dlcpkt()
# first send discover to get MAC ADDR
# and VERSION_INFO of the box
print " > Info request on broadcast"
dlcpkt_w.dstaddr = ETH_ADDR_BROADCAST
dlcpkt_w.srcaddr = my_eth_addr_r
dlcpkt_w.wcmd = i16ton(CMD_VERSION)
resp = request(s, dlcpkt_w)
dlcpkt_r.unpack(resp)
print " < Receive response from %s - %s" % (eth_ntoa(dlcpkt_r.srcaddr), dlcpkt_r.bdata[4:])
box_eth_addr_r = dlcpkt_r.srcaddr
ch = raw_input('Continue ? (y|N) ')
if ch != 'y':
print "Ok, exit !"
s.close()
sys.exit(0)
# ask we want to put a firmware
print " > Flash request to %s" % (eth_ntoa(box_eth_addr_r))
dlcpkt_w.dstaddr = box_eth_addr_r
dlcpkt_w.wcmd = i16ton(CMD_REQUEST)
dlcpkt_w.wsequence = i16ton(counter_wsequence)
resp = request(s, dlcpkt_w)
dlcpkt_r.unpack(resp)
if dlcpkt_r.wcmd == i16ton(CMD_REQUEST) \
and dlcpkt_r.wsequence == dlcpkt_w.wsequence :
print " < Ok, box wait flashing"
else:
print " < Error on flash request ..."
s.close()
sys.exit(0)
counter_wsequence = (counter_wsequence + 1) % (0x10000)
# send the firmware
send_file(s, dlcpkt_w, firm);
dlcpkt_w.bzero()
dlcpkt_w.dstaddr = box_eth_addr_r
dlcpkt_w.srcaddr = my_eth_addr_r
# verify ?
# reboot the box ?
ch = raw_input('Press Enter to reboot the box')
print " > Send reboot request"
dlcpkt_w.wcmd = i16ton(CMD_RESET)
resp = request(s, dlcpkt_w);
dlcpkt_r.unpack(resp)
if dlcpkt_r.wcmd == i16ton(CMD_RESET):
print " < Ok, rebooting the box"
else:
print " > Error on rebooting command"
print "End."
s.close()