Skip to content
Browse files

submodules are too hard for some people, screw it for now.

  • Loading branch information...
1 parent d4cfd25 commit e954a17f813cdff910579a633a827084ffa43d91 @apmorton apmorton committed Apr 15, 2012
Showing with 588 additions and 4 deletions.
  1. +0 −3 .gitmodules
  2. +0 −1 xflash
  3. +50 −0 xflash/README
  4. +25 −0 xflash/setup.py
  5. +87 −0 xflash/xflash/XConfig.py
  6. +201 −0 xflash/xflash/XFlash.py
  7. +28 −0 xflash/xflash/XStatus.py
  8. +197 −0 xflash/xflash/__init__.py
View
3 .gitmodules
@@ -1,3 +0,0 @@
-[submodule "xflash"]
- path = xflash
- url = git://github.com/Juvenal1228/XFlash.git
1 xflash
@@ -1 +0,0 @@
-Subproject commit 9d4f70a075b54b02a7149e63b4acc7568c9291c6
View
50 xflash/README
@@ -0,0 +1,50 @@
+Features:
+ * read/write/erase all size nands - 16mb, 64mb, 256mb, 512mb
+ * flash xsvf's to your CPLD (assuming your nandflasher supports that) - Nand-X
+ * power on/off the console (assuming your nandflasher supports that and is wired correctly)
+ * supports armV1 and armV3 flashers (where nandpro only supports armV3)
+
+Usage:
+
+ xflash [-h] {read,write,erase,xsvf,update,poweroff,poweron} ...
+
+ XBox 360 NAND Flasher
+
+ optional arguments:
+ -h, --help show this help message and exit
+
+ Operations:
+ {read,write,erase,xsvf,update,poweroff,poweron}
+ read Dumps an image from the NAND
+ write Writes an image into the NAND
+ erase Erases blocks in the NAND
+ xsvf Flash a CPLD with an xsvf file
+ update Jumps into the bootloader of the NAND Flashing device
+ for updating the firmware
+ poweroff Shuts down the attached XBox 360
+ poweron Powers up the attached XBox 360
+
+
+detailed usage for each command can be had with
+xflash <command> --help
+
+Examples:
+# dump the full sized nand to 'nanddump.bin'
+$sudo xflash read nanddump.bin
+
+# write the full sized nand from 'nandflash.bin'
+$sudo xflash write nandflash.bin
+
+# dump the first 50 blocks worth of nand to shortdump.bin
+$sudo xflash read shortdump.bin 0 50
+
+# write the first 50 blocks of nand from image.ecc
+$sudo xflash write image.ecc 0 50
+
+Thanks / Credits:
+-Whoever wrote the original XFlash.py script
+-Anyone else involved the Free60 Project
+-The Makers of NandPro and the PIC Flashing code
+-G33KatWork (https://github.com/G33KatWork/XBox-360-AVR-flasher) - for his modifications to XFlash.py
+-Juvenal - current author of XFlash 1.3
+
View
25 xflash/setup.py
@@ -0,0 +1,25 @@
+from setuptools import setup, find_packages
+import sys, os
+
+setup(name='xflash',
+ version='1.3.1',
+ description='Xbox360 USB SPI Flasher client',
+ long_description='',
+ classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
+ keywords='',
+ author='Juvenal',
+ author_email='none@of.your.biz',
+ url='https://github.com/Juvenal1228/XFlash/',
+ license='BSD',
+ packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+ include_package_data=True,
+ zip_safe=False,
+ install_requires=[
+ 'pyusb>=1.0.0a1',
+ 'argparse',
+ ],
+ entry_points="""
+ [console_scripts]
+ xflash = xflash:main
+ """,
+)
View
87 xflash/xflash/XConfig.py
@@ -0,0 +1,87 @@
+
+class XConfigParseError(Exception):
+ pass
+
+class XConfig(object):
+ def __init__(self, config):
+ self.config = config
+ self.controllertype = config >> 17 & 3
+ self.blocktype = config >> 4 & 3
+
+ self.pagesz = 0x200
+ self.metasz = 0x10
+ self.metatype = 0
+ self.blocksz = 0
+ self.sizeblocks = 0
+ self.sizesmallblocks = 0
+ self.fsblocks = 0
+ ctype = self.controllertype
+ btype = self.blocktype
+
+ if ctype == 0:
+ self.metatype = 0
+ self.blocksz = 0x20
+ if btype == 0:
+ msg = 'nand type 0:0 is invalid'
+ raise XConfigParseError(msg)
+ elif btype == 1:
+ self.sizeblocks = 0x400
+ self.fsblocks = 0x3E0
+ elif btype == 2:
+ self.sizeblocks = 0x800
+ self.fsblocks = 0x7C0
+ elif btype == 3:
+ self.sizeblocks = 0x1000
+ self.fsblocks = 0xF80
+ elif ctype == 1 and btype == 0:
+ msg = 'nand type 1:0 is invalid'
+ raise XConfigParseError(msg)
+ elif ctype in (1, 2) and btype in (0, 1):
+ self.metatype = 1
+ self.blocksz = 0x20
+ if btype == 0 or (btype == 1 and ctype == 1):
+ self.sizeblocks = 0x400
+ self.fsblocks = 0x3E0
+ elif ctype == 2 and btype == 1:
+ self.sizeblocks = 0x1000
+ self.fsblocks = 0xF80
+ elif ctype in (1, 2) and btype in (2, 3):
+ self.metatype = 2
+ if btype == 2:
+ self.blocksz = 0x100
+ self.sizeblocks = 1 << ((config >> 19 & 3) + (config >> 21 & 15) + 23) >> 17
+ self.fsblocks = 0x1E0
+ elif btype == 3:
+ self.blocksz = 0x200
+ self.sizeblocks = 1 << ((config >> 19 & 3) + (config >> 21 & 15) + 23) >> 18
+ self.fsblocks = 0xF0
+ else:
+ msg = 'controller type %s is invalid' % ctype
+ raise XConfigParseError(msg)
+ self.sizesmallblocks = self.sizeblocks * (self.blocksz / 0x20)
+ self.blocksperlittle = self.blocksz / 0x20
+
+ def printConfig(self):
+ fmt = """
+ FlashConfig:\t%X
+ PageSize:\t%X
+ MetaSize:\t%X
+ MetaType:\t%X
+ BlockSize:\t%X
+ SizeInBlocks:\t%X
+ SizeInSBlocks:\t%X
+ FileBlocks:\t%X
+ """ % (
+ self.config,
+ self.pagesz,
+ self.metasz,
+ self.metatype,
+ self.blocksz,
+ self.sizeblocks,
+ self.sizesmallblocks,
+ self.fsblocks,
+ )
+ print fmt
+
+
+
View
201 xflash/xflash/XFlash.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+import usb
+import struct
+import StringIO
+
+from xflash import XConfig
+
+class DeviceNotFoundError(Exception):
+ pass
+
+CMD_DATA_READ = 0x01
+CMD_DATA_WRITE = 0x02
+CMD_DATA_INIT = 0x03
+CMD_DATA_DEINIT = 0x04
+CMD_DATA_STATUS = 0x05
+CMD_DATA_ERASE = 0x06
+CMD_DATA_EXEC = 0x07
+CMD_DEV_VERSION = 0x08
+CMD_XSVF_EXEC = 0x09
+CMD_XBOX_PWRON = 0x10
+CMD_XBOX_PWROFF = 0x11
+CMD_DEV_UPDATE = 0xF0
+
+class XFlash(object):
+ def __init__(self, idVendor=0xffff, idProduct=0x4):
+ self.idVendor = idVendor
+ self.idProduct = idProduct
+ self.dev = None
+
+ def deviceFind(self):
+ self.dev = usb.core.find(idVendor=self.idVendor,
+ idProduct=self.idProduct)
+
+ if self.dev is None:
+ raise DeviceNotFoundError(self.idVendor, self.idProduct)
+
+ self.dev.set_configuration()
+
+ self.ep_out = 0x05
+ self.ep_in = 0x82
+
+ def deviceReset(self):
+ if self.dev is None:
+ return
+
+ self.dev.reset()
+ self.dev.set_configuration()
+
+ def deviceCmd(self, cmd, argA=0, argB=0, timeout=None):
+ buf = struct.pack('<LL', argA, argB)
+ self.dev.ctrl_transfer(bmRequestType = usb.TYPE_VENDOR,
+ bRequest = cmd,
+ wValue = 0,
+ wIndex = 0,
+ data_or_wLength = buf,
+ timeout = timeout)
+
+ def deviceUpdate(self):
+ try:
+ self.deviceCmd(CMD_DEV_UPDATE)
+ except: pass
+
+ def deviceVersion(self):
+ self.deviceCmd(CMD_DEV_VERSION, 0 , 4)
+ buf = self.dev.read(self.ep_in, 4) # read 4 bytes
+ buf = ''.join([chr(x) for x in buf])
+ return struct.unpack('<L', buf)[0]
+
+ def xsvfInit(self):
+ self.deviceReset()
+ self.deviceVersion()
+ self.deviceVersion()
+ return self.deviceVersion()
+
+ def xsvfWrite(self, buf):
+ self.deviceCmd(CMD_DATA_WRITE, 0, len(buf))
+ self.dev.write(self.ep_out, buf)
+
+ def xsvfExecute(self):
+ self.deviceCmd(CMD_XSVF_EXEC, timeout=10000)
+ return self.flashStatus()
+
+ def __flash_status(self, cmd=None):
+ if not cmd is None:
+ self.deviceCmd(cmd)
+
+ buf = self.dev.read(self.ep_in, 4) # read 4 bytes
+ buf = ''.join([chr(x) for x in buf])
+ return struct.unpack('<L', buf)[0]
+
+ def flashInit(self):
+ config = self.__flash_status(CMD_DATA_INIT)
+ self.xc = XConfig.XConfig(config)
+ return config
+
+ def flashDeInit(self):
+ self.deviceCmd(CMD_DATA_DEINIT)
+
+ def flashStatus(self):
+ return self.__flash_status(CMD_DATA_STATUS)
+
+ def flashErase(self, block):
+ self.deviceCmd(CMD_DATA_ERASE, block)
+ if self.deviceVersion() >= 3:
+ self.deviceCmd(CMD_DATA_EXEC, block)
+ return self.flashStatus()
+
+ def flashRead(self, block):
+ self.deviceCmd(CMD_DATA_READ, block, 0x4200)
+ buf = self.dev.read(self.ep_in, 0x4200)
+ buf = ''.join([chr(x) for x in buf])
+ status = self.flashStatus()
+ return (status, buf)
+
+ def flashReadBlock(self, block):
+ """
+ Read a full block, in whatever mode the flash config
+ says is right
+ """
+ buf = ''
+ status = 0
+ adjblock = block * self.xc.blocksperlittle
+ for b in range(adjblock, adjblock+self.xc.blocksperlittle):
+ (stts, bff) = self.flashRead(b)
+ status |= stts
+ buf += bff
+ return (status, buf)
+
+ def flashWrite(self, block, buf):
+ if len(buf) < 0x4200:
+ raise ValueError('buffer is not long enough')
+ self.deviceCmd(CMD_DATA_WRITE, block, 0x4200)
+ self.dev.write(self.ep_out, buf)
+ if self.deviceVersion() >= 3:
+ self.deviceCmd(CMD_DATA_EXEC, block)
+ return self.flashStatus()
+
+ def flashWriteBlock(self, block, buf):
+ """
+ write a full block, in whatever mode the flash config
+ says is right
+ """
+ status = 0
+ adjblock = block * self.xc.blocksperlittle
+ for b in range(self.xc.blocksperlittle):
+ blk = adjblock + b
+ bff = b * 0x4200
+ status |= self.flashWrite(blk, buf[bff:bff+0x4200])
+ return status
+ def consolePowerOn(self):
+ self.deviceCmd(CMD_XBOX_PWRON)
+
+ def consolePowerOff(self):
+ self.deviceCmd(CMD_XBOX_PWROFF)
+
+ @classmethod
+ def compress(cls, ib):
+ ob = StringIO.StringIO()
+
+ rs = re = rl = 0
+ while rs < len(ib):
+ # check for repeat
+ if ib[rs] == ib[rs + 1]:
+ re = rs
+ while re < len(ib) and ib[rs] == ib[re]:
+ re += 1
+ rl = re - rs
+ ob.write(ib[rs:rs+2])
+ ob.write(chr(rl - 2))
+ rs += rl
+ else:
+ ob.write(ib[rs])
+ rs += 1
+ return ob.getvalue()
+
+# def calcecc(data):
+# assert len(data) == 0x210
+# val = 0
+# for i in range(0x1066):
+# if not i & 31:
+# v = ~struct.unpack("<L", data[i/8:i/8+4])[0]
+# val ^= v & 1
+# v >>= 1
+# if val & 1:
+# val ^= 0x6954559
+# val >>= 1
+#
+# val = ~val
+# return data[:-4] + struct.pack("<L", (val << 6) & 0xFFFFFFFF)
+#
+# def addecc(data, block = 0, off_8 = "\x00" * 4):
+# res = ""
+# while len(data):
+# d = (data[:0x200] + "\x00" * 0x200)[:0x200]
+# data = data[0x200:]
+#
+# d += struct.pack("<L4B4s4s", block / 32, 0, 0xFF, 0, 0, off_8, "\0\0\0\0")
+# d = calcecc(d)
+# block += 1
+# res += d
+# return res
View
28 xflash/xflash/XStatus.py
@@ -0,0 +1,28 @@
+STATUS_ILL_LOG = 0x800
+STATUS_PIN_WP_N = 0x400
+STATUS_PIN_BY_N = 0x200
+STATUS_INT_CP = 0x100
+STATUS_ADDR_ER = 0x080
+STATUS_BB_ER = 0x040
+STATUS_RNP_ER = 0x020
+STATUS_ECC_ER = 0x01c
+STATUS_WR_ER = 0x002
+STATUS_BUSY = 0x001
+
+STATUS_OK = (STATUS_PIN_BY_N)
+STATUS_ERROR = (STATUS_ILL_LOG |
+ STATUS_ADDR_ER |
+ STATUS_BB_ER |
+ STATUS_RNP_ER |
+ STATUS_ECC_ER |
+ STATUS_WR_ER)
+
+def statusIsError(status):
+ if status & STATUS_ERROR != 0:
+ return True
+ if status & STATUS_OK == 0:
+ return True
+ return False
+
+def statusHasBit(status, bit):
+ return status & bit != 0
View
197 xflash/xflash/__init__.py
@@ -0,0 +1,197 @@
+import argparse
+import os
+import pprint
+import sys
+
+from XFlash import XFlash, DeviceNotFoundError
+from XConfig import XConfig, XConfigParseError
+from XStatus import *
+
+pp = pprint.PrettyPrinter()
+
+class ConsoleUI:
+ def opStart(self, name, plural):
+ self.name = name[0].upper() + name[1:]
+ self.plural = plural
+ sys.stdout.write(self.name + '\t')
+
+ def opProgress(self,progress, total=-1):
+ if (total >= 0):
+ prstr = '0x%04X / 0x%04X' % (progress, total)
+ else:
+ prstr = '0x%04X' % (progress)
+
+ sys.stdout.write(prstr.ljust(20))
+ sys.stdout.write('\b' * 20)
+ sys.stdout.flush()
+
+ def opProgressErr(self, block, error):
+ sys.stdout.write('\b' * 20)
+ msg = 'Error: %X %s block %X' % (error,
+ self.plural,
+ block)
+ sys.stdout.write(msg.ljust(40) + '\n')
+ self.opStart(self.name, self.plural)
+
+ def opEnd(self, result):
+ sys.stdout.write('\b' * 2)
+ sys.stdout.write(result.ljust(20))
+ sys.stdout.write("\n")
+
+def hex2int(instring):
+ try:
+ return int(instring, 16)
+ except ValueError:
+ msg = '%s is not a valid hexadecimal number' % (instring)
+ raise argparse.ArgumentTypeError(msg)
+
+def main(argv=None):
+ parser = argparse.ArgumentParser(description='XBox 360 NAND Flasher')
+ actions = parser.add_subparsers(title='Operations', dest='action')
+
+ read = actions.add_parser('read', help='Dumps an image from the NAND')
+ read.add_argument('file', nargs=1, type=argparse.FileType('w'),
+ help='The file to dump the NAND to')
+ read.add_argument('start', nargs='?', metavar='start',
+ action='store', type=hex2int, default=0,
+ help='The block to start the action from')
+ read.add_argument('length', nargs='?', metavar='length',
+ action='store', type=hex2int, default=None,
+ help='The count of blocks to perform the action to')
+
+ write = actions.add_parser('write', help='Writes an image into the NAND')
+ write.add_argument('file', nargs=1, type=argparse.FileType('r'),
+ help='The image file to write to the NAND')
+ write.add_argument('start', nargs='?', metavar='start',
+ action='store', type=hex2int, default=0,
+ help='The block to start the action from')
+ write.add_argument('length', nargs='?', metavar='length',
+ action='store', type=hex2int, default=None,
+ help='The count of blocks to perform the action to')
+
+ erase = actions.add_parser('erase', help='Erases blocks in the NAND')
+ erase.add_argument('start', nargs='?', metavar='start',
+ action='store', type=hex2int, default=0,
+ help='The block to start the action from')
+ erase.add_argument('length', nargs='?', metavar='length',
+ action='store', type=hex2int, default=None,
+ help='The count of blocks to perform the action to')
+
+ xsvf = actions.add_parser('xsvf', help='Flash a CPLD with an xsvf file')
+ xsvf.add_argument('file', nargs=1, type=argparse.FileType('r'), help='The xsvf file to read from')
+
+ update = actions.add_parser('update',
+ help='Jumps into the bootloader of the NAND Flashing device for updating the firmware')
+ shutdown = actions.add_parser('poweroff',
+ help='Shuts down the attached XBox 360')
+ poweron = actions.add_parser('poweron',
+ help='Powers up the attached XBox 360')
+
+ arguments = parser.parse_args(argv)
+
+ ui = ConsoleUI()
+ xf = XFlash()
+ xc = None
+ doOp = None
+
+ try:
+ xf.deviceFind()
+ except DeviceNotFoundError:
+ print 'XFlash USB device not found'
+ sys.exit(1)
+
+ xf.deviceReset()
+
+ start = length = end = 0
+
+ print "Using XFlash @ [%s:%s]" % (xf.dev.bus, xf.dev.address)
+ if arguments.action in ('erase', 'write', 'read'):
+ try:
+ vers = xf.deviceVersion()
+ print 'ARM Version %s' % (vers)
+ flashconfig = xf.flashInit()
+ print 'FlashConfig: 0x%08X' % (flashconfig)
+ xc = XConfig(flashconfig)
+ except XConfigParseError as e:
+ print 'Flash Config is invalid!'
+ print e
+ sys.exit(1)
+ except:
+ xf.flashDeInit()
+ start = arguments.start
+ length = arguments.length or (xc.sizesmallblocks - start)
+ end = start + length
+
+ if end > xc.sizesmallblocks:
+ print 'Error: tried to read past the nand length'
+ print 'NandSize: %X\tOperationEnd: %X' % (xc.sizeblocks, end)
+ sys.exit(1)
+
+ if arguments.action == 'erase':
+ doOp = xf.flashErase
+
+ if arguments.action == 'read':
+ def doOp(b):
+ (status, buf) = xf.flashRead(b)
+ arguments.file[0].write(buf)
+ return status
+
+ if arguments.action == 'write':
+ def doOp(b):
+ blocksize = 528 * 32
+ buf = arguments.file[0].read(blocksize)
+ if len(buf) < blocksize:
+ buf += ('\xFF' * (blocksize-len(buf)))
+ return xf.flashWrite(b, buf)
+
+ if arguments.action == 'xsvf':
+ vers = xf.xsvfInit()
+ if vers < 3:
+ print 'ARM Version %s does not support xsvf flashing!' % vers
+ sys.exit(1)
+ print 'ARM Version %s' % (vers)
+ fbuf = arguments.file[0].read()
+ print 'Read 0x%x bytes OK' % (len(fbuf))
+ buf = XFlash.compress(fbuf)
+ print 'Compressed to 0x%x bytes OK' % (len(buf))
+ xf.xsvfWrite(buf)
+ print '0x%x bytes sent OK' % (len(buf))
+ print 'Executing File...',
+ sys.stdout.flush()
+ status = xf.xsvfExecute()
+ print 'OK!' if status == 0 else 'FAIL!'
+ xf.deviceReset()
+
+ if arguments.action == 'update':
+ xf.deviceUpdate()
+
+ if arguments.action == 'poweron':
+ xf.flashPowerOn()
+
+ if arguments.action == 'poweroff':
+ xf.flashPowerOff()
+
+ if arguments.action in ('read', 'write', 'erase'):
+ plural = arguments.action[:-1] + 'ing'
+ if arguments.action == 'read':
+ plural = 'reading'
+ ui.opStart(arguments.action, plural)
+ try:
+ for b in xrange(start, end):
+ status = doOp(b)
+ if statusIsError(status):
+ ui.opProgressErr(b, status)
+ ui.opProgress(b, end-1)
+ except KeyboardInterrupt:
+ ui.opEnd('INTERRUPTED!')
+ path = os.path.abspath(arguments.file[0].name)
+ if os.path.isfile(path):
+ os.remove(path)
+ except:
+ ui.opEnd('FAIL!')
+ raise
+ else:
+ ui.opEnd('%X OK!' % (length))
+
+if __name__ == '__main__':
+ main()

0 comments on commit e954a17

Please sign in to comment.
Something went wrong with that request. Please try again.