async def test_sflash(): dis.clear() dis.text(None, 18, 'Serial Flash') dis.show() from sflash import SF from ustruct import pack import ngu msize = 1024 * 1024 SF.chip_erase() for phase in [0, 1]: steps = 7 * 4 for i in range(steps): dis.progress_bar(i / steps) dis.show() await sleep_ms(250) if not SF.is_busy(): break assert not SF.is_busy() # "didn't finish" # leave chip blank if phase == 1: break buf = bytearray(32) for addr in range(0, msize, 1024): SF.read(addr, buf) assert set(buf) == {255} # "not blank" rnd = ngu.hash.sha256s(pack('I', addr)) SF.write(addr, rnd) SF.read(addr, buf) assert buf == rnd # "write failed" dis.progress_bar_show(addr / msize) # check no aliasing, also right size part for addr in range(0, msize, 1024): expect = ngu.hash.sha256s(pack('I', addr)) SF.read(addr, buf) assert buf == expect # "readback failed" dis.progress_bar_show(addr / msize)
async def erase(self): # must be used by caller before writing any bytes assert not self.readonly assert self.length == 0 # 'already wrote?' for i in range(0, self.max_size, blksize): SF.block_erase(self.start + i) if i and self.message: from glob import dis dis.progress_bar_show(i / self.max_size) # expect block erase to take up to 2 seconds while SF.is_busy(): await sleep_ms(50)
async def handle_upload(self, offset, total_size, data): from sflash import SF from glob import dis, hsm_active from utils import check_firmware_hdr from sigheader import FW_HEADER_OFFSET, FW_HEADER_SIZE, FW_HEADER_MAGIC # maintain a running SHA256 over what's received if offset == 0: self.file_checksum = sha256() self.is_fw_upgrade = False assert offset % 256 == 0, 'alignment' assert offset + len(data) <= total_size <= MAX_UPLOAD_LEN, 'long' if hsm_active: # additional restrictions in HSM mode assert offset + len(data) <= total_size <= MAX_TXN_LEN, 'psbt' if offset == 0: assert data[0:5] == b'psbt\xff', 'psbt' for pos in range(offset, offset + len(data), 256): if pos % 4096 == 0: # erase here dis.fullscreen("Receiving...", offset / total_size) SF.sector_erase(pos) # expect 10-22 ms delay here await sleep_ms(12) while SF.is_busy(): await sleep_ms(2) # write up to 256 bytes here = data[pos - offset:pos - offset + 256] self.file_checksum.update(here) # Very special case for firmware upgrades: intercept and modify # header contents on the fly, and also fail faster if wouldn't work # on this specific hardware. # - workaround: ckcc-protocol upgrade process understates the file # length and appends hdr, but that's kinda a bug, so support both is_trailer = (pos == (total_size - FW_HEADER_SIZE) or pos == total_size) if pos == (FW_HEADER_OFFSET & ~255): hdr = memoryview(here)[-128:] magic, = unpack_from('<I', hdr[0:4]) if magic == FW_HEADER_MAGIC: self.is_fw_upgrade = bytes(hdr) prob = check_firmware_hdr(hdr, total_size) if prob: raise ValueError(prob) if is_trailer and self.is_fw_upgrade: # expect the trailer to exactly match the original one assert len(here) == 128 # == FW_HEADER_SIZE hdr = memoryview(here)[-128:] assert hdr == self.is_fw_upgrade # indicates hacking # but don't write it, instead offer user a chance to abort from auth import authorize_upgrade authorize_upgrade(self.is_fw_upgrade, pos) # pretend we wrote it, so ckcc-protocol or whatever gives normal feedback return offset SF.write(pos, here) # full page write: 0.6 to 3ms while SF.is_busy(): await sleep_ms(1) if offset + len(data) >= total_size and not hsm_active: # probably done dis.progress_bar_show(1.0) ux.restore_menu() return offset
def wait_writable(self): # TODO: timeouts here while SF.is_busy(): pass
async def handle_upload(self, offset, total_size, data): from sflash import SF from glob import dis, hsm_active from utils import check_firmware_hdr from sigheader import FW_HEADER_OFFSET, FW_HEADER_SIZE # maintain a running SHA256 over what's received if offset == 0: self.file_checksum = sha256() assert offset % 256 == 0, 'alignment' assert offset + len(data) <= total_size <= MAX_UPLOAD_LEN, 'long' if hsm_active: # additional restrictions in HSM mode assert offset + len(data) <= total_size <= MAX_TXN_LEN, 'psbt' if offset == 0: assert data[0:5] == b'psbt\xff', 'psbt' for pos in range(offset, offset + len(data), 256): if pos % 4096 == 0: # erase here dis.fullscreen("Receiving...", offset / total_size) SF.sector_erase(pos) # expect 10-22 ms delay here await sleep_ms(12) while SF.is_busy(): await sleep_ms(2) # write up to 256 bytes here = data[pos - offset:pos - offset + 256] self.file_checksum.update(here) # Very special case for firmware upgrades: intercept and modify # header contents on the fly, and also fail faster if wouldn't work # on this specific hardware. # - workaround: ckcc-protocol upgrade process understates the file # length and appends hdr, but that's kinda a bug, so support both if (pos == (FW_HEADER_OFFSET & ~255) or pos == (total_size - FW_HEADER_SIZE) or pos == total_size): prob = check_firmware_hdr(memoryview(here)[-128:], None, bad_magic_ok=True) if prob: raise ValueError(prob) SF.write(pos, here) # full page write: 0.6 to 3ms while SF.is_busy(): await sleep_ms(1) if offset + len(data) >= total_size and not hsm_active: # probably done dis.progress_bar_show(1.0) ux.restore_menu() return offset