mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-13 07:48:30 +00:00
commita09100ab4aAuthor: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:55:55 2022 +0800 bump lockfile commit53172e1471Author: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:46:40 2022 +0800 bump verification pins commit36398810ddAuthor: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:24:36 2022 +0800 bump version numbers to absorb new syscalls commit062fa6178aMerge:ac4a854e3bcc955aAuthor: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:23:31 2022 +0800 Merge branch 'reply-and-receive-next-syscall' into main commitac4a854e49Merge:d1375343e4c298dbAuthor: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:22:25 2022 +0800 Merge branch 'main' of github.com:betrusted-io/xous-core into main commit3bcc955abaAuthor: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:21:43 2022 +0800 add test code that also benchmarks the new routines. commite4c298db40Merge:f71278b4e2bf7109Author: bunnie <bunnie@kosagi.com> Date: Fri Dec 30 15:16:13 2022 +0800 Merge pull request #289 from betrusted-io/reply-and-receive-next-syscall Reply and receive next syscall commite2bf710939Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:06:51 2022 +0800 services: xous-ticktimer: use reply_and_receive_next() Convert the ticktimer server to use the new reply_and_receive_next_legacy() call. This is an example of how to use this API for future projects. Signed-off-by: Sean Cross <sean@xobs.io> commit0e75b8e479Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:05:26 2022 +0800 test-spawn: rename `Message` to `MessageEnvelope` Signed-off-by: Sean Cross <sean@xobs.io> commit3218bde2b4Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:04:01 2022 +0800 kernel: implement reply_and_receive_next Implement `ReplyAndReceiveNext` as a syscall. This will reply to a message, pull the next message out of the queue, and then re-queue the server to run. This should make servers much more responsive. This patch has been tested on both hosted and native modes. Signed-off-by: Sean Cross <sean@xobs.io> commitd1375343c1Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 23:34:48 2022 +0800 update release notes commitf71278b422Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 22:04:55 2022 +0800 fix issue #109 - refactor key fill/scan to not crash after pruning After a prune, key fill/scan can crash because there is an assumption that data is always resident in the cache for a small key. This code handles the case where the small key data was evicted during a prune, and reads the data back in just for that key on demand. Furthermore, during a `sync` call, it will pull in the key data that corresponds to the page containing just the small key, allowing that page to be re-composed and replaced in memory. caching is used on the decryption of the small key page so the expensive bit of the call should only happen once per small key bank. tested with pddb-ci and manually in hosted mode. A portion of code was re-written to make it clear that only the length of allocated data is copied to/from disk and memory, instead of the reserved space as the previous code would indicate. The reserved space is guaranteed to be zero because the RAM-based page for composing the small key block is zero-ized prior to operations; left comments reminding me of this in the future so I don't accidentally optimize something out and cause data leakage between small keys because I'm allowing the reserved areas to contain stale data. commite25da3a720Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 22:04:15 2022 +0800 add implemenation of the `prune` debug call this allows us to trigger the cache prune manually, instead of in response to memory backpressure only commit549e67feadAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 22:03:19 2022 +0800 add a `prune` debug call to force a hard-prune of the pddb cache commite045574d39Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:06:51 2022 +0800 services: xous-ticktimer: use reply_and_receive_next() Convert the ticktimer server to use the new reply_and_receive_next_legacy() call. This is an example of how to use this API for future projects. Signed-off-by: Sean Cross <sean@xobs.io> commit23c17d7632Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:05:26 2022 +0800 test-spawn: rename `Message` to `MessageEnvelope` Signed-off-by: Sean Cross <sean@xobs.io> commitaae5f3737cAuthor: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:04:01 2022 +0800 kernel: implement reply_and_receive_next Implement `ReplyAndReceiveNext` as a syscall. This will reply to a message, pull the next message out of the queue, and then re-queue the server to run. This should make servers much more responsive. This patch has been tested on both hosted and native modes. Signed-off-by: Sean Cross <sean@xobs.io> commitab0c7d3b32Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:03:39 2022 +0800 kernel: server: check buf is some when returning mem Signed-off-by: Sean Cross <sean@xobs.io> commit46f340324dAuthor: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:02:57 2022 +0800 kernel: rename `Message` -> `MessageEnvelope` This syncs with the latest changes to `xous-rs`. Signed-off-by: Sean Cross <sean@xobs.io> commit911ddb60fcAuthor: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:01:27 2022 +0800 kernel: debug: fix klog on baremetal klog() refers to an outdated `.uart` struct, which has since been renamed to `.serial`. Signed-off-by: Sean Cross <sean@xobs.io> commitd88254b1b1Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:00:35 2022 +0800 xous: add reply_and_receive_next_legacy call This call is used to return values while simultaneously listening for new messages. The -legacy suffix is used to disambiguate scalar values, so we know whether to send Scalar1, Scalar2, or Scalar5 messages. Signed-off-by: Sean Cross <sean@xobs.io> commit64c88f9942Author: Sean Cross <sean@xobs.io> Date: Thu Dec 29 21:00:05 2022 +0800 xous: add Message type This type actually contains the message contents, and will be used to return data to the caller. Signed-off-by: Sean Cross <sean@xobs.io> commitd463f9563fAuthor: Sean Cross <sean@xobs.io> Date: Thu Dec 29 20:58:51 2022 +0800 xous: rename Message to MessageEnvelope This reflects the fact that up until now, the `Message` struct has merely been the envelope that surrounds the message. It does not contain any actual Message contents. Signed-off-by: Sean Cross <sean@xobs.io> commit49ce85f9a8Author: Sean Cross <sean@xobs.io> Date: Wed Jul 6 15:57:10 2022 +0800 ticktimer-server: prepare for using ReplyAndReceiveNext Misc cleanup prior to switching which call we use. Signed-off-by: Sean Cross <sean@xobs.io> commitbe56af6303Author: Sean Cross <sean@xobs.io> Date: Wed Jul 6 15:35:22 2022 +0800 xous: add wrapper for reply_and_receive_next This wrapper enables userspace programs to use the new ReplyAndReceiveNext syscall easily. Signed-off-by: Sean Cross <sean@xobs.io> commit36e6e6c30bAuthor: Sean Cross <sean@xobs.io> Date: Wed Jul 6 15:18:32 2022 +0800 xous: syscall: add ReplyAndReceiveNext call Signed-off-by: Sean Cross <sean@xobs.io> commit1e56598b10Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 00:53:32 2022 +0800 chore: fix some typos commit1291b0f00bAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 00:38:27 2022 +0800 add issue # to release note commit7206f24271Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 00:37:47 2022 +0800 update release notes commit2f30e1b50aAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 00:11:03 2022 +0800 implement #269 commit5370c333daAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 29 00:10:42 2022 +0800 add callback for monitoring basis lock/unlock events commit8d42feb051Author: bunnie <bunnie@kosagi.com> Date: Wed Dec 28 19:48:40 2022 +0800 increase heap for mtxcli; add memory usage reporting commitac1df054abAuthor: bunnie <bunnie@kosagi.com> Date: Mon Dec 26 13:52:44 2022 +0800 absorb 22-bit satp fix commit89500015a7Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 15 17:52:55 2022 +0800 try to fix regression from #284 still hitting cache coherence edge cases. expanding the flush set read by one more way. commit41ea0a37c5Merge:e422740db288d93aAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 15 14:36:40 2022 +0800 Merge branch 'main' of github.com:betrusted-io/xous-core into main commitb288d93abdMerge:8e1d0502d4527b82Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 15 14:00:10 2022 +0800 Merge pull request #284 from betrusted-io/use-thin-lto cargo: use thin-lto commitd4527b8227Author: Sean Cross <sean@xobs.io> Date: Thu Dec 15 12:26:00 2022 +0800 cargo: use thin-lto Use thin LTO for linking. This shouldn't have too big of an affect on the resulting binary size, and it fixes the build for 1.66.0: = note: rust-lld: error: undefined symbol: core::panicking::panic_str_nounwind::h9b449c0fe03c8dac >>> referenced by compiler_builtins.22a3dcb1-cgu.13 >>> compiler_builtins-b913bacb8fda1c55.compiler_builtins.22a3dcb1-cgu.13.rcgu.o:(compiler_builtins::math::libm::rem_pio2_large::rem_pio2_large::h3db6431f23a281c1) in archive root\lib\rustlib\riscv32imac-unknown-xous-elf\lib\libcompiler_builtins-b913bacb8fda1c55.rlib >>> referenced by compiler_builtins.22a3dcb1-cgu.13 >>> compiler_builtins-b913bacb8fda1c55.compiler_builtins.22a3dcb1-cgu.13.rcgu.o:(compiler_builtins::math::libm::rem_pio2_large::rem_pio2_large::h3db6431f23a281c1) in archive root\lib\rustlib\riscv32imac-unknown-xous-elf\lib\libcompiler_builtins-b913bacb8fda1c55.rlib Signed-off-by: Sean Cross <sean@xobs.io> commite422740d37Author: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 23:03:50 2022 +0800 chore: last one. really. commitad79ebf8baAuthor: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 23:02:09 2022 +0800 chore: more typos. no really, i can stop at any time. commitb1b6d46d27Author: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 22:56:37 2022 +0800 chore: more typos probably should not have tried to spell check this, now i'm wasting my time. commit283ea1de0dAuthor: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 22:49:25 2022 +0800 chore: fix more typos, add french dictionary to vscode workspace settings commit1d94968b2fAuthor: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 22:36:47 2022 +0800 chore: fix typos commite930d88563Author: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 01:42:00 2022 +0800 placeholder for 0.9.12 release notes commit8e1d05020dAuthor: bunnie <bunnie@kosagi.com> Date: Tue Dec 13 00:32:11 2022 +0800 add v0.9.11 to the restore.py URL list commitbd230f5bb0Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:51:27 2022 +0800 fix text to speech mode preferences setting never allow the audio system to be turned off for blind people, and disallow 0-volume setting as well. commit38bc254bb9Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:28:33 2022 +0800 fix missing ja/zh translation commit60d4482ce2Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:16:04 2022 +0800 bump lockfile commit56f0843229Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:08:28 2022 +0800 bump verifier numbers commit5370ce396dAuthor: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:07:20 2022 +0800 bump all revs due to SoC artifact update commitcac0e57cdaAuthor: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:06:31 2022 +0800 change default target commit04b441a234Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:06:19 2022 +0800 add 0.9.11 build artifact commit08cf863ff5Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 16:05:15 2022 +0800 update SoC artifacts for 0.9.11 build commitc32b5d5bacAuthor: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 15:31:15 2022 +0800 bump release notes commit80ce119c95Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 15:31:06 2022 +0800 fix RSSI calculations commitedf24d9fc2Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 14:11:19 2022 +0800 clean up on the wifi signal bars commiteb17edc4ecMerge:9a580070435a6ea2Author: bunnie <bunnie@kosagi.com> Date: Mon Dec 12 11:47:18 2022 +0800 Merge pull request #283 from gsora/feat/wifi-signal-bars feat: wifi signal bar commit9a580070a3Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 17:50:51 2022 +0800 revert threaded init question now that deface issue is fixed commitf9cdf31857Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 17:07:29 2022 +0800 bump release notes commit196ceb0d29Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 16:42:03 2022 +0800 fix: avoid thrashing when recomputing previously defaced canvases There was a dubious line that I never felt good about and today I found out why. By flipping every canvas to drawable before doing the intersection computation we also throw away the defacement state, so previously defaced canvases would lose their status bit, causing thrashing of the canvases anytime there is a re-computation of the canvas ordering. This fix caches if a canvas was already defaced, and if it turns out that an on-screen canvas should not be drawable but was already defaced, the setting is re-applied, preventing thrashing. commitdca043d387Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 16:41:53 2022 +0800 improve context swap debug messages commit8d8dd2bd2fAuthor: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 16:41:25 2022 +0800 fix typo in unwrap message commitf31afa5125Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 11 16:41:06 2022 +0800 remove unused opcode in shellchat; improve debug output commit6138b65d76Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 23:44:18 2022 +0800 update comments to reflect changes in CI infra commit399124f942Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 22:05:47 2022 +0800 fix hosted mode commit939e13950dAuthor: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 18:59:55 2022 +0800 comment out debug in rootkeys implementation section commit2097afc166Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 18:58:06 2022 +0800 add efuse burning flow to rootkeys currently only available for builds that have the `efuse` feature enabled commit19e16aea29Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 18:56:59 2022 +0800 fix jtag fuse burning & add diagnostic messages - msb/lsb swap on efuse bank fixed - hazardous-debug feauter gates added - dry-run for "testing only" added commit4ec866e1d7Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 18:56:12 2022 +0800 add diagnostic seal command commitf5c687a524Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 17:45:30 2022 +0800 clarify user prompt commit2b4e47384eAuthor: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 17:44:54 2022 +0800 add fused device support and warnings to precursorupdater commite42c4c7b33Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 16:39:14 2022 +0800 add warnings & support for fused devices burning a generic image to a fused device will surely lead to a brick, so, try to keep a user from shooting themselves in the foot. commitc2f7a63f97Author: bunnie <bunnie@kosagi.com> Date: Sat Dec 10 16:39:01 2022 +0800 remove some whitespace to allow for easier debugging commit1f89843122Author: bunnie <bunnie@kosagi.com> Date: Fri Dec 9 12:38:53 2022 +0800 defer init root keys question until status thread starts also defer the preference thread from loading until after the PDDB mount thread is started -- doesn't make sense to start it any earlier than that, since it depends on the PDDB being mounted. commit02f9701546Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 8 17:42:12 2022 +0800 make an explicit dvt feature to simplify flags commit4dd983bfc4Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 8 17:19:05 2022 +0800 adjust security parameters for dvt build commitfc08c3e29dAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 8 17:14:47 2022 +0800 add hazardous-debug to modals bip-39 for convenient key printout during debug. flag is normally off. commitdd82b7f32aAuthor: bunnie <bunnie@kosagi.com> Date: Thu Dec 8 17:14:21 2022 +0800 add no-codec mode to status this is for DVT builds where the codec isn't supported, needed for efuse testing commita5d341c7a6Author: bunnie <bunnie@kosagi.com> Date: Thu Dec 8 14:09:36 2022 +0800 add a bitstream encryption/recovery tool for fused devices This is useful for recovering bricked devices where the backup key is available. This tool can be used to generate an FPGA image for a device that has been permanently fused to a non-0 backup key. commit4f270f54beAuthor: bunnie <bunnie@kosagi.com> Date: Wed Dec 7 22:24:56 2022 +0800 revert debugging on status opcodes commit435a6ea2bdAuthor: Gianguido Sorà <me@gsora.xyz> Date: Wed Dec 7 14:28:07 2022 +0100 feat: wifi signal bar This commit draws a Wi-Fi signal bar with 5 bars (lowest to highest signal), to the right of SSID name. Signal power is calculated from a lowest of -90dBm to a maximum of -30dBm, by increments of 13dBm. commitf84a9d10c7Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 4 18:13:37 2022 +0800 add placeholder for the efuse flow in the rootkey service commit8208a86c08Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 4 18:12:30 2022 +0800 add the UX front-end for backup key burning - instructions for BBRAM flow (refers to wiki) - consent for efuse flow - fix a blocking issue on backup prep (minor problem, just gives users more time to contemplate without a panic) commit3838f3bc15Author: bunnie <bunnie@kosagi.com> Date: Sun Dec 4 18:12:05 2022 +0800 add a pause for context swaps on menu release commit53c9af1033Author: bunnie <bunnie@kosagi.com> Date: Fri Dec 2 23:00:38 2022 +0800 bump readme's
952 lines
38 KiB
Python
952 lines
38 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
|
|
import re
|
|
|
|
from progressbar.bar import ProgressBar
|
|
from bip_utils import (
|
|
Bip39MnemonicValidator, Bip39MnemonicDecoder
|
|
)
|
|
from Crypto.Cipher import AES
|
|
from Crypto.Hash import SHA256
|
|
from Crypto.Random import get_random_bytes
|
|
|
|
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 bitflip(data_block, bitwidth=32):
|
|
if bitwidth == 0:
|
|
return data_block
|
|
bytewidth = bitwidth // 8
|
|
bitswapped = bytearray()
|
|
i = 0
|
|
while i < len(data_block):
|
|
data = int.from_bytes(data_block[i:i+bytewidth], byteorder='big', signed=False)
|
|
b = '{:0{width}b}'.format(data, width=bitwidth)
|
|
bitswapped.extend(int(b[::-1], 2).to_bytes(bytewidth, byteorder='big'))
|
|
i = i + bytewidth
|
|
return bytes(bitswapped)
|
|
|
|
# assumes a, b are the same length eh?
|
|
def xor_bytes(a, b):
|
|
i = 0
|
|
y = bytearray()
|
|
while i < len(a):
|
|
y.extend((a[i] ^ b[i]).to_bytes(1, byteorder='little'))
|
|
i = i + 1
|
|
|
|
return bytes(y)
|
|
|
|
def try_key_to_bytes(input):
|
|
if len(input.split(' ')) == 24: # 24 words is BIP-39
|
|
# Get if a mnemonic is valid with automatic language detection, return bool
|
|
assert(Bip39MnemonicValidator().IsValid(input))
|
|
# Like before with automatic language detection
|
|
key_bytes = Bip39MnemonicDecoder().Decode(input)
|
|
else:
|
|
key_bytes = int(input, 16).to_bytes(32, byteorder='big')
|
|
return key_bytes
|
|
|
|
# binfile should be the input SoC file, already read in as bytes()
|
|
# returns the encrypted version of binfile
|
|
def encrypt_to_efuse(binfile, key):
|
|
print("Encrypting gateware to target-specific key...")
|
|
# extract the keys
|
|
key_bytes = bytes([0] * 32)
|
|
new_key = try_key_to_bytes(key)
|
|
new_hmac = get_random_bytes(32)
|
|
new_iv = get_random_bytes(16)
|
|
|
|
# search for structure
|
|
# 0x3001_6004 -> specifies the CBC key
|
|
# 4 words of CBC IV
|
|
# 0x3003_4001 -> ciphertext len
|
|
# 1 word of ciphertext len
|
|
# then ciphertext
|
|
|
|
position = 0
|
|
iv_pos = 0
|
|
while position < len(binfile):
|
|
cwd = int.from_bytes(binfile[position:position+4], 'big')
|
|
if cwd == 0x3001_6004:
|
|
iv_pos = position+4
|
|
if cwd == 0x3003_4001:
|
|
break
|
|
position = position + 1
|
|
|
|
position = position + 4
|
|
|
|
ciphertext_len = 4* int.from_bytes(binfile[position:position+4], 'big')
|
|
position = position + 4
|
|
|
|
active_area = binfile[position : position+ciphertext_len]
|
|
postamble = binfile[position+ciphertext_len:]
|
|
|
|
iv_bytes = bitflip(binfile[iv_pos : iv_pos+0x10]) # note that the IV is embedded in the file
|
|
|
|
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
|
|
plain_bitstream = cipher.decrypt(bitflip(active_area))
|
|
|
|
# now construct the output file and its hashes
|
|
plaintext = bytearray()
|
|
f = bytearray()
|
|
|
|
# fixed header that sets 66MHz config speed, x1, 1.8V, eFuse target
|
|
device_header = [
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
|
0xaa, 0x99, 0x55, 0x66, 0x20, 0x00, 0x00, 0x00, 0x30, 0x03, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x0b,
|
|
0x30, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x12, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0xc0, 0x01,
|
|
0x80, 0x00, 0x00, 0x40, 0x30, 0x00, 0xa0, 0x01, 0x80, 0x00, 0x00, 0x40, 0x30, 0x01, 0xc0, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x01, 0x60, 0x04,
|
|
]
|
|
|
|
for item in device_header: # add the cleartext header
|
|
f.extend(bytes([item]))
|
|
|
|
f.extend(bitflip(new_iv)) # insert the IV
|
|
|
|
ciphertext_header = [
|
|
0x30, 0x03, 0x40, 0x01, 0x00, 0x08, 0x5b, 0x98,
|
|
]
|
|
for item in ciphertext_header: # add the cleartext length-of-ciphertext field before the ciphertext
|
|
f.extend(bytes([item]))
|
|
|
|
# generate the header and footer hash keys.
|
|
header = int(0x6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C6C).to_bytes(32, byteorder='big')
|
|
keyed_header = xor_bytes(header, new_hmac)
|
|
footer = int(0x3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A3A).to_bytes(32, byteorder='big')
|
|
keyed_footer = xor_bytes(footer, new_hmac)
|
|
|
|
# add the header
|
|
plaintext.extend(keyed_header)
|
|
plaintext.extend(header)
|
|
|
|
# insert the bitstream plaintext, skipping the header and the trailing HMAC.
|
|
plaintext.extend(plain_bitstream[64:-160])
|
|
|
|
# compute first HMAC of stream with new HMAC key
|
|
h1 = SHA256.new()
|
|
k = 0
|
|
while k < len(plaintext) - 320: # HMAC does /not/ cover the whole file, it stops 320 bytes short of the end
|
|
h1.update(bitflip(plaintext[k:k+16], 32))
|
|
k = k + 16
|
|
h1_digest = h1.digest()
|
|
|
|
# add the footer
|
|
plaintext.extend(keyed_footer)
|
|
plaintext.extend(footer)
|
|
plaintext.extend(bytes(32)) # empty spot where hash #1 would be stored
|
|
hash_pad = [ # sha-256 padding for the zero'd hash #1, which is in the bitstream and seems necessary for verification
|
|
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
|
|
]
|
|
plaintext.extend(hash_pad)
|
|
|
|
# compute the hash of the hash, presumably to prevent length extension attacks?
|
|
h2 = SHA256.new()
|
|
h2.update(bitflip(keyed_footer))
|
|
h2.update(bitflip(footer))
|
|
h2.update(h1_digest)
|
|
h2_digest = h2.digest()
|
|
|
|
# commit the final HMAC to the bitstream plaintext
|
|
plaintext.extend(bitflip(h2_digest))
|
|
|
|
# encrypt the bitstream
|
|
newcipher = AES.new(new_key, AES.MODE_CBC, new_iv)
|
|
|
|
# finally generate the ciphertext block, which encapsulates the HMACs
|
|
ciphertext = newcipher.encrypt(bytes(plaintext))
|
|
|
|
# add ciphertext to the bitstream
|
|
f.extend(bitflip(ciphertext))
|
|
|
|
# add the cleartext postamble to the bitstream. These are a series of NOP commands + all of the csr.csv data & signatures
|
|
f.extend(postamble)
|
|
print("Encryption success! {} bytes generated.".format(len(f)))
|
|
assert len(f) == 2621440, "Encryption length is incorrect; aborting!"
|
|
|
|
return f
|
|
|
|
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,
|
|
)
|
|
parser.add_argument(
|
|
"--dump", required=False, help="Dump a region of memory. Takes a virtual memory map file as an argument. Looks for lines of format `V|P vaddr paddr`", type=str,
|
|
)
|
|
parser.add_argument(
|
|
"--dump-file", required=False, help="Name out output file for dump. Required if --dump is specified.", type=str,
|
|
)
|
|
parser.add_argument(
|
|
"--key", help="Backup key in hex or BIP-39 format. Used to factory-reset efused devices. Specify BIP-39 phrase within double-quotes.", 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 unknown 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()
|
|
|
|
if args.dump:
|
|
table = []
|
|
with open(args.dump, 'r') as map:
|
|
for line in map.readlines():
|
|
rgx = re.search('.*V\|P\s([0-9a-fA-F]*)\s([0-9a-fA-F]*).*', line)
|
|
if rgx is not None:
|
|
map = rgx.groups()
|
|
table += [int(map[1], 16)]
|
|
|
|
if args.dump_file is None:
|
|
print("Must specify --dump-file when using --dump")
|
|
exit(0)
|
|
with open(args.dump_file, 'wb') as f:
|
|
for page in table:
|
|
pc_usb.poke(vexdbg_addr, 0x00020000)
|
|
data = pc_usb.burst_read(page, 4096)
|
|
pc_usb.poke(vexdbg_addr, 0x02000000)
|
|
f.write(data)
|
|
exit(0)
|
|
|
|
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("WARNING: if a backup key is set, the correct key MUST be specified with `--key`, or else the device will be bricked.")
|
|
print("Continue? (y/n)")
|
|
confirm = input()
|
|
if len(confirm) > 0 and confirm.lower()[:1] == 'y':
|
|
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 args.key is not None:
|
|
image = encrypt_to_efuse(image, args.key)
|
|
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("`--force` specified; all checks disabled, you may brick your device!")
|
|
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.")
|
|
print("WARNING: if a backup key is set, the correct key MUST be specified with `--key`, or else the device will be bricked.")
|
|
print("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 args.key is not None:
|
|
image = encrypt_to_efuse(image, args.key)
|
|
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:
|
|
print("WARNING: if a backup key is set, the correct key MUST be specified with `--key`, or else the device will be bricked.")
|
|
print("Continue? (y/n)")
|
|
confirm = input()
|
|
if len(confirm) > 0 and confirm.lower()[:1] == 'y':
|
|
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()
|
|
if sections[1] == "soc_csr.bin" and args.key is not None:
|
|
image = encrypt_to_efuse(image, args.key)
|
|
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)
|