mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-08 04:56:43 +00:00
It's a bit confusing to run the wrong tool but hopefully there are ample help messages sprinkled in this to guide the average user to the right location.
741 lines
29 KiB
Python
741 lines
29 KiB
Python
#! /usr/bin/env python3
|
|
|
|
import argparse
|
|
|
|
import usb.core
|
|
import usb.util
|
|
import array
|
|
import sys
|
|
import hashlib
|
|
import csv
|
|
import urllib.request
|
|
|
|
from progressbar.bar import ProgressBar
|
|
|
|
class PrecursorUsb:
|
|
def __init__(self, dev):
|
|
self.dev = dev
|
|
self.RDSR = 0x05
|
|
self.RDSCUR = 0x2B
|
|
self.RDID = 0x9F
|
|
self.WREN = 0x06
|
|
self.WRDI = 0x04
|
|
self.SE4B = 0x21
|
|
self.BE4B = 0xDC
|
|
self.PP4B = 0x12
|
|
self.registers = {}
|
|
self.regions = {}
|
|
self.gitrev = ''
|
|
|
|
def register(self, name):
|
|
return int(self.registers[name], 0)
|
|
|
|
def peek(self, addr, display=False):
|
|
_dummy_s = '\x00'.encode('utf-8')
|
|
data = array.array('B', _dummy_s * 4)
|
|
|
|
numread = self.dev.ctrl_transfer(bmRequestType=(0x80 | 0x43), bRequest=0,
|
|
wValue=(addr & 0xffff), wIndex=((addr >> 16) & 0xffff),
|
|
data_or_wLength=data, timeout=500)
|
|
|
|
read_data = int.from_bytes(data.tobytes(), byteorder='little', signed=False)
|
|
if display == True:
|
|
print("0x{:08x}".format(read_data))
|
|
return read_data
|
|
|
|
def poke(self, addr, wdata, check=False, display=False):
|
|
if check == True:
|
|
_dummy_s = '\x00'.encode('utf-8')
|
|
data = array.array('B', _dummy_s * 4)
|
|
|
|
numread = self.dev.ctrl_transfer(bmRequestType=(0x80 | 0x43), bRequest=0,
|
|
wValue=(addr & 0xffff), wIndex=((addr >> 16) & 0xffff),
|
|
data_or_wLength=data, timeout=500)
|
|
|
|
read_data = int.from_bytes(data.tobytes(), byteorder='little', signed=False)
|
|
print("before poke: 0x{:08x}".format(read_data))
|
|
|
|
data = array.array('B', wdata.to_bytes(4, 'little'))
|
|
numwritten = self.dev.ctrl_transfer(bmRequestType=(0x00 | 0x43), bRequest=0,
|
|
wValue=(addr & 0xffff), wIndex=((addr >> 16) & 0xffff),
|
|
data_or_wLength=data, timeout=500)
|
|
|
|
if check == True:
|
|
_dummy_s = '\x00'.encode('utf-8')
|
|
data = array.array('B', _dummy_s * 4)
|
|
|
|
numread = self.dev.ctrl_transfer(bmRequestType=(0x80 | 0x43), bRequest=0,
|
|
wValue=(addr & 0xffff), wIndex=((addr >> 16) & 0xffff),
|
|
data_or_wLength=data, timeout=500)
|
|
|
|
read_data = int.from_bytes(data.tobytes(), byteorder='little', signed=False)
|
|
print("after poke: 0x{:08x}".format(read_data))
|
|
if display == True:
|
|
print("wrote 0x{:08x} to 0x{:08x}".format(wdata, addr))
|
|
|
|
def burst_read(self, addr, len):
|
|
_dummy_s = '\x00'.encode('utf-8')
|
|
maxlen = 4096
|
|
|
|
ret = bytearray()
|
|
packet_count = len // maxlen
|
|
if (len % maxlen) != 0:
|
|
packet_count += 1
|
|
|
|
for pkt_num in range(packet_count):
|
|
cur_addr = addr + pkt_num * maxlen
|
|
if pkt_num == packet_count - 1:
|
|
if len % maxlen != 0:
|
|
bufsize = len % maxlen
|
|
else:
|
|
bufsize = maxlen
|
|
else:
|
|
bufsize = maxlen
|
|
|
|
data = array.array('B', _dummy_s * bufsize)
|
|
numread = self.dev.ctrl_transfer(bmRequestType=(0x80 | 0x43), bRequest=0,
|
|
wValue=(cur_addr & 0xffff), wIndex=((cur_addr >> 16) & 0xffff),
|
|
data_or_wLength=data, timeout=500)
|
|
|
|
if numread != bufsize:
|
|
print("Burst read error: {} bytes requested, {} bytes read at 0x{:08x}".format(bufsize, numread, cur_addr))
|
|
exit(1)
|
|
|
|
ret = ret + data
|
|
|
|
return ret
|
|
|
|
def burst_write(self, addr, data):
|
|
if len(data) == 0:
|
|
return
|
|
|
|
# the actual "addr" doesn't matter for a burst_write, because it's specified
|
|
# as an argument to the flash_pp4b command. We lock out access to the base of
|
|
# SPINOR because it's part of the gateware, so, we pick a "safe" address to
|
|
# write to instead. The page write responder will aggregate any write data
|
|
# to anywhere in the SPINOR address range.
|
|
writebuf_addr = 0x2098_0000 # the current start address of the kernel, for example
|
|
|
|
maxlen = 4096
|
|
packet_count = len(data) // maxlen
|
|
if (len(data) % maxlen) != 0:
|
|
packet_count += 1
|
|
|
|
for pkt_num in range(packet_count):
|
|
cur_addr = addr + pkt_num * maxlen
|
|
if pkt_num == packet_count - 1:
|
|
if len(data) % maxlen != 0:
|
|
bufsize = len(data) % maxlen
|
|
else:
|
|
bufsize = maxlen
|
|
else:
|
|
bufsize = maxlen
|
|
|
|
wdata = array.array('B', data[(pkt_num * maxlen):(pkt_num * maxlen) + bufsize])
|
|
numwritten = self.dev.ctrl_transfer(bmRequestType=(0x00 | 0x43), bRequest=0,
|
|
# note use of writebuf_addr instead of cur_addr -> see comment above about the quirk of write addressing
|
|
wValue=(writebuf_addr & 0xffff), wIndex=((writebuf_addr >> 16) & 0xffff),
|
|
data_or_wLength=wdata, timeout=500)
|
|
|
|
if numwritten != bufsize:
|
|
print("Burst write error: {} bytes requested, {} bytes written at 0x{:08x}".format(bufsize, numwritten, cur_addr))
|
|
exit(1)
|
|
|
|
def ping_wdt(self):
|
|
self.poke(self.register('wdt_watchdog'), 1, display=False)
|
|
self.poke(self.register('wdt_watchdog'), 1, display=False)
|
|
|
|
def spinor_command_value(self, exec=0, lock_reads=0, cmd_code=0, dummy_cycles=0, data_words=0, has_arg=0):
|
|
return ((exec & 1) << 1 |
|
|
(lock_reads & 1) << 24 |
|
|
(cmd_code & 0xff) << 2 |
|
|
(dummy_cycles & 0x1f) << 11 |
|
|
(data_words & 0xff) << 16 |
|
|
(has_arg & 1) << 10
|
|
)
|
|
|
|
def flash_rdsr(self, lock_reads):
|
|
self.poke(self.register('spinor_cmd_arg'), 0)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=lock_reads, cmd_code=self.RDSR, dummy_cycles=4, data_words=1, has_arg=1)
|
|
)
|
|
return self.peek(self.register('spinor_cmd_rbk_data'), display=False)
|
|
|
|
def flash_rdscur(self):
|
|
self.poke(self.register('spinor_cmd_arg'), 0)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.RDSCUR, dummy_cycles=4, data_words=1, has_arg=1)
|
|
)
|
|
return self.peek(self.register('spinor_cmd_rbk_data'), display=False)
|
|
|
|
def flash_rdid(self, offset):
|
|
self.poke(self.register('spinor_cmd_arg'), 0)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, cmd_code=self.RDID, dummy_cycles=4, data_words=offset, has_arg=1)
|
|
)
|
|
return self.peek(self.register('spinor_cmd_rbk_data'), display=False)
|
|
|
|
def flash_wren(self):
|
|
self.poke(self.register('spinor_cmd_arg'), 0)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.WREN)
|
|
)
|
|
|
|
def flash_wrdi(self):
|
|
self.poke(self.register('spinor_cmd_arg'), 0)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.WRDI)
|
|
)
|
|
|
|
def flash_se4b(self, sector_address):
|
|
self.poke(self.register('spinor_cmd_arg'), sector_address)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.SE4B, has_arg=1)
|
|
)
|
|
|
|
def flash_be4b(self, block_address):
|
|
self.poke(self.register('spinor_cmd_arg'), block_address)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.BE4B, has_arg=1)
|
|
)
|
|
|
|
def flash_pp4b(self, address, data_bytes):
|
|
self.poke(self.register('spinor_cmd_arg'), address)
|
|
self.poke(self.register('spinor_command'),
|
|
self.spinor_command_value(exec=1, lock_reads=1, cmd_code=self.PP4B, has_arg=1, data_words=(data_bytes//2))
|
|
)
|
|
|
|
def load_csrs(self, fname=None):
|
|
LOC_CSRCSV = 0x20277000 # this address shouldn't change because it's how we figure out our version number
|
|
# CSR extraction:
|
|
# dd if=soc_csr.bin of=csr_data_0.9.6.bin skip=2524 count=32 bs=1024
|
|
if fname == None:
|
|
csr_data = self.burst_read(LOC_CSRCSV, 0x8000)
|
|
else:
|
|
with open(fname, "rb") as f:
|
|
csr_data = f.read(0x8000)
|
|
|
|
hasher = hashlib.sha512()
|
|
hasher.update(csr_data[:0x7FC0])
|
|
digest = hasher.digest()
|
|
if digest != csr_data[0x7fc0:]:
|
|
print("Could not find a valid csr.csv descriptor on the device, aborting!")
|
|
exit(1)
|
|
|
|
csr_len = int.from_bytes(csr_data[:4], 'little')
|
|
csr_extracted = csr_data[4:4+csr_len]
|
|
decoded = csr_extracted.decode('utf-8')
|
|
# strip comments
|
|
stripped = []
|
|
for line in decoded.split('\n'):
|
|
if line.startswith('#') == False:
|
|
stripped.append(line)
|
|
# create database
|
|
csr_db = csv.reader(stripped)
|
|
for row in csr_db:
|
|
if len(row) > 1:
|
|
if 'csr_register' in row[0]:
|
|
self.registers[row[1]] = row[2]
|
|
if 'memory_region' in row[0]:
|
|
self.regions[row[1]] = [row[2], row[3]]
|
|
if 'git_rev' in row[0]:
|
|
self.gitrev = row[1]
|
|
print("Using SoC {} registers".format(self.gitrev))
|
|
|
|
def erase_region(self, addr, length):
|
|
# ID code check
|
|
code = self.flash_rdid(1)
|
|
print("ID code bytes 1-2: 0x{:08x}".format(code))
|
|
if code != 0x8080c2c2:
|
|
print("ID code mismatch")
|
|
exit(1)
|
|
code = self.flash_rdid(2)
|
|
print("ID code bytes 2-3: 0x{:08x}".format(code))
|
|
if code != 0x3b3b8080:
|
|
print("ID code mismatch")
|
|
exit(1)
|
|
|
|
# block erase
|
|
progress = ProgressBar(min_value=0, max_value=length, prefix='Erasing ').start()
|
|
erased = 0
|
|
while erased < length:
|
|
self.ping_wdt()
|
|
if (length - erased >= 65536) and ((addr & 0xFFFF) == 0):
|
|
blocksize = 65536
|
|
else:
|
|
blocksize = 4096
|
|
|
|
while True:
|
|
self.flash_wren()
|
|
status = self.flash_rdsr(1)
|
|
if status & 0x02 != 0:
|
|
break
|
|
|
|
if blocksize == 4096:
|
|
self.flash_se4b(addr + erased)
|
|
else:
|
|
self.flash_be4b(addr + erased)
|
|
erased += blocksize
|
|
|
|
while (self.flash_rdsr(1) & 0x01) != 0:
|
|
pass
|
|
|
|
result = self.flash_rdscur()
|
|
if result & 0x60 != 0:
|
|
print("E_FAIL/P_FAIL set on erase, programming may fail, but trying anyways...")
|
|
|
|
if self.flash_rdsr(1) & 0x02 != 0:
|
|
self.flash_wrdi()
|
|
while (self.flash_rdsr(1) & 0x02) != 0:
|
|
pass
|
|
if erased < length:
|
|
progress.update(erased)
|
|
progress.finish()
|
|
print("Erase finished")
|
|
|
|
# addr is relative to the base of FLASH (not absolute)
|
|
def flash_program(self, addr, data, verify=True):
|
|
flash_region = int(self.regions['spiflash'][0], 0)
|
|
flash_len = int(self.regions['spiflash'][1], 0)
|
|
|
|
if (addr + len(data) > flash_len):
|
|
print("Write data out of bounds! Aborting.")
|
|
exit(1)
|
|
|
|
# ID code check
|
|
code = self.flash_rdid(1)
|
|
print("ID code bytes 1-2: 0x{:08x}".format(code))
|
|
if code != 0x8080c2c2:
|
|
print("ID code mismatch")
|
|
exit(1)
|
|
code = self.flash_rdid(2)
|
|
print("ID code bytes 2-3: 0x{:08x}".format(code))
|
|
if code != 0x3b3b8080:
|
|
print("ID code mismatch")
|
|
exit(1)
|
|
|
|
# block erase
|
|
progress = ProgressBar(min_value=0, max_value=len(data), prefix='Erasing ').start()
|
|
erased = 0
|
|
while erased < len(data):
|
|
self.ping_wdt()
|
|
if (len(data) - erased >= 65536) and ((addr & 0xFFFF) == 0):
|
|
blocksize = 65536
|
|
else:
|
|
blocksize = 4096
|
|
|
|
while True:
|
|
self.flash_wren()
|
|
status = self.flash_rdsr(1)
|
|
if status & 0x02 != 0:
|
|
break
|
|
|
|
if blocksize == 4096:
|
|
self.flash_se4b(addr + erased)
|
|
else:
|
|
self.flash_be4b(addr + erased)
|
|
erased += blocksize
|
|
|
|
while (self.flash_rdsr(1) & 0x01) != 0:
|
|
pass
|
|
|
|
result = self.flash_rdscur()
|
|
if result & 0x60 != 0:
|
|
print("E_FAIL/P_FAIL set on erase, programming may fail, but trying anyways...")
|
|
|
|
if self.flash_rdsr(1) & 0x02 != 0:
|
|
self.flash_wrdi()
|
|
while (self.flash_rdsr(1) & 0x02) != 0:
|
|
pass
|
|
if erased < len(data):
|
|
progress.update(erased)
|
|
progress.finish()
|
|
print("Erase finished")
|
|
|
|
# program
|
|
# pad out to the nearest word length
|
|
if len(data) % 4 != 0:
|
|
data += bytearray([0xff] * (4 - (len(data) % 4)))
|
|
written = 0
|
|
progress = ProgressBar(min_value=0, max_value=len(data), prefix='Writing ').start()
|
|
while written < len(data):
|
|
self.ping_wdt()
|
|
if len(data) - written > 256:
|
|
chunklen = 256
|
|
else:
|
|
chunklen = len(data) - written
|
|
|
|
while True:
|
|
self.flash_wren()
|
|
status = self.flash_rdsr(1)
|
|
if status & 0x02 != 0:
|
|
break
|
|
|
|
self.burst_write(self.register('spinor_wdata'), data[written:(written+chunklen)])
|
|
self.flash_pp4b(addr + written, chunklen)
|
|
|
|
written += chunklen
|
|
if written < len(data):
|
|
progress.update(written)
|
|
progress.finish()
|
|
print("Write finished")
|
|
|
|
if self.flash_rdsr(1) & 0x02 != 0:
|
|
self.flash_wrdi()
|
|
while (self.flash_rdsr(1) & 0x02) != 0:
|
|
pass
|
|
|
|
# dummy reads to clear the "read lock" bit
|
|
self.flash_rdsr(0)
|
|
|
|
# verify
|
|
self.ping_wdt()
|
|
if verify:
|
|
print("Performing readback for verification...")
|
|
self.ping_wdt()
|
|
rbk_data = self.burst_read(addr + flash_region, len(data))
|
|
if rbk_data != data:
|
|
errs = 0
|
|
err_thresh = 64
|
|
for i in range(0, len(rbk_data)):
|
|
if rbk_data[i] != data[i]:
|
|
if errs < err_thresh:
|
|
print("Error at 0x{:x}: {:x}->{:x}".format(i, data[i], rbk_data[i]))
|
|
errs += 1
|
|
if errs == err_thresh:
|
|
print("Too many errors, stopping print...")
|
|
print("Errors were found in verification, programming failed")
|
|
print("Total byte errors: {}".format(errs))
|
|
exit(1)
|
|
else:
|
|
print("Verification passed.")
|
|
else:
|
|
print("Skipped verification at user request")
|
|
|
|
self.ping_wdt()
|
|
|
|
def auto_int(x):
|
|
return int(x, 0)
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Developer tool for loading local images to a Precursor. Use `precursorupdater` to fetch from a pre-built image.")
|
|
parser.add_argument(
|
|
"--soc", required=False, help="'Factory Reset' the SoC gateware. Note: this will overwrite any secret keys stored in your device!", type=str, nargs='?', metavar=('SoC gateware file'), const='../precursors/soc_csr.bin'
|
|
)
|
|
parser.add_argument(
|
|
"-s", "--staging", required=False, help="Stage an update to apply", type=str, nargs='?', metavar=('SoC gateware file'), const='../precursors/soc_csr.bin'
|
|
)
|
|
parser.add_argument(
|
|
"-l", "--loader", required=False, help="Loader", type=str, nargs='?', metavar=('loader file'), const='../target/riscv32imac-unknown-xous-elf/release/loader.bin'
|
|
)
|
|
parser.add_argument(
|
|
"--disable-boot", required=False, action='store_true', help="Disable system boot (for use in multi-stage updates)"
|
|
)
|
|
parser.add_argument(
|
|
"--enable-boot-wipe", required=False, action='store_true', help="Re-enable system boot for factory reset. Requires both a loader (-l) and a soc (--soc) spec. Overwrites root keys."
|
|
)
|
|
parser.add_argument(
|
|
"--enable-boot-update", required=False, action='store_true', help="Re-enable system boot for updates. Requires both a loader (-l) and a staging (-s) spec. Stages SOC without overwriting root keys."
|
|
)
|
|
parser.add_argument(
|
|
"-k", "--kernel", required=False, help="Kernel", type=str, nargs='?', metavar=('kernel file'), const='../target/riscv32imac-unknown-xous-elf/release/xous.img'
|
|
)
|
|
parser.add_argument(
|
|
"-e", "--ec", required=False, help="EC gateware", type=str, nargs='?', metavar=('EC gateware package'), const='ec_fw.bin'
|
|
)
|
|
parser.add_argument(
|
|
"-w", "--wf200", required=False, help="WF200 firmware", type=str, nargs='?', metavar=('WF200 firmware package'), const='wf200_fw.bin'
|
|
)
|
|
parser.add_argument(
|
|
"--erase-pddb", help="Erase the PDDB area", action="store_true"
|
|
)
|
|
parser.add_argument(
|
|
"--audiotest", required=False, help="Test audio clip (must be 8kHz WAV)", type=str, nargs='?', metavar=('Test audio clip'), const="testaudio.wav"
|
|
)
|
|
parser.add_argument(
|
|
"--peek", required=False, help="Inspect an address", type=auto_int, metavar=('ADDR')
|
|
)
|
|
parser.add_argument(
|
|
"--poke", required=False, help="Write to an address", type=auto_int, nargs=2, metavar=('ADDR', 'DATA')
|
|
)
|
|
parser.add_argument(
|
|
"--check-poke", required=False, action='store_true', help="Read data before and after the poke"
|
|
)
|
|
parser.add_argument(
|
|
"--config", required=False, help="Print the descriptor", action='store_true'
|
|
)
|
|
parser.add_argument(
|
|
"-i", "--image", required=False, help="Manually specify an image and address. Offset is relative to bottom of flash.", type=str, nargs=2, metavar=('IMAGEFILE', 'ADDR')
|
|
)
|
|
parser.add_argument(
|
|
"--verify", help="Readback verification. May fail for large files due to WDT timeout.", default=False, action='store_true'
|
|
)
|
|
parser.add_argument(
|
|
"--force", help="Ignore gitrev version on SoC and try to burn an image anyways", action="store_true"
|
|
)
|
|
parser.add_argument(
|
|
"--bounce", help="cycle the device through a reset", action="store_true"
|
|
)
|
|
parser.add_argument(
|
|
"--factory-new", help="reset the entire image to mimic exactly what comes out of the factory, including temp files for testing. Warning: this will take a long time.", action="store_true"
|
|
)
|
|
parser.add_argument(
|
|
"--override-csr", required=False, help="CSR file to use instead of CSR values stored with the image. Used to recover in case of partial update of soc_csr.bin", type=str,
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if not len(sys.argv) > 1:
|
|
print("No arguments specified, doing nothing. Use --help for more information.")
|
|
print("If you are looking to fetch pre-built images and burn them, this is the wrong tool:")
|
|
print("Use `precursorupdater` instead (via `pip3 install precursorupdater`)")
|
|
exit(1)
|
|
|
|
dev = usb.core.find(idProduct=0x5bf0, idVendor=0x1209)
|
|
|
|
if dev is None:
|
|
raise ValueError('Precursor device not found')
|
|
|
|
dev.set_configuration()
|
|
if args.config:
|
|
cfg = dev.get_active_configuration()
|
|
print(cfg)
|
|
|
|
pc_usb = PrecursorUsb(dev)
|
|
|
|
if args.verify:
|
|
verify = True
|
|
else:
|
|
verify = False
|
|
|
|
if args.peek:
|
|
pc_usb.peek(args.peek, display=True)
|
|
# print(burst_read(dev, args.peek, 256).hex())
|
|
exit(0)
|
|
|
|
if args.poke:
|
|
addr, data = args.poke
|
|
pc_usb.poke(addr, data, check=args.check_poke, display=True)
|
|
# import os
|
|
# d = bytearray(os.urandom(8000))
|
|
# burst_write(dev, addr, d)
|
|
# r = burst_read(dev, addr, 8000)
|
|
# print(r.hex())
|
|
# if d != r:
|
|
# print("mismatch")
|
|
# else:
|
|
# print("match")
|
|
exit(0)
|
|
|
|
pc_usb.load_csrs(args.override_csr) # prime the CSR values
|
|
if "v0.8" in pc_usb.gitrev:
|
|
locs = {
|
|
"LOC_SOC" : [0x0000_0000, "soc_csr.bin"],
|
|
"LOC_STAGING": [0x0028_0000, "pass"],
|
|
"LOC_LOADER" : [0x0050_0000, "loader.bin"],
|
|
"LOC_KERNEL" : [0x0098_0000, "xous.img"],
|
|
"LOC_WF200" : [0x07F8_0000, "pass"],
|
|
"LOC_EC" : [0x07FC_E000, "pass"],
|
|
"LOC_AUDIO" : [0x0634_0000, "short_8khz.wav"],
|
|
"LEN_AUDIO" : [0x01C4_0000, "pass"],
|
|
"LOC_PDDB" : [0x0100_0000, "pass"],
|
|
}
|
|
elif "v0.9" in pc_usb.gitrev:
|
|
locs = {
|
|
"LOC_SOC" : [0x0000_0000, "soc_csr.bin"],
|
|
"LOC_STAGING": [0x0028_0000, "pass"],
|
|
"LOC_LOADER" : [0x0050_0000, "loader.bin"],
|
|
"LOC_KERNEL" : [0x0098_0000, "xous.img"],
|
|
"LOC_WF200" : [0x07F8_0000, "pass"],
|
|
"LOC_EC" : [0x07FC_E000, "pass"],
|
|
"LOC_AUDIO" : [0x0634_0000, "short_8khz.wav"],
|
|
"LEN_AUDIO" : [0x01C4_0000, "pass"],
|
|
"LOC_PDDB" : [0x01D8_0000, "pass"],
|
|
}
|
|
elif args.force == True:
|
|
# try the v0.9 offsets
|
|
locs = {
|
|
"LOC_SOC" : [0x00000000, "soc_csr.bin"],
|
|
"LOC_STAGING": [0x00280000, "pass"],
|
|
"LOC_LOADER" : [0x00500000, "loader.bin"],
|
|
"LOC_KERNEL" : [0x00980000, "xous.img"],
|
|
"LOC_WF200" : [0x07F80000, "pass"],
|
|
"LOC_EC" : [0x07FCE000, "pass"],
|
|
"LOC_AUDIO" : [0x06340000, "short_8khz.wav"],
|
|
"LEN_AUDIO" : [0x01C40000, "pass"],
|
|
"LOC_PDDB" : [0x01D80000, "pass"],
|
|
}
|
|
else:
|
|
print("SoC is from an unknow rev '{}', use --force to continue anyways with v0.9 firmware offsets".format(pc_usb.load_csrs()))
|
|
exit(1)
|
|
|
|
vexdbg_addr = int(pc_usb.regions['vexriscv_debug'][0], 0)
|
|
pc_usb.ping_wdt()
|
|
print("Halting CPU.")
|
|
pc_usb.poke(vexdbg_addr, 0x00020000)
|
|
|
|
if args.erase_pddb:
|
|
print("Erasing PDDB region")
|
|
pc_usb.erase_region(locs['LOC_PDDB'][0], locs['LOC_EC'][0] - locs['LOC_PDDB'][0])
|
|
|
|
if args.disable_boot:
|
|
print("Disabling boot")
|
|
pc_usb.erase_region(locs['LOC_LOADER'][0], 1024 * 256)
|
|
|
|
if args.enable_boot_wipe:
|
|
if args.loader == None:
|
|
print("Must provide both a loader and soc image")
|
|
if args.soc == None:
|
|
print("Must provide both a soc and loader image")
|
|
print("Enabling boot with {} and {}".format(args.loader, args.soc))
|
|
print("Programming loader image {}".format(args.loader))
|
|
with open(args.loader, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_LOADER'][0], image, verify=verify)
|
|
print("Programming SoC gateware".format(args.soc))
|
|
with open(args.soc, "rb") as f:
|
|
image = f.read()
|
|
if verify == True:
|
|
print("Note: SoC verification is not possible as readback is locked for security purposes")
|
|
pc_usb.flash_program(locs['LOC_SOC'][0], image, verify=False)
|
|
|
|
print("Erasing PDDB root structures")
|
|
pc_usb.erase_region(locs['LOC_PDDB'][0], 1024 * 1024)
|
|
|
|
print("Resuming CPU.")
|
|
pc_usb.poke(vexdbg_addr, 0x02000000)
|
|
|
|
print("Resetting SOC...")
|
|
try:
|
|
pc_usb.poke(pc_usb.register('reboot_soc_reset'), 0xac, display=False)
|
|
except usb.core.USBError:
|
|
pass # we expect an error because we reset the SOC and that includes the USB core
|
|
exit(0)
|
|
|
|
if args.enable_boot_update:
|
|
if args.loader == None:
|
|
print("Must provide both a loader and soc image")
|
|
if args.staging == None:
|
|
print("Must provide both a soc and loader image")
|
|
print("Enabling boot with {} and {}".format(args.loader, args.staging))
|
|
print("Programming loader image {}".format(args.loader))
|
|
with open(args.loader, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_LOADER'][0], image, verify=verify)
|
|
print("Staging SoC gateware".format(args.staging))
|
|
with open(args.staging, "rb") as f:
|
|
image = f.read()
|
|
if verify == True:
|
|
print("Note: staging area verification is not possible as readback is locked for security purposes")
|
|
pc_usb.flash_program(locs['LOC_STAGING'][0], image, verify=verify)
|
|
|
|
print("Resuming CPU.")
|
|
pc_usb.poke(vexdbg_addr, 0x02000000)
|
|
|
|
print("Resetting SOC...")
|
|
try:
|
|
pc_usb.poke(pc_usb.register('reboot_soc_reset'), 0xac, display=False)
|
|
except usb.core.USBError:
|
|
pass # we expect an error because we reset the SOC and that includes the USB core
|
|
exit(0)
|
|
|
|
if args.image:
|
|
image_file, addr_str = args.image
|
|
addr = int(addr_str, 0)
|
|
print("Burning manually specified image '{}' to address 0x{:08x} relative to bottom of FLASH".format(image_file, addr))
|
|
with open(image_file, "rb") as f:
|
|
image_data = f.read()
|
|
pc_usb.flash_program(addr, image_data, verify=verify)
|
|
|
|
if args.ec != None:
|
|
print("Staging EC firmware package '{}' in SOC memory space...".format(args.ec))
|
|
with open(args.ec, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_EC'][0], image, verify=verify)
|
|
|
|
if args.wf200 != None:
|
|
print("Staging WF200 firmware package '{}' in SOC memory space...".format(args.wf200))
|
|
with open(args.wf200, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_WF200'][0], image, verify=verify)
|
|
|
|
if args.staging != None:
|
|
print("Staging SoC gateware {}".format(args.soc))
|
|
with open(args.staging, "rb") as f:
|
|
image = f.read()
|
|
if verify == True:
|
|
print("Note: staging area verification is not possible as readback is locked for security purposes")
|
|
pc_usb.flash_program(locs['LOC_STAGING'][0], image, verify=verify)
|
|
|
|
if args.kernel != None:
|
|
print("Programming kernel image {}".format(args.kernel))
|
|
with open(args.kernel, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_KERNEL'][0], image, verify=verify)
|
|
|
|
if args.loader != None:
|
|
print("Programming loader image {}".format(args.loader))
|
|
with open(args.loader, "rb") as f:
|
|
image = f.read()
|
|
pc_usb.flash_program(locs['LOC_LOADER'][0], image, verify=verify)
|
|
|
|
if args.soc != None:
|
|
if args.force == True:
|
|
print("Programming SoC gateware {}".format(args.soc))
|
|
with open(args.soc, "rb") as f:
|
|
image = f.read()
|
|
if verify == True:
|
|
print("Note: SoC verification is not possible as readback is locked for security purposes")
|
|
pc_usb.flash_program(locs['LOC_SOC'][0], image, verify=False)
|
|
print("Erasing PDDB root structures")
|
|
pc_usb.erase_region(locs['LOC_PDDB'][0], 1024 * 1024)
|
|
else:
|
|
print("This will overwrite any secret keys in your device and erase PDDB keys. Continue? (y/n)")
|
|
confirm = input()
|
|
if len(confirm) > 0 and confirm.lower()[:1] == 'y':
|
|
print("Programming SoC gateware {}".format(args.soc))
|
|
with open(args.soc, "rb") as f:
|
|
image = f.read()
|
|
if verify == True:
|
|
print("Note: SoC verification is not possible as readback is locked for security purposes")
|
|
pc_usb.flash_program(locs['LOC_SOC'][0], image, verify=False)
|
|
print("Erasing PDDB root structures")
|
|
pc_usb.erase_region(locs['LOC_PDDB'][0], 1024 * 1024)
|
|
|
|
|
|
if args.audiotest != None:
|
|
print("Loading audio test clip {}".format(args.audiotest))
|
|
with open(args.audiotest, "rb") as f:
|
|
image = f.read()
|
|
if len(image) >= locs['LEN_AUDIO'][0]:
|
|
print("audio file is too long, aborting audio burn!")
|
|
else:
|
|
pc_usb.flash_program(locs['LOC_AUDIO'][0], image, verify=verify)
|
|
|
|
if args.factory_new:
|
|
base_url = "https://ci.betrusted.io/releases/v0.9.5/"
|
|
# erase the entire flash
|
|
pc_usb.erase_region(0, 0x800_0000)
|
|
# burn the gateware
|
|
for sections in locs.values():
|
|
if sections[1] != 'pass':
|
|
print('retrieving {}'.format(base_url + sections[1]))
|
|
with urllib.request.urlopen(base_url + sections[1]) as f:
|
|
print('burning at {:x}'.format(sections[0]))
|
|
image = f.read()
|
|
pc_usb.flash_program(sections[0], image, verify=False)
|
|
|
|
|
|
print("Resuming CPU.")
|
|
pc_usb.poke(vexdbg_addr, 0x02000000)
|
|
|
|
print("Resetting SOC...")
|
|
try:
|
|
pc_usb.poke(pc_usb.register('reboot_soc_reset'), 0xac, display=False)
|
|
except usb.core.USBError:
|
|
pass # we expect an error because we reset the SOC and that includes the USB core
|
|
|
|
# print("If you need to run more commands, please unplug and re-plug your device in, as the Precursor USB core was just reset")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
exit(0)
|