Virtdisk working better

This commit is contained in:
Peter D. Gray 2021-11-05 10:47:24 -04:00
parent 132f1d31c2
commit 6cc0483c21
No known key found for this signature in database
GPG Key ID: F0E6CC6AFC16CF7B
62 changed files with 783 additions and 776 deletions

View File

@ -273,9 +273,11 @@ Here's what the warning screen looks like:
- Dev signs binary release with private "zero key" published in our Github
- Give firmware binary file to users (via web download probably)
- They upgrade via normal process (copy to MicroSD, or USB upgrade)
- On first reboot, big "unauthorized firmware" warning is shown, with delay
- On first reboot, big "unauthorized firmware" warning is shown, with delay.
- If they know the main PIN (since they are the owner), they follow process to set green light
- Next reboot and following, as long as "genuine" mode is maintained, they boot without warnings
- Next reboot and following, as long as "genuine" mode is maintained, they boot without
warnings (Mk3 and earlier)
- Mk4 will always alert on boot-up when running code not approved by Coinkite.
### Benefits

2
external/libngu vendored

@ -1 +1 @@
Subproject commit 606381cacd20fe908c20f095294e3de5dd2dc6c5
Subproject commit 174f6c983eb3a0579294d753799b450fa9a6b007

@ -1 +1 @@
Subproject commit 5917fc199c7e1dd2ef7f9ada8bc48abe0488e9f3
Subproject commit 778d3e461607b646592ce7606a14fc7164616d09

View File

@ -1,3 +1,7 @@
#
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
font_files = {
'small': 'assets/zevv-peep-iso8859-15-07x14.bdf',
'large': 'assets/zevv-peep-iso8859-15-10x20.bdf',
@ -5,6 +9,7 @@ font_files = {
}
# test with:
#
# ./build.py build --portable && ./testit.py --msg "hello→world←\n↳this\n•Bullet\n•Text" -f small
#
special_chars = dict(small=[

View File

@ -1,3 +1,10 @@
## 5.0.0 - Nov ??, 2021
Mk4 - New hardware
- (mk3&4) Performance improved: some internal objects cached to reduce delays when
accessing master secret. Helps address explorer, many USB commands and signing.
## 4.1.4 - Sep ??, 2021
- Enhancement: if an XFP of zero is seen in a PSBT file, assume that should be replaced by

View File

@ -10,7 +10,7 @@ from ux import ux_enter_number
from utils import imported, pretty_short_delay, problem_file_line
import uasyncio
from uasyncio import sleep_ms
from files import CardSlot, CardMissingError
from files import CardSlot, CardMissingError, needs_microsd
from utils import xfp2str
from glob import settings
from pincodes import pa
@ -32,10 +32,6 @@ async def start_selftest(*args):
settings.save()
async def needs_microsd():
# Standard msg shown if no SD card detected when we need one.
return await ux_show_story("Please insert a MicroSD card before attempting this operation.")
async def needs_primary():
# Standard msg shown if action can't be done w/o main PIN
await ux_show_story("Only the holder of the main PIN (not the secondary) can perform this function. Please start over with the main PIN.")
@ -142,6 +138,7 @@ async def maybe_dev_menu(*a):
async def dev_enable_vcp(*a):
# Enable USB serial port emulation, for devs.
# Mk3 and earlier only.
#
from usb import is_vcp_active
@ -164,6 +161,7 @@ The USB virtual serial port has now been enabled. Use a real computer to connect
async def dev_enable_disk(*a):
# Enable disk emulation, which allows them to change code.
# Mk3 and earlier only.
#
cur = pyb.usb_mode()
@ -182,6 +180,7 @@ Keep tmp files and other junk out!""")
async def dev_enable_protocol(*a):
# Turn off disk emulation. Keep VCP enabled, since they are still devs.
# Mk3 and earlier
cur = pyb.usb_mode()
if cur and 'HID' in cur:
await ux_show_story('Coldcard USB protocol is already enabled (HID mode)')
@ -272,7 +271,7 @@ async def microsd_upgrade(*a):
pos += here
else:
# just read it to where we want it
dest = PSRAM.write(0, size)
dest = SF.write(0, size)
fp.readinto(dest)
if failed:
@ -837,8 +836,9 @@ async def start_login_sequence():
IMPT.start_task('idle', idle_logout())
# Do green-light set immediately after firmware upgrade
if not pa.is_secondary:
if version.is_fresh_version() and version.mk_num <=3:
# - mk4 doesn't work this way, light will already be green
if version.mk_num <= 3:
if version.is_fresh_version() and not pa.is_secondary:
pa.greenlight_firmware()
dis.show()
@ -870,16 +870,21 @@ async def start_login_sequence():
await ar.interact()
except: pass
if version.mk_num >= 4:
if version.has_nfc and settings.get('nfc', 0):
# Maybe allow NFC now
import nfc
nfc.NFCHandler.startup()
if settings.get('vdsk', 0):
# Maybe start virtual disk
import vdisk
vdisk.VirtDisk()
# Allow USB protocol, now that we are auth'ed
if not settings.get('du', 0):
from usb import enable_usb
enable_usb()
if version.mk_num >= 4 and version.has_nfc:
# Maybe allow NFC now
if settings.get('nfc', 0):
import nfc
nfc.NFCHandler.startup()
def goto_top_menu():
# Start/restart menu system
@ -1223,14 +1228,22 @@ async def restore_everything_cleartext(*A):
async def wipe_filesystem(*A):
if not await ux_confirm('''\
Erase internal filesystem and rebuild it. Resets contents of internal flash area \
used for code patches. Does not affect funds, or seed words but may reset settings \
used with other BIP39 passphrases. \
Does not affect SD card, if any.'''):
used for settings and HSM config file. Does not affect funds, or seed words but \
will reset settings used with other BIP39 passphrases. \
Does not affect MicroSD card, if any.'''):
return
from files import wipe_flash_filesystem
wipe_flash_filesystem()
async def wipe_vdisk(*A):
if not await ux_confirm('''\
Erases and reformats shared RAM disk. This is a secure erase that blanks every byte.'''):
return
import glob
await glob.VD.wipe_disk()
async def wipe_sd_card(*A):
if not await ux_confirm('''\
Erases and reformats MicroSD card. This is not a secure erase but more of a quick format.'''):
@ -1272,8 +1285,8 @@ async def list_files(*A):
ch = await ux_show_story('''SHA256(%s)\n\n%s\n\nPress 6 to delete.''' % (basename, B2A(chk.digest())), escape='6')
if ch == '6':
from files import securely_blank_file
securely_blank_file(fn)
with CardSlot() as card:
card.securely_blank_file(fn)
return
@ -1408,15 +1421,11 @@ async def bless_flash(*a):
async def ready2sign(*a):
# Top menu choice of top menu! Signing!
# - check if any signable in SD card, if so do it
# - if nothing, then talk about USB connection
# - if no card, check virtual disk for PSBT
# - if still nothing, then talk about USB connection
from public_constants import MAX_TXN_LEN
import stash
from glob import NFC
if stash.bip39_passphrase:
title = '[%s]' % settings.get('xfp')
else:
title = None
def is_psbt(filename):
if '-signed' in filename.lower():
@ -1435,6 +1444,11 @@ async def ready2sign(*a):
# just check if we have candidates, no UI
choices = await file_picker(None, suffix='psbt', min_size=50,
max_size=MAX_TXN_LEN, taster=is_psbt)
if stash.bip39_passphrase:
title = '[%s]' % settings.get('xfp')
else:
title = None
if not choices:
msg = '''Coldcard is ready to sign spending transactions!
@ -1923,9 +1937,24 @@ async def change_nfc_enable(enable):
nfc.NFCHandler.startup()
async def change_virtdisk_enable(enable):
print("vdisk: %d" % enable)
pass
import glob, vdisk
from usb import enable_usb, disable_usb
if bool(enable) == bool(glob.VD):
# not a change in state, do nothing
print("vdisk: no change")
return
print("vdisk: %d" % enable)
if enable:
# just showing up as new media is enough (MacOS) to make it show up
vdisk.VirtDisk()
assert glob.VD
else:
assert glob.VD
glob.VD.shutdown()
assert not glob.VD
async def change_which_chain(name):
# setting already changed, but reflect that value in other settings

View File

@ -349,8 +349,7 @@ def generate_address_csv(path, addr_fmt, ms_wallet, account_num, n, start=0):
async def make_address_summary_file(path, addr_fmt, ms_wallet, account_num, count=250):
# write addresses into a text file on the MicroSD
from glob import dis
from files import CardSlot, CardMissingError
from actions import needs_microsd
from files import CardSlot, CardMissingError, needs_microsd
# simple: always set number of addresses.
# - takes 60 seconds to write 250 addresses on actual hardware

View File

@ -734,9 +734,10 @@ def psbt_encoding_taster(taste, psbt_len):
return decoder, output_encoder, psbt_len
def sign_psbt_file(filename):
async def sign_psbt_file(filename, force_vdisk=False):
# sign a PSBT file found on a MicroSD card
from files import CardSlot, CardMissingError, securely_blank_file
# - or from VirtualDisk (mk4)
from files import CardSlot, CardMissingError
from glob import dis
from sram2 import tmp_buf
@ -744,11 +745,10 @@ def sign_psbt_file(filename):
#print("sign: %s" % filename)
# copy file into our spiflash
# - can't work in-place on the card because we want to support writing out to different card
# - accepts hex or base64 encoding, but binary prefered
with CardSlot() as card:
with CardSlot(force_vdisk, readonly=True) as card:
with open(filename, 'rb') as fd:
dis.fullscreen('Reading...')
@ -814,7 +814,7 @@ def sign_psbt_file(filename):
for path in [orig_path, None]:
try:
with CardSlot() as card:
with CardSlot(force_vdisk, readonly=True) as card:
out_full, out_fn = card.pick_filename(target_fname, path)
out_path = path
if out_full: break
@ -828,7 +828,7 @@ def sign_psbt_file(filename):
else:
# attempt write-out
try:
with CardSlot() as card:
with CardSlot(force_vdisk) as card:
if is_comp and del_after:
# don't write signed PSBT if we'd just delete it anyway
out_fn = None
@ -853,13 +853,13 @@ def sign_psbt_file(filename):
txid+'.txn', out_path, overwrite=True)
os.rename(out2_full, after_full)
if del_after:
# this can do nothing if they swapped SDCard between steps, which is ok,
# but if the original file is still there, this blows it away.
# - if not yet final, the foo-part.psbt file stays
try:
securely_blank_file(filename)
except: pass
if del_after:
# this can do nothing if they swapped SDCard between steps, which is ok,
# but if the original file is still there, this blows it away.
# - if not yet final, the foo-part.psbt file stays
try:
card.securely_blank_file(filename)
except: pass
# success and done!
break
@ -869,6 +869,10 @@ def sign_psbt_file(filename):
sys.print_exception(exc)
# fall thru to try again
if force_vdisk:
await ux_show_story(prob, title='Error')
return
# prompt them to input another card?
ch = await ux_show_story(prob+"Please insert an SDCard to receive signed transaction, "
"and press OK.", title="Need Card")

View File

@ -323,8 +323,7 @@ async def verify_backup_file(fname_or_fd):
# read 7z header, and measure checksums
# - no password is wanted/required
# - really just checking CRC32, but that's enough against truncated files
from files import CardSlot, CardMissingError
from actions import needs_microsd
from files import CardSlot, CardMissingError, needs_microsd
prob = None
fd = None
@ -381,8 +380,7 @@ async def restore_complete_doit(fname_or_fd, words, file_cleanup=None):
# - some errors will be shown, None return in that case
# - no return if successful (due to reboot)
from glob import dis
from files import CardSlot, CardMissingError
from actions import needs_microsd
from files import CardSlot, CardMissingError, needs_microsd
# build password
password = ' '.join(words)

View File

@ -1,30 +1,9 @@
# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# dev_helper.py - Debug and similar code.
# dev_helper.py - Debug code, not shipped.
#
import ckcc, pyb
from uasyncio import sleep_ms
async def monitor_usb():
# Provide a helpful message when virtual serial port is connected.
# Without this, they have to press enter or something to see they are connected.
from usb import is_vcp_active
u = pyb.USB_VCP()
was_connected = u.isconnected()
while 1:
await sleep_ms(100)
conn = u.isconnected()
if conn and not was_connected:
# this direct write bypasses vcp_lockdown code.
if is_vcp_active():
u.write(b"\r\nWelcome to Coldcard! Press ^C to stop GUI and enter REPL.\r\n")
else:
u.write(b"\r\nColdcard developer features disabled.\r\n")
was_connected = conn
async def usb_keypad_emu():
# Take keypresses on USB virtual serial port (when not in REPL mode)
@ -79,4 +58,8 @@ async def usb_keypad_emu():
numpad.inject(k)
continue
def setup():
from imptask import IMPT
IMPT.start_task('usb_keypad_emu', usb_keypad_emu())
# EOF

View File

@ -118,8 +118,7 @@ be needed for different systems.
async def write_text_file(fname_pattern, body, title, total_parts=72):
# - total_parts does need not be precise
from glob import dis
from files import CardSlot, CardMissingError
from actions import needs_microsd
from files import CardSlot, CardMissingError, needs_microsd
# choose a filename
try:
@ -412,8 +411,7 @@ async def make_json_wallet(label, generator, fname_pattern='new-wallet.json'):
# Record **public** values and helpful data into a JSON file
from glob import dis
from files import CardSlot, CardMissingError
from actions import needs_microsd
from files import CardSlot, CardMissingError, needs_microsd
dis.fullscreen('Generating...')

View File

@ -2,9 +2,17 @@
#
# files.py - MicroSD and related functions.
#
import pyb, ckcc, os, sys, utime
import pyb, ckcc, os, sys, utime, glob
from uerrno import ENOENT
async def needs_microsd():
# Standard msg shown if no SD card detected when we need one.
return await ux_show_story("Please insert a MicroSD card before attempting this operation.")
def _is_ejected():
sd = pyb.SDCard()
return not sd.present()
def _try_microsd(bad_fs_ok=False):
# Power up, mount the SD card, return False if we can't for some reason.
#
@ -95,7 +103,9 @@ def wipe_microsd_card():
sd = pyb.SDCard()
assert sd
if not sd.present(): return
if not sd.present():
return
# power cycle so card details (like size) are re-read from current card
sd.power(0)
@ -196,10 +206,17 @@ class CardSlot:
from machine import Pin
cls.active_led = Pin('SD_ACTIVE', Pin.OUT)
def __init__(self):
self.active = False
def __init__(self, force_vdisk=False, readonly=False):
self.mountpt = None
self.force_vdisk = force_vdisk
self.readonly = readonly
def __enter__(self):
# Mk4: maybe use our virtual disk in preference to SD Card
if glob.VD and (_is_ejected() or self.force_vdisk):
self.mountpt = glob.VD.mount(self.readonly)
return self
# Get ready!
self.active_led.on()
@ -214,25 +231,29 @@ class CardSlot:
ok = _try_microsd()
if not ok:
self.recover()
self._recover()
raise CardMissingError
self.active = True
self.mountpt = '/sd'
return self
def __exit__(self, *a):
self.recover()
if self.mountpt == '/sd':
self._recover()
else:
glob.VD.unmount()
self.mountpt = None
return False
def recover(self):
def _recover(self):
# done using the microSD -- unpower it
self.active_led.off()
self.active = False
try:
assert self.mountpt == '/sd'
os.umount('/sd')
except: pass
@ -249,9 +270,9 @@ class CardSlot:
def get_paths(self):
# (full) paths to check on the card
root = self.get_sd_root()
return [root]
#root = self.get_sd_root()
#return [root]
return [self.mountpt]
def get_id_hash(self):
# hash over card config and serial # details
@ -276,10 +297,10 @@ class CardSlot:
# - no UI here please
import ure
assert self.active # used out of context mgr
assert self.mountpt # used out of context mgr
# prefer SD card if we can
path = path or (self.get_sd_root() + '/')
# put it back where we found it
path = path or (self.mountpt + '/')
assert '/' not in pattern
assert '.' in pattern
@ -314,19 +335,18 @@ class CardSlot:
return fname, fname[len(path):]
def securely_blank_file(full_path):
# input PSBT file no longer required; so delete it
# - blank with zeros
# - rename to garbage (to hide filename after undelete)
# - delete
# - ok if file missing already (card maybe have been swapped)
#
# NOTE: we know the FAT filesystem code is simple, see
# ../external/micropython/extmod/vfs_fat.[ch]
def securely_blank_file(self, full_path):
# input PSBT file no longer required; so delete it
# - blank with zeros
# - rename to garbage (to hide filename after undelete)
# - delete
# - ok if file missing already (card maybe have been swapped)
#
# NOTE: we know the FAT filesystem code is simple, see
# ../external/micropython/extmod/vfs_fat.[ch]
path, basename = full_path.rsplit('/', 1)
path, basename = full_path.rsplit('/', 1)
with CardSlot() as card:
try:
blk = bytes(64)

View File

@ -77,6 +77,8 @@ def has_secrets():
def nfc_enabled():
from glob import NFC
return bool(NFC)
def vdisk_enabled():
return bool(settings.get('vdsk', 0))
HWTogglesMenu = [
ToggleMenuItem('USB Port', 'du', ['Default On', 'Disable USB'], invert=True,
@ -155,7 +157,8 @@ SDCardMenu = [
MenuItem('Clone Coldcard', predicate=has_secrets, f=clone_write_data),
MenuItem('List Files', f=list_files),
MenuItem('NFC File Share', predicate=nfc_enabled, f=nfc_share_file),
MenuItem('Format Card', f=wipe_sd_card),
MenuItem('Format SD Card', f=wipe_sd_card),
MenuItem('Format RAM Disk', predicate=vdisk_enabled, f=wipe_vdisk),
]
UpgradeMenu = [
@ -165,15 +168,26 @@ UpgradeMenu = [
MenuItem('Bless Firmware', f=bless_flash),
]
DevelopersMenu = [
# xxxxxxxxxxxxxxxx
MenuItem("Normal USB Mode", f=dev_enable_protocol),
MenuItem("Enable USB REPL", f=dev_enable_vcp),
MenuItem("Enable USB Disk", f=dev_enable_disk),
MenuItem("Wipe Patch Area", f=wipe_filesystem),
MenuItem('Warm Reset', f=reset_self),
MenuItem("Restore Txt Bkup", f=restore_everything_cleartext),
]
if version.mk_num < 4:
DevelopersMenu = [
# xxxxxxxxxxxxxxxx
MenuItem("Normal USB Mode", f=dev_enable_protocol),
MenuItem("Enable USB REPL", f=dev_enable_vcp),
MenuItem("Enable USB Disk", f=dev_enable_disk),
MenuItem("Wipe Patch Area", f=wipe_filesystem), # needs better label
MenuItem('Warm Reset', f=reset_self),
MenuItem("Restore Txt Bkup", f=restore_everything_cleartext),
]
else:
# Mk4 and later
from mk4 import dev_enable_repl
DevelopersMenu = [
# xxxxxxxxxxxxxxxx
MenuItem("Serial REPL", f=dev_enable_repl),
MenuItem("Wipe LFS", f=wipe_filesystem), # kills settings, HSM stuff
MenuItem('Warm Reset', f=reset_self),
MenuItem("Restore Txt Bkup", f=restore_everything_cleartext),
]
AdvancedVirginMenu = [ # No PIN, no secrets yet (factory fresh)
# xxxxxxxxxxxxxxxx
@ -221,7 +235,6 @@ DangerZoneMenu = [
MenuItem("Debug Functions", menu=DebugFunctionsMenu), # actually harmless
MenuItem("Seed Functions", menu=SeedFunctionsMenu),
MenuItem("I Am Developer.", menu=maybe_dev_menu),
MenuItem("Wipe Patch Area", f=wipe_filesystem), # needs better label
MenuItem('Perform Selftest', f=start_selftest), # little harmful
MenuItem("Set High-Water", f=set_highwater),
MenuItem('Wipe HSM Policy', f=wipe_hsm_policy, predicate=hsm_policy_available),

View File

@ -20,6 +20,9 @@ settings = None
# PSRAM (on Mk4 only)
PSRAM = None
# Virtual Disk (Mk4)
VD = None
# NFC interface (Mk4, and can be disabled)
NFC = None

View File

@ -6,7 +6,7 @@ import sys, uasyncio, ckcc
def die_with_debug(exc):
try:
is_debug = ckcc.vcp_enabled(None)
is_debug = ckcc.vcp_enabled(None) or ckcc.is_debug_build()
except:
# robustness
is_debug = False

View File

@ -16,21 +16,19 @@ assert not glob.dis, "main reimport"
# this makes the GC run when larger objects are free in an attempt to reduce fragmentation.
gc.threshold(4096)
if 1:
if 0:
# useful for debug: keep this stub!
import ckcc
ckcc.vcp_enabled(True)
from h import *
#pyb.usb_mode('VCP+MSC') # handy but annoying disk issues
#pyb.usb_mode('VCP')
if 0:
raise SystemExit
print("---\nColdcard Wallet from Coinkite Inc. (c) 2018-2021.\n")
print("---\nColdcard Wallet from Coinkite Inc. (c) 2018-2021.")
from version import get_mpy_version
datestamp,vers,_ = get_mpy_version()
print("Version: " + vers + " / " + datestamp)
import version
datestamp,vers,_ = version.get_mpy_version()
print("Version: %s / %s\n" % (vers, datestamp))
# Setup OLED and get something onto it.
from display import Display
@ -39,7 +37,7 @@ dis.splash()
glob.dis = dis
# slowish imports, some with side-effects
import version, ckcc, uasyncio
import ckcc, uasyncio
if version.mk_num == 4:
# early setup code needed on Mk4
@ -57,18 +55,6 @@ else:
# Serial Flash memory
from sflash import SF
if version.is_devmode:
# For devs only: allow code in this directory to overide compiled-in stuff. Dangerous!
# - using relative paths here so works better on simulator
# - you must boot w/ non-production-signed firmware to get here
sys.path.insert(0, 'flash/lib')
# Give external devs a way to start stuff early
try:
import boot2
except: pass
# Setup membrane numpad (mark 2+)
from mempad import MembraneNumpad
numpad = MembraneNumpad()
@ -85,11 +71,6 @@ async def more_setup():
# MAYBE: check if we're a brick and die again? Or show msg?
try:
# Some background "tasks"
#
from dev_helper import monitor_usb
IMPT.start_task('vcp', monitor_usb())
from files import CardSlot
CardSlot.setup()
@ -104,7 +85,7 @@ async def more_setup():
print("factory mode")
# in factory mode, turn on USB early to allow debug/setup
from usb import enable_usb
enable_usb()
enable_usb(version.has_psram)
# always start the self test.
if not settings.get('tested', False):
@ -153,9 +134,15 @@ def go():
die_with_debug(exc)
if version.is_devmode:
# Give external devs a way to start semi-early.
# Start some debug-only code.
try:
import main2
import dev_helper
dev_helper.setup()
except: pass
# Simulator code
try:
import sim_quickstart
except: pass
uasyncio.create_task(more_setup())

View File

@ -19,7 +19,6 @@ freeze_as_mpy('', [
'files.py',
'flow.py',
'glob.py',
'h.py',
'history.py',
'hsm.py',
'hsm_ux.py',
@ -61,3 +60,13 @@ freeze_as_mpy('', [
'zevvpeep.py',
'public_constants.py',
], opt=3)
# Maybe include test code.
import os
if int(os.environ.get('DEBUG_BUILD', 0)):
freeze_as_mpy('', [
'h.py',
'dev_helper.py',
'usb_test_commands.py',
], opt=0)

View File

@ -8,4 +8,3 @@ freeze_as_mpy('', [
'trick_pins.py',
'graphics_mk4.py',
], opt=0)

View File

@ -15,16 +15,11 @@ def make_flash_fs():
os.mount(fl, '/flash')
open('/flash/README.txt', 'wt').write("LFS Virt disk")
os.mkdir('/flash/lib')
os.mkdir('/flash/settings')
def make_psram_fs():
# ALWAYS remake this, because PSRAM does not forget old state during quick
# resets and such.
print("Mount /psram")
# Lower level code has wiped and created filesystem already, but
# add some more files?
# Filesystem is wiped and rebuild on each boot before this point, but
# add some more files.
ps = ckcc.PSRAM()
os.mount(ps, '/psram')
@ -32,14 +27,14 @@ def make_psram_fs():
open('/psram/README.txt', 'wt').write('''
COLDCARD Virtual Disk
1) copy your PSBT file to be signed here.
2) select from Coldcard menu, approve transaction.
3) signed transaction file(s) will be created here.
1) copy your PSBT file here.
2) select from Coldcard menu & approve transaction.
3) signed transaction file(s) will be saved here.
'''.replace('\n', '\r\n'))
date, ver, *_ = version.get_mpy_version()
open('/psram/version.txt', 'wt').write('\r\n'.join([ver, date, '']))
open('/psram/ident/version.txt', 'wt').write('\r\n'.join([ver, date, '']))
# generally, leave it unmounted
os.umount('/psram')
@ -56,9 +51,15 @@ def init0():
except BaseException as exc:
sys.print_exception(exc)
# create singleton for Virtual Disk
import vdisk
vdisk.VirtDisk()
assert glob.VD
async def dev_enable_repl(*a):
# Enable serial port connection. You'll have to break case open.
from ux import ux_show_story
# allow REPL access
ckcc.vcp_enabled(True)
print("REPL enabled.")
await ux_show_story("""\
The serial port has now been enabled.\n\n3.3v TTL on Tx/Rx/Gnd pads @ 115,200 bps.""")
# EOF

View File

@ -6,11 +6,10 @@ import stash, chains, ustruct, ure, uio, sys, ngu
#from ubinascii import hexlify as b2a_hex
from utils import xfp2str, str2xfp, swab32, cleanup_deriv_path, keypath_to_str, str_to_keypath
from ux import ux_show_story, ux_confirm, ux_dramatic_pause, ux_clear_keys, ux_enter_number
from files import CardSlot, CardMissingError
from files import CardSlot, CardMissingError, needs_microsd
from public_constants import AF_P2SH, AF_P2WSH_P2SH, AF_P2WSH, AFC_SCRIPT, MAX_PATH_DEPTH
from menu import MenuSystem, MenuItem
from opcodes import OP_CHECKMULTISIG
from actions import needs_microsd
from exceptions import FatalPSBTIssue
from glob import settings

View File

@ -183,10 +183,14 @@ class NFCHandler:
print("NFC: first time")
self.write_config1(I2C_CFG, 0x3a)
utime.sleep_ms(10) # required
# set to no RF when first powered up (so CC is quiet when system unpowered)
# - side-effect: sets rf to sleep now too
self.write_config1(RF_MNGT, 2)
utime.sleep_ms(10) # might be needed?
# XXX locking stuff?
def setup(self):
@ -505,10 +509,4 @@ class NFCHandler:
else:
raise ValueError(ctype)
def presence_check():
# Does NFC hardware exist on this board?
# SDA/SCL will be tied low
from machine import Pin
return Pin('NFC_SDA', mode=Pin.IN).value() or Pin('NFC_SCL', mode=Pin.IN).value()
# EOF

View File

@ -4,9 +4,8 @@
# paper.py - generate paper wallets, based on random values (not linked to wallet)
#
from utils import imported
from actions import needs_microsd
from ux import ux_show_story, ux_dramatic_pause
from files import CardSlot, CardMissingError
from files import CardSlot, CardMissingError, needs_microsd
from actions import file_picker
from menu import MenuSystem, MenuItem

View File

@ -3,7 +3,7 @@
# pwsave.py - Save bip39 passphrases into encrypted file on MicroSD (if desired)
#
import sys, stash, ujson, os, ngu
from files import CardSlot, CardMissingError
from files import CardSlot, CardMissingError, needs_microsd
class PassphraseSaver:
# Encrypts BIP-39 passphrase very carefully, and appends
@ -46,7 +46,6 @@ class PassphraseSaver:
# encrypt and save; always appends.
from ux import ux_dramatic_pause
from glob import dis
from actions import needs_microsd
while 1:
dis.fullscreen('Saving...')

View File

@ -461,16 +461,20 @@ def set_seed_value(words=None, encoded=None):
nv = encoded
from glob import dis
dis.fullscreen('Applying...')
pa.change(new_secret=nv)
try:
dis.fullscreen('Applying...')
dis.busy_bar(True)
pa.change(new_secret=nv)
# re-read settings since key is now different
# - also captures xfp, xpub at this point
pa.new_main_secret(nv)
# re-read settings since key is now different
# - also captures xfp, xpub at this point
pa.new_main_secret(nv)
# check and reload secret
pa.reset()
pa.login()
# check and reload secret
pa.reset()
pa.login()
finally:
dis.busy_bar(False)
def set_bip39_passphrase(pw):
# apply bip39 passphrase for now (volatile)
@ -520,22 +524,28 @@ async def remember_bip39_passphrase():
def clear_seed():
from glob import dis
import utime
import utime, callgate
dis.fullscreen('Clearing...')
dis.busy_bar(True)
# clear settings associated with this key, since it will be no more
settings.blank()
# save a blank secret (all zeros is a special case, detected by bootloader)
nv = bytes(AE_SECRET_LEN)
pa.change(new_secret=nv)
if version.mk_num >= 4:
callgate.fast_wipe(True)
# NOT REACHED
else:
# save a blank secret (all zeros is a special case, detected by bootloader)
nv = bytes(AE_SECRET_LEN)
pa.change(new_secret=nv)
if version.has_608:
# wipe the long secret too
nv = bytes(AE_LONG_SECRET_LEN)
pa.ls_change(nv)
if version.has_608:
# wipe the long secret too
nv = bytes(AE_LONG_SECRET_LEN)
pa.ls_change(nv)
dis.busy_bar(False)
dis.fullscreen('Reboot...')
utime.sleep(1)

View File

@ -159,10 +159,10 @@ class SensitiveValues:
self.__class__._cache_used = utime.ticks_ms()
else:
if self._cache_secret:
# they are using new BIP39 passphrase; we already have raw secret
# they are using new BIP39 passphrase but we already have raw secret
self.secret = bytearray(self._cache_secret)
else:
# slow, read from secure element(s)
# slow: read from secure element(s)
self.secret = pa.fetch()
# slow: do bip39 key stretching (typically)
@ -322,6 +322,7 @@ class SensitiveValues:
def duress_root(self):
# Return a bip32 node for the duress wallet linked to this wallet.
# 0x80000000 - 0xCC10 = 2147431408
# Obsoleted in Mk4: use BIP-85 instead
p = "m/2147431408'/0'/0'"
dirty = self.derive_path(p)

View File

@ -51,7 +51,7 @@ hid_descp = bytes([
HSM_WHITELIST = frozenset({
'logo', 'ping', 'vers', # harmless/boring
'upld', 'sha2', 'dwld', 'stxn', # up/download/sign PSBT needed
'mitm','ncry', # maybe limited by policy tho
'mitm', 'ncry', # maybe limited by policy tho
'smsg', # limited by policy
'blkc', 'hsts', # report status values
'stok', 'smok', # completion check: sign txn or msg
@ -67,6 +67,7 @@ handler = None
def enable_usb():
# We can't change it on the fly; must be disabled before here
# - only one combo of subclasses can be used during a single power-up cycle
cur = pyb.usb_mode()
if cur:
print("USB already enabled: %s" % cur)
@ -307,7 +308,7 @@ class USBHandler:
except:
raise FramingError('decode')
if cmd[0].isupper() and (is_simulator() or is_devmode):
if cmd[0].isupper() and is_devmode:
# special hacky commands to support testing w/ the simulator
try:
from usb_test_commands import do_usb_command

View File

@ -1,14 +1,10 @@
# items imported here may be useful to EVAL and EXEC commands, which tests depend on.
# items imported here may be useful to EVAL and EXEC commands, which test cases depend on.
import uio, sys, version, nvstore, glob
try:
from sflash import SF
except: pass
from glob import *
# Mk4:
# copy this file to PSRAMdisk
# a=open('/psram/usb_test_commands.py', 'rb').read()
# open('/flash/lib/usb_test_commands.py', 'wb').write(a)
from glob import *
def do_usb_command(cmd, args):
# TESTING commands!

View File

@ -3,41 +3,97 @@
# vdisk.py - Share a virtual RAM disk with a USB host.
#
#
import os, sys, pyb, ckcc, version, glob, uasyncio
import os, sys, pyb, ckcc, version, glob, uasyncio, utime
from sigheader import FW_MIN_LENGTH
from public_constants import MAX_UPLOAD_LEN
from glob import settings
from usb import enable_usb, disable_usb
from uasyncio import sleep_ms
# block device implemented on half the PSRAM
VBLKDEV = ckcc.PSRAM()
VD = ckcc.PSRAM()
MAX_PSRAM_FILE = const(2<<20) # 2 megs
MIN_QUIET_TIME = 250 # (ms) delay after host writes disk, before we look at it.
def _host_done_cb(_psram):
# back into the singleton
glob.VD.host_done_handler()
# get back into the singleton
assert glob.VD
if glob.VD:
glob.VD.host_done_handler()
class VirtDisk:
def __init__(self):
VD.callback(_host_done_cb)
self.contents = self.sample()
self.ignore = set()
# Feature is enabled, altho USB might be off.
print("vdisk: init")
glob.VD = self
self.ignore = set()
self.contents = self.sample()
VBLKDEV.callback(_host_done_cb)
VBLKDEV.set_inserted(True)
print("vdisk: started")
def shutdown(self):
# we've been disabled, stop
print("vdisk: shutdown")
VBLKDEV.set_inserted(False)
VBLKDEV.callback(None)
glob.VD = None
def unmount(self):
# just unmount; ignore errors
try:
os.umount('/vdisk')
enable_usb()
except:
pass
# allow host to change again
if glob.VD:
VBLKDEV.set_inserted(True)
def mount(self, readonly=False):
# Prepare to read the filesystem. Block host. Return mount pt.
for _ in range(10):
# wait until it's been idle for a little bit
host = VBLKDEV.get_time()
if utime.ticks_diff(utime.ticks_ms(), host) > MIN_QUIET_TIME:
break
utime.sleep_ms(MIN_QUIET_TIME//5)
else:
print("busy disk?")
try:
if not readonly:
disable_usb()
VBLKDEV.set_inserted(False)
os.mount(VBLKDEV, '/vdisk', readonly=readonly)
st = os.statvfs('/vdisk')
return '/vdisk'
except OSError as exc:
# corrupt or unformated?
# XXX incomlpete error handling here; needs work
VBLKDEV.set_inserted(True)
sys.print_exception(exc)
return None
def sample(self):
# Peek at the contents of the disk right now
# - only root directory
# - only files, and capture their sizes
try:
os.mount(VD, '/tmp', readonly=True)
os.mount(VBLKDEV, '/vdisk', readonly=True)
return list(sorted((fn, sz) for (fn,ty,_,sz) in os.ilistdir('/tmp') if ty == 0x8000))
return list(sorted((fn, sz) for (fn,ty,_,sz) in os.ilistdir('/vdisk') if ty == 0x8000))
except BaseException as exc:
sys.print_exception(exc)
return []
finally:
os.umount('/tmp')
os.umount('/vdisk')
def import_file(self, filename, sz):
# copy file into another area of PSRAM where rest of system can use it
@ -45,50 +101,87 @@ class VirtDisk:
# I could not resist doing this in C... since we already have the
# data in memory, why mess around with file concepts?
actual = VD.copy_file(0, filename)
actual = VBLKDEV.copy_file(0, filename)
assert actual == sz
return actual
def new_psbt(self, filename, sz):
# New incoming PSBT has been detected, start to sign it.
print("new PSBT: " + filename)
from auth import sign_psbt_file
uasyncio.create_task(sign_psbt_file('/vdisk/'+filename, force_vdisk=True))
def new_firmware(self, filename, sz):
# potential new firmware file detected
# - copy to start of PSRAM, begin upgrade confirm
self.import_file(filename, sz)
uasyncio.create_task(psram_upgrade(filename, sz))
def host_done_handler(self):
print('host-wrote')
if settings.get('vdsk', 0) != 2:
# auto mode not enabled, so ignore changes
return
now = self.sample()
if now == self.contents:
# no-op change, common, ignore
# - timestamp changes, hidden files, MacOS BS, etc.
print('no file change')
return
# clear ignored items once they are deleted
self.ignore.intersection_update(fn for fn,_ in now)
self.contents = now
# Look for files we want to taste; assume they have
# been fully written-out because we are called after a
# fairly long timeout
print(repr(now))
print('New files? %r' % now)
for fn, sz in now:
if fn in self.ignore:
continue
if fn[0] == '.':
continue
if sz >= MAX_PSRAM_FILE:
print("%s: too big" % fn)
self.ignore.add(fn)
continue
lfn = fn.lower()
if lfn.endswith('.psbt') and sz > 100:
self.ignore.add(fn)
self.new_psbt(fn, sz)
break
if lfn.endswith('.dfu') and sz > FW_MIN_LENGTH:
self.ignore.add(fn) # in case they decline it
self.new_firmware(fn, sz)
break
async def wipe_disk(self):
# Reformat. Near instant.
from glob import dis
from mk4 import make_psram_fs
dis.fullscreen('Formatting...')
dis.progress_bar_show(0.1)
disable_usb()
VBLKDEV.wipe()
make_psram_fs()
enable_usb()
await sleep_ms(50)
dis.progress_bar_show(1)
await sleep_ms(250)
async def psram_upgrade(filename, size):
@ -109,16 +202,13 @@ async def psram_upgrade(filename, size):
# pull out firmware header
hdr = PSRAM.read_at(offset+FW_HEADER_OFFSET, FW_HEADER_SIZE)
if filename == 'dev.dfu' and version.is_devmode:
if filename == 'dev.dfu':
# skip the checking and display for us devs and "just do it"
# - the bootrom still does the checks, you just can't see useful errors
from pincodes import pa
if pa.is_successful():
print("dev.dfu being installed")
pa.firmware_upgrade(offset, size)
else:
# can't do this before login
print("need PIN")
assert pa.is_successful()
print("dev.dfu being installed")
pa.firmware_upgrade(offset, size)
return
# get user buy-in and approval of the change.

View File

@ -49,17 +49,25 @@ def get_header_value(fld_name):
return ustruct.unpack_from(FWH_PY_FORMAT, hdr)[idx]
def nfc_presence_check():
# Does NFC hardware exist on this board?
# SDA/SCL will be tied low
from machine import Pin
return Pin('NFC_SDA', mode=Pin.IN).value() or Pin('NFC_SCL', mode=Pin.IN).value()
def get_is_devmode():
# what firmware signing key did we boot with? are we in dev mode?
if mk_num == 4:
# mk4 we are built differently
import ckcc
return ckcc.is_debug_build()
from sigheader import RAM_HEADER_BASE, FWH_PK_NUM_OFFSET
import stm
if mk_num == 4:
# mk4 we use flash header
kn = get_header_value('pubkey_num')
else:
# Important? Use the RAM version of this, not flash version!
kn = stm.mem32[RAM_HEADER_BASE + FWH_PK_NUM_OFFSET]
# Important? Use the RAM version of this, not flash version!
kn = stm.mem32[RAM_HEADER_BASE + FWH_PK_NUM_OFFSET]
# For now, all keys are "production" except number zero, which will be made public
# - some other keys may be de-authorized and so on in the future
@ -123,9 +131,7 @@ def probe_system():
has_psram = True
has_se2 = True
mk_num = 4
from nfc import presence_check
has_nfc = presence_check()
has_nfc = nfc_presence_check()
else:
# mark 2
has_608 = callgate.has_608()

View File

@ -1,7 +1,7 @@
/*
* (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC.
* (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
*
* based on ports/stm32/mpconfigport.h
* based on ports/stm32/mpconfigport.h but overrides only
*
*/
/*
@ -37,342 +37,10 @@
#include "mpconfigboard.h"
#include "mpconfigboard_common.h"
#if 0
// Adding a wrapper to this function, see modckcc.c
#include <stdbool.h>
#undef MICROPY_HW_BDEV_WRITEBLOCK
#define MICROPY_HW_BDEV_WRITEBLOCK CKCC_flash_bdev_writeblock
extern bool CKCC_flash_bdev_writeblock(const uint8_t *src, uint32_t block);
#endif
// memory allocation policies
#define MICROPY_ALLOC_PATH_MAX (128)
// emitters
#define MICROPY_PERSISTENT_CODE_LOAD (1)
#ifndef MICROPY_EMIT_THUMB
#define MICROPY_EMIT_THUMB (1)
#endif
#ifndef MICROPY_EMIT_INLINE_THUMB
#define MICROPY_EMIT_INLINE_THUMB (1)
#endif
// compiler configuration
#define MICROPY_COMP_MODULE_CONST (1)
#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1)
#define MICROPY_COMP_RETURN_IF_EXPR (1)
// optimisations
#define MICROPY_OPT_COMPUTED_GOTO (1)
#define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (0)
#define MICROPY_OPT_MPZ_BITWISE (1)
// Python internal features
#define MICROPY_READER_VFS (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_ENABLE_FINALISER (1)
#define MICROPY_STACK_CHECK (1)
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE (32)
#define MICROPY_KBD_EXCEPTION (1)
#define MICROPY_HELPER_REPL (1)
#define MICROPY_REPL_EMACS_KEYS (1)
#define MICROPY_REPL_AUTO_INDENT (1)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_ENABLE_SOURCE_LINE (1)
#ifndef MICROPY_FLOAT_IMPL // can be configured by each board via mpconfigboard.mk
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#endif
#define MICROPY_STREAMS_NON_BLOCK (1)
#define MICROPY_MODULE_WEAK_LINKS (1)
#define MICROPY_CAN_OVERRIDE_BUILTINS (1)
#define MICROPY_USE_INTERNAL_ERRNO (1)
#define MICROPY_ENABLE_SCHEDULER (1)
#define MICROPY_SCHEDULER_DEPTH (8)
//?//#define MICROPY_VFS (1)
//?//#define MICROPY_VFS_FAT (1)
// control over Python builtins
#define MICROPY_PY_FUNCTION_ATTRS (1)
#define MICROPY_PY_BUILTINS_STR_UNICODE (1)
#define MICROPY_PY_BUILTINS_STR_CENTER (1)
#define MICROPY_PY_BUILTINS_STR_PARTITION (1)
#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1)
#define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
#define MICROPY_PY_BUILTINS_FROZENSET (1)
#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1)
#define MICROPY_PY_ALL_SPECIAL_METHODS (1)
#define MICROPY_PY_BUILTINS_COMPILE (1)
#define MICROPY_PY_BUILTINS_EXECFILE (1)
#define MICROPY_PY_BUILTINS_INPUT (1)
#define MICROPY_PY_BUILTINS_POW3 (1)
#define MICROPY_PY_BUILTINS_HELP (0)
#define MICROPY_PY_BUILTINS_HELP_TEXT __unused__
#define MICROPY_PY_BUILTINS_HELP_MODULES (0)
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1)
#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1)
#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (0)
#define MICROPY_PY_CMATH (0)
#define MICROPY_PY_IO (1)
#define MICROPY_PY_IO_IOBASE (1)
#define MICROPY_PY_IO_FILEIO (1)
#define MICROPY_PY_SYS_MAXSIZE (1)
#define MICROPY_PY_SYS_EXIT (1)
#define MICROPY_PY_SYS_STDFILES (1)
#define MICROPY_PY_SYS_STDIO_BUFFER (1)
#ifndef MICROPY_PY_SYS_PLATFORM // let boards override it if they want
#define MICROPY_PY_SYS_PLATFORM "pyboard"
#endif
#define MICROPY_PY_UERRNO (1)
#ifndef MICROPY_PY_THREAD
#define MICROPY_PY_THREAD (0)
#endif
// NOTE: also remove modules from MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS below
// extended modules
#define MICROPY_PY_UCTYPES (1)
#define MICROPY_PY_UZLIB (1)
#define MICROPY_PY_UJSON (1)
#define MICROPY_PY_URE (1)
#define MICROPY_PY_UHEAPQ (1)
#define MICROPY_PY_UHASHLIB (1)
#define MICROPY_PY_UBINASCII (1)
// PDG: not wanted; we have better random stuff
#define MICROPY_PY_URANDOM (0)
#define MICROPY_PY_URANDOM_EXTRA_FUNCS (0)
#define MICROPY_PY_UTIMEQ (1)
#define MICROPY_PY_UTIME_MP_HAL (1)
#define MICROPY_PY_OS_DUPTERM (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_PULSE (0)
#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new
// one i2c: #2 for SE2
#define MICROPY_PY_MACHINE_I2C (1)
#define MICROPY_PY_MACHINE_I2C_MAKE_NEW machine_hard_i2c_make_new
#define MICROPY_PY_MACHINE_SPI (1)
#define MICROPY_PY_MACHINE_SPI_MSB (SPI_FIRSTBIT_MSB)
#define MICROPY_PY_MACHINE_SPI_LSB (SPI_FIRSTBIT_LSB)
#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hard_spi_make_new
#define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0)
#define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (HAL_RCC_GetSysClockFreq() / 48)
// Required
#define MICROPY_PY_FRAMEBUF (1)
#define MICROPY_PY_USELECT (1)
// PDG: not wanted
#define MICROPY_PY_USOCKET (0)
#define MICROPY_PY_NETWORK (0)
// fatfs configuration used in ffconf.h
#define MICROPY_FATFS_ENABLE_LFN (1)
#define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */
#define MICROPY_FATFS_USE_LABEL (1)
#define MICROPY_FATFS_RPATH (2)
#define MICROPY_FATFS_MULTI_PARTITION (1)
// TODO these should be generic, not bound to fatfs
#define mp_type_fileio mp_type_vfs_fat_fileio
#define mp_type_textio mp_type_vfs_fat_textio
// use vfs's functions for import stat and builtin open
#define mp_import_stat mp_vfs_import_stat
#define mp_builtin_open mp_vfs_open
#define mp_builtin_open_obj mp_vfs_open_obj
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
// extra built in modules to add to the list of known ones
extern const struct _mp_obj_module_t machine_module;
extern const struct _mp_obj_module_t pyb_module;
extern const struct _mp_obj_module_t stm_module;
extern const struct _mp_obj_module_t mp_module_ubinascii;
extern const struct _mp_obj_module_t mp_module_ure;
extern const struct _mp_obj_module_t mp_module_uzlib;
extern const struct _mp_obj_module_t mp_module_ujson;
extern const struct _mp_obj_module_t mp_module_uheapq;
extern const struct _mp_obj_module_t mp_module_uhashlib;
extern const struct _mp_obj_module_t mp_module_uos;
extern const struct _mp_obj_module_t mp_module_utime;
extern const struct _mp_obj_module_t mp_module_usocket;
extern const struct _mp_obj_module_t mp_module_network;
extern const struct _mp_obj_module_t ckcc_module;
extern const struct _mp_obj_module_t mp_module_tcc;
extern const struct _mp_obj_module_t mp_module_uqr;
#if MICROPY_PY_USOCKET
#define SOCKET_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_usocket), MP_ROM_PTR(&mp_module_usocket) },
#define SOCKET_BUILTIN_MODULE_WEAK_LINKS { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&mp_module_usocket) },
#else
#define SOCKET_BUILTIN_MODULE
#define SOCKET_BUILTIN_MODULE_WEAK_LINKS
#endif
#if MICROPY_PY_NETWORK
#define NETWORK_BUILTIN_MODULE { MP_ROM_QSTR(MP_QSTR_network), MP_ROM_PTR(&mp_module_network) },
#else
#define NETWORK_BUILTIN_MODULE
#endif
#define MICROPY_PORT_BUILTIN_MODULES \
{ MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \
{ MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \
{ MP_ROM_QSTR(MP_QSTR_stm), MP_ROM_PTR(&stm_module) }, \
{ MP_ROM_QSTR(MP_QSTR_uos), MP_ROM_PTR(&mp_module_uos) }, \
{ MP_ROM_QSTR(MP_QSTR_utime), MP_ROM_PTR(&mp_module_utime) }, \
SOCKET_BUILTIN_MODULE \
NETWORK_BUILTIN_MODULE \
{ MP_ROM_QSTR(MP_QSTR_ckcc), MP_ROM_PTR(&ckcc_module) }, \
{ MP_ROM_QSTR(MP_QSTR_tcc), MP_ROM_PTR(&mp_module_tcc) }, \
{ MP_ROM_QSTR(MP_QSTR_uqr), MP_ROM_PTR(&mp_module_uqr) }, \
// PDG: don't love this, but it makes "import os" work as "import uos"
#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \
{ MP_ROM_QSTR(MP_QSTR_binascii), MP_ROM_PTR(&mp_module_ubinascii) }, \
{ MP_ROM_QSTR(MP_QSTR_collections), MP_ROM_PTR(&mp_module_collections) }, \
{ MP_ROM_QSTR(MP_QSTR_re), MP_ROM_PTR(&mp_module_ure) }, \
{ MP_ROM_QSTR(MP_QSTR_zlib), MP_ROM_PTR(&mp_module_uzlib) }, \
{ MP_ROM_QSTR(MP_QSTR_json), MP_ROM_PTR(&mp_module_ujson) }, \
{ MP_ROM_QSTR(MP_QSTR_heapq), MP_ROM_PTR(&mp_module_uheapq) }, \
{ MP_ROM_QSTR(MP_QSTR_hashlib), MP_ROM_PTR(&mp_module_uhashlib) }, \
{ MP_ROM_QSTR(MP_QSTR_io), MP_ROM_PTR(&mp_module_io) }, \
{ MP_ROM_QSTR(MP_QSTR_os), MP_ROM_PTR(&mp_module_uos) }, \
{ MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_utime) }, \
{ MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&mp_module_ustruct) }, \
{ MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \
{ MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_uerrno) }, \
// extra constants
#define MICROPY_PORT_CONSTANTS \
{ MP_ROM_QSTR(MP_QSTR_umachine), MP_ROM_PTR(&machine_module) }, \
{ MP_ROM_QSTR(MP_QSTR_machine), MP_ROM_PTR(&machine_module) }, \
{ MP_ROM_QSTR(MP_QSTR_ckcc), MP_ROM_PTR(&ckcc_module) }, \
{ MP_ROM_QSTR(MP_QSTR_pyb), MP_ROM_PTR(&pyb_module) }, \
{ MP_ROM_QSTR(MP_QSTR_stm), MP_ROM_PTR(&stm_module) }, \
#define MP_STATE_PORT MP_STATE_VM
#define MICROPY_PORT_ROOT_POINTERS \
const char *readline_hist[8]; \
\
mp_obj_t pyb_hid_report_desc; \
\
mp_obj_t pyb_config_main; \
\
mp_obj_t pyb_switch_callback; \
\
mp_obj_t pin_class_mapper; \
mp_obj_t pin_class_map_dict; \
\
mp_obj_t pyb_extint_callback[PYB_EXTI_NUM_VECTORS]; \
\
/* pointers to all Timer objects (if they have been created) */ \
struct _pyb_timer_obj_t *pyb_timer_obj_all[MICROPY_HW_MAX_TIMER]; \
\
/* stdio is repeated on this UART object if it's not null */ \
struct _pyb_uart_obj_t *pyb_stdio_uart; \
\
/* pointers to all UART objects (if they have been created) */ \
struct _pyb_uart_obj_t *pyb_uart_obj_all[MICROPY_HW_MAX_UART]; \
\
/* pointers to all CAN objects (if they have been created) */ \
struct _pyb_can_obj_t *pyb_can_obj_all[2]; \
// type definitions for the specific machine
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1))
#define MP_SSIZE_MAX (0x7fffffff)
#define UINT_FMT "%u"
#define INT_FMT "%d"
typedef int mp_int_t; // must be pointer size
typedef unsigned int mp_uint_t; // must be pointer size
typedef long mp_off_t;
#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len)
// We have inlined IRQ functions for efficiency (they are generally
// 1 machine instruction).
//
// Note on IRQ state: you should not need to know the specific
// value of the state variable, but rather just pass the return
// value from disable_irq back to enable_irq. If you really need
// to know the machine-specific values, see irq.h.
static inline void enable_irq(mp_uint_t state) {
__set_PRIMASK(state);
}
static inline mp_uint_t disable_irq(void) {
mp_uint_t state = __get_PRIMASK();
__disable_irq();
return state;
}
#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq()
#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state)
#if MICROPY_PY_THREAD
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(void); \
mp_handle_pending(); \
if (pyb_thread_enabled) { \
MP_THREAD_GIL_EXIT(); \
pyb_thread_yield(); \
MP_THREAD_GIL_ENTER(); \
} else { \
__WFI(); \
} \
} while (0);
#define MICROPY_THREAD_YIELD() pyb_thread_yield()
#else
#define MICROPY_EVENT_POLL_HOOK \
do { \
extern void mp_handle_pending(void); \
mp_handle_pending(); \
__WFI(); \
} while (0);
#define MICROPY_THREAD_YIELD()
#endif
// We need an implementation of the log2 function which is not a macro
#define MP_NEED_LOG2 (1)
#if 0
// There is no classical C heap in bare-metal ports, only Python
// garbage-collected heap. For completeness, emulate C heap via
// GC heap. Note that MicroPython core never uses malloc() and friends,
// so these defines are mostly to help extension module writers.
#define malloc(n) m_malloc(n)
#define free(p) m_free(p)
#define realloc(p, n) m_realloc(p, n)
#endif
// see stm32f4XX_hal_conf.h USE_USB_FS & USE_USB_HS
// at the moment only USB_FS is supported
//#define USE_DEVICE_MODE
//#define USE_HOST_MODE
// We need to provide a declaration/definition of alloca()
#include <alloca.h>
// default port config: see ports/stm32
#include "mpconfigport.h"
#undef MICROPY_PY_MACHINE_PULSE
#define MICROPY_PY_MACHINE_PULSE (0)
#define MICROPY_PY_COLLECTIONS_DEQUE (1)

View File

@ -19,6 +19,7 @@
#include "boardctrl.h"
#include "softtimer.h"
#include "ulight.h"
#include "uart.h"
#include "storage.h"
#include "usb.h"
@ -32,9 +33,6 @@
// Presume genuine light is on, at start
STATIC bool presumably_green_light = true;
// see vcp_lockdown.c where this is used
bool ckcc_vcp_enabled;
MP_DECLARE_CONST_FUN_OBJ_0(pyb_rng_get_obj);
MP_DECLARE_CONST_FUN_OBJ_1(pyb_rng_get_bytes_obj);
@ -173,6 +171,12 @@ STATIC mp_obj_t is_simulator(void)
}
MP_DEFINE_CONST_FUN_OBJ_0(is_simulator_obj, is_simulator);
STATIC mp_obj_t is_debug_build(void)
{
return MP_OBJ_NEW_SMALL_INT(COLDCARD_DEBUG);
}
MP_DEFINE_CONST_FUN_OBJ_0(is_debug_build_obj, is_debug_build);
STATIC mp_obj_t get_cpu_id(void)
{
// Are we running on a STM32L496RG6? If so, expect 0x461
@ -183,6 +187,9 @@ MP_DEFINE_CONST_FUN_OBJ_0(get_cpu_id_obj, get_cpu_id);
STATIC mp_obj_t vcp_enabled(mp_obj_t new_val)
{
// see vcp_lockdown.c where this is used
extern bool ckcc_vcp_enabled;
// Report/Control the VCP lockout. Call with None to readback.
if(mp_obj_is_integer(new_val)) {
ckcc_vcp_enabled = !!(mp_obj_get_int_truncated(new_val));
@ -223,16 +230,6 @@ STATIC mp_obj_t usb_active(void)
}
MP_DEFINE_CONST_FUN_OBJ_0(usb_active_obj, usb_active);
STATIC mp_obj_t wipe_fs(void)
{
// Erase and reformat flash filesystem
// - not supported on Mk4
mp_raise_ValueError(NULL);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(wipe_fs_obj, wipe_fs);
STATIC mp_obj_t breakpoint(void)
{
// drop into the debugger, if connected.
@ -261,9 +258,9 @@ STATIC const mp_rom_map_elem_t ckcc_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_gate), MP_ROM_PTR(&sec_gate_obj) },
{ MP_ROM_QSTR(MP_QSTR_oneway), MP_ROM_PTR(&sec_oneway_gate_obj) },
{ MP_ROM_QSTR(MP_QSTR_is_simulator), MP_ROM_PTR(&is_simulator_obj) },
{ MP_ROM_QSTR(MP_QSTR_is_debug_build), MP_ROM_PTR(&is_debug_build_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_cpu_id), MP_ROM_PTR(&get_cpu_id_obj) },
{ MP_ROM_QSTR(MP_QSTR_vcp_enabled), MP_ROM_PTR(&vcp_enabled_obj) },
{ MP_ROM_QSTR(MP_QSTR_wipe_fs), MP_ROM_PTR(&wipe_fs_obj) },
{ MP_ROM_QSTR(MP_QSTR_presume_green), MP_ROM_PTR(&presume_green_obj) },
{ MP_ROM_QSTR(MP_QSTR_breakpoint), MP_ROM_PTR(&breakpoint_obj) },
{ MP_ROM_QSTR(MP_QSTR_watchpoint), MP_ROM_PTR(&watchpoint_obj) },
@ -285,8 +282,8 @@ void ckcc_early_init(void)
{
// Add system-wide init code here.
// Disable ^C to interrupt code.
// cannot find where this might be set by other code to ^C.
// Disable ^C to interrupt code... but see mp_hal_set_interrupt_char()
// for best disable code.
mp_interrupt_char = -1;
// Do the equivilent of "py.usb_mode(None)" in boot.py
@ -294,6 +291,8 @@ void ckcc_early_init(void)
pyb_usb_flags |= PYB_USB_FLAG_USB_MODE_CALLED;
}
void ckcc_boardctrl_before_boot_py(boardctrl_state_t *state)
{
// do not run /boot.py even if it exists
@ -338,25 +337,6 @@ ckcc_heap_end(void)
return rv;
}
#if 0
// Add a wrapper to this flash_bdev_writeblock() so we
// can detect when any change made to "green lit" firmware,
// and therefore clear the light. It doesn't really matter to anything
// but's a debug aid and (mostly?) prevents the case where user switches
// off the Coldcard with green light on, but unverified changes in flash.
//
bool CKCC_flash_bdev_writeblock(const uint8_t *src, uint32_t block)
{
if(presumably_green_light) {
callgate_lower(4, 1, NULL);
presumably_green_light = false;
}
return flash_bdev_writeblock(src, block);
}
#endif
// There is no classical C heap in bare-metal ports, only Python
// garbage-collected heap. For completeness, emulate C heap via
// GC heap. Note that MicroPython core never uses malloc() and friends,
@ -376,5 +356,4 @@ void *realloc(void *ptr, size_t size)
return m_realloc(ptr, size);
}
// EOF

View File

@ -11,7 +11,8 @@ MICROPY_VFS_LFS2 = 1
MICROPY_VFS_FAT = 1
# see py/mpconfig.h which uses this var if set
#INC += -DMP_CONFIGFILE=\"boards/$(BOARD)/ckcc-port.h\"
CFLAGS_EXTRA += -DMP_CONFIGFILE=\"boards/$(BOARD)/ckcc-port.h\"
CFLAGS_EXTRA += -DCOLDCARD_DEBUG=$(DEBUG_BUILD)
# obsolete
# need the CDC inf file to be built before this file
@ -78,6 +79,11 @@ build-COLDCARD_MK/boards/COLDCARD_MK4/modckcc.o: COPT = -O0 -DNDEBUG
build-COLDCARD_MK4/lib/stm32lib/CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.o: \
CFLAGS += -DSystemInit=SystemInit_OMIT
# bugfix: replace keyboard interrupt handling
build-COLDCARD_MK4/lib/utils/interrupt_char.o: \
CFLAGS += -Dmp_hal_set_interrupt_char=mp_hal_set_interrupt_char_OMIT
files:
# SRC_C: $(SRC_C)
@echo

View File

@ -35,13 +35,11 @@ extern __IO uint32_t uwTick;
STATIC mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self);
STATIC const uint8_t psram_msc_lu_num = 1;
STATIC uint16_t psram_msc_lu_flags;
typedef struct _psram_obj_t {
mp_obj_base_t base;
uint32_t host_write_time;
uint32_t cc_write_time;
} psram_obj_t;
// singleton
@ -50,13 +48,13 @@ psram_obj_t psram_obj = {
{ &psram_type },
};
#define HOST_WR_TIMEOUT 2000 // (ms)
#define HOST_WR_TIMEOUT 750 // (ms)
static soft_timer_entry_t host_wr_done;
// This flag is needed to support removal of the medium, so that the USB drive
// can be unmounted and won't be remounted automatically.
#define FLAGS_STARTED (0x01)
#define FLAGS_READONLY (0x02)
// we only have a single LUN, so flags can be simple
// - note that "started" is more like inserted vs. ejected
bool flag_STARTED = false;
bool flag_READONLY = false;
// psram_init()
//
@ -78,10 +76,13 @@ psram_init(void)
static void
reset_wr_timeout(void)
{
// host has written something, reset/set a timeout to look at new change,
// assuming more is not written before the timeout expires.
// host has written something, reset/set a timeout to handle new change
soft_timer_remove(&host_wr_done);
psram_obj.host_write_time = uwTick;
if(host_wr_done.callback != MP_ROM_NONE) {
host_wr_done.expiry_ms = uwTick + HOST_WR_TIMEOUT;
soft_timer_insert(&host_wr_done);
@ -93,8 +94,13 @@ reset_wr_timeout(void)
static void
wr_timeout_now(void)
{
// host did something that indicates it won't be writing anymore to
// the disk, and therefore ok to immediately look at contents.
soft_timer_remove(&host_wr_done);
psram_obj.host_write_time = uwTick;
if(host_wr_done.callback != MP_ROM_NONE) {
mp_sched_schedule(host_wr_done.callback, MP_OBJ_FROM_PTR(&psram_obj));
}
@ -104,25 +110,15 @@ static uint8_t *block_to_ptr(uint32_t blk, uint16_t num_blk)
{
// Range checking on incoming requests also done in SCSI_CheckAddressRange()
// but this is an extra layer of safety, important since we might expose
// our address space otherwise.
// our address space otherwise!
// - note unsigned arguments
if(blk >= BLOCK_COUNT) return NULL;
if((blk+num_blk) > BLOCK_COUNT) return NULL;
return &PSRAM_TOP_BASE[blk * BLOCK_SIZE];
}
static inline void lu_flag_set(uint8_t lun, uint8_t flag) {
psram_msc_lu_flags |= flag << (lun * 2);
}
static inline void lu_flag_clr(uint8_t lun, uint8_t flag) {
psram_msc_lu_flags &= ~(flag << (lun * 2));
}
static inline bool lu_flag_is_set(uint8_t lun, uint8_t flag) {
return psram_msc_lu_flags & (flag << (lun * 2));
}
// Sent in response to MODE SENSE(6) command
const uint8_t PSRAM_MSC_Mode_Sense6_Data[4] = {
0x03, // mode data length
@ -176,8 +172,10 @@ STATIC int8_t psram_msc_Init(uint8_t lun_in) {
if (lun_in != 0) {
return 0;
}
lu_flag_set(0, FLAGS_STARTED);
lu_flag_clr(0, FLAGS_READONLY);
// don't change flag here, might have been set by python
//flags_STARTED = false;
//flags_READONLY = false;
return 0;
}
@ -235,7 +233,9 @@ STATIC int8_t psram_msc_IsReady(uint8_t lun) {
}
// NOTE: called frequently, and must be T for MacOS to recognize at all
return lu_flag_is_set(lun, FLAGS_STARTED) ? 0 : -1;
// when F, macos keeps trying to work until it's ready again (freezing programs
// trying to work with the drive).
return flag_STARTED ? 0 : -1;
}
// Check if a logical unit is write protected
@ -243,7 +243,7 @@ STATIC int8_t psram_msc_IsWriteProtected(uint8_t lun) {
if (lun >= psram_msc_lu_num) {
return -1;
}
return lu_flag_is_set(lun, FLAGS_READONLY) ? 1 : 0;
return flag_READONLY ? 1 : 0;
}
// Start or stop a logical unit
@ -251,21 +251,33 @@ STATIC int8_t psram_msc_StartStopUnit(uint8_t lun, uint8_t started) {
if (lun >= psram_msc_lu_num) {
return -1;
}
printf("PSRAMdisk: started=%d\n", started);
// host is not allowed to change our ready status: always fail
printf("PSRAMdisk: started=%d tried\n", started);
ckcc_usb_active = true;
if(!started) {
// (macos) is trying to "eject" the disk. Note this event.
wr_timeout_now();
}
return -1;
#if 0
if (started) {
lu_flag_set(lun, FLAGS_STARTED);
flag_STARTED = true;
ckcc_usb_active = true;
} else {
lu_flag_clr(lun, FLAGS_STARTED);
flag_STARTED = false;
}
return 0;
#endif
}
// Prepare a logical unit for possible removal
STATIC int8_t psram_msc_PreventAllowMediumRemoval(uint8_t lun, uint8_t param) {
printf("PSRAMdisk: prevallow=%d\n", param);
if(param == 0) {
// allow removal == host is done (like unmount in MacOS)
// allow removal == host is done (like after umount in MacOS)
wr_timeout_now();
}
@ -300,7 +312,6 @@ STATIC int8_t psram_msc_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint
memcpy(ptr, buf, blk_len*BLOCK_SIZE);
reset_wr_timeout();
psram_obj.host_write_time = uwTick;
return 0;
}
@ -308,6 +319,7 @@ STATIC int8_t psram_msc_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint
// Get the number of attached logical units
STATIC int8_t psram_msc_GetMaxLun(void) {
ckcc_usb_active = true;
return psram_msc_lu_num - 1;
}
@ -415,8 +427,6 @@ int direct_psram_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t n
memcpy(ptr, src, num_blocks * BLOCK_SIZE);
psram_obj.cc_write_time = uwTick;
return 0;
}
@ -425,9 +435,12 @@ STATIC mp_obj_t psram_ioctl(mp_obj_t self_in, mp_obj_t cmd_in, mp_obj_t arg_in)
mp_int_t cmd = mp_obj_get_int(cmd_in);
switch (cmd) {
case MP_BLOCKDEV_IOCTL_INIT:
case MP_BLOCKDEV_IOCTL_DEINIT:
case MP_BLOCKDEV_IOCTL_SYNC:
// umount() called; CC done w/ filesystem
return MP_OBJ_NEW_SMALL_INT(0);
case MP_BLOCKDEV_IOCTL_INIT: // when mount() happens (even R/O)
case MP_BLOCKDEV_IOCTL_DEINIT: // not observed
// nothing to do
return MP_OBJ_NEW_SMALL_INT(0);
@ -482,7 +495,7 @@ mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self)
memset(PSRAM_TOP_BASE, 0x21, BLOCK_SIZE * BLOCK_COUNT);
// Build obj to handle blockdev protocol
fs_user_mount_t vfs;
fs_user_mount_t vfs = {0};
psram_init_vfs(&vfs, false);
// newfs:
@ -492,13 +505,14 @@ mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self)
uint8_t working_buf[FF_MAX_SS];
FRESULT res = f_mkfs(&vfs.fatfs, FM_FAT|FM_SFD, BLOCK_SIZE, working_buf, sizeof(working_buf));
if (res != FR_OK) {
mp_printf(&mp_plat_print, "PSRAM: can't create filesystem\n");
//mp_printf(&mp_plat_print, "PSRAM: can't create filesystem\n");
goto fail;
}
// set volume label, which becomes mountpoint on MacOS
// .. can't do this from python AFAIK
f_setlabel(&vfs.fatfs, "COLDCARD");
f_mkdir(&vfs.fatfs, "ident");
FIL fp;
UINT n;
@ -508,16 +522,17 @@ mp_obj_t psram_wipe_and_setup(mp_obj_t unused_self)
{ char fname[80];
const uint8_t *id = (const uint8_t *)MP_HAL_UNIQUE_ID_ADDRESS; // 12 bytes, binary
snprintf(fname, sizeof(fname),
"ckcc-%02X%02X%02X%02X%02X%02X.txt",
"ident/ckcc-%02X%02X%02X%02X%02X%02X.txt",
id[11], id[10] + id[2], id[9], id[8] + id[0], id[7], id[6]);
const char *serial = &fname[11];
f_open(&vfs.fatfs, &fp, fname, FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fp, fname+5, 12, &n);
f_write(&fp, serial, 12, &n);
f_write(&fp, "\r\n", 2, &n);
f_close(&fp);
f_open(&vfs.fatfs, &fp, "serial.txt", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fp, fname+5, 12, &n);
f_open(&vfs.fatfs, &fp, "ident/serial.txt", FA_WRITE | FA_CREATE_ALWAYS);
f_write(&fp, serial, 12, &n);
f_write(&fp, "\r\n", 2, &n);
f_close(&fp);
}
@ -551,7 +566,7 @@ mp_obj_t psram_mmap_file(mp_obj_t unused_self, mp_obj_t fname_in)
const char *fname = mp_obj_str_get_str(fname_in);
// Build obj to handle python protocol
fs_user_mount_t vfs;
fs_user_mount_t vfs = {0};
psram_init_vfs(&vfs, true);
FRESULT res = f_mount(&vfs.fatfs);
@ -629,7 +644,7 @@ mp_obj_t psram_copy_file(mp_obj_t unused_self, mp_obj_t offset_in, mp_obj_t fnam
const char *fname = mp_obj_str_get_str(fname_in);
// Build obj to handle python protocol
fs_user_mount_t vfs;
fs_user_mount_t vfs = {0};
psram_init_vfs(&vfs, true);
FRESULT res = f_mount(&vfs.fatfs);
@ -706,16 +721,36 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_3(psram_copy_file_obj, psram_copy_file);
mp_obj_t psram_set_callback(mp_obj_t unused_self, mp_obj_t callback_in)
{
if(callback_in != MP_ROM_NONE) {
soft_timer_remove(&host_wr_done);
// set or clear the callback, use None to disable
soft_timer_remove(&host_wr_done);
host_wr_done.callback = callback_in;
}
host_wr_done.callback = callback_in;
return host_wr_done.callback;
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(psram_set_callback_obj, psram_set_callback);
mp_obj_t psram_set_inserted(mp_obj_t unused_self, mp_obj_t enable_in)
{
// set or clear insertion status (media started)
if(enable_in != MP_ROM_NONE) {
bool enable = !!mp_obj_get_int(enable_in);
flag_STARTED = enable;
}
return MP_OBJ_NEW_SMALL_INT(flag_STARTED);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(psram_set_inserted_obj, psram_set_inserted);
mp_obj_t psram_get_time(mp_obj_t unused_self)
{
// return time of last write from host
return mp_obj_new_int_from_uint(psram_obj.host_write_time);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(psram_get_time_obj, psram_get_time);
STATIC const mp_rom_map_elem_t psram_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&psram_readblocks_obj) },
@ -725,6 +760,8 @@ STATIC const mp_rom_map_elem_t psram_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_mmap), MP_ROM_PTR(&psram_mmap_file_obj) },
{ MP_ROM_QSTR(MP_QSTR_copy_file), MP_ROM_PTR(&psram_copy_file_obj) },
{ MP_ROM_QSTR(MP_QSTR_callback), MP_ROM_PTR(&psram_set_callback_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_inserted), MP_ROM_PTR(&psram_set_inserted_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_time), MP_ROM_PTR(&psram_get_time_obj) },
};
STATIC MP_DEFINE_CONST_DICT(psram_locals_dict, psram_locals_dict_table);

View File

@ -9,36 +9,80 @@
#include "extmod/misc.h"
#include "usb.h"
#include "uart.h"
#include "lib/utils/interrupt_char.h" // for mp_interrupt_char
extern bool ckcc_vcp_enabled;
// is the REPL enabled by user (default: no)
bool ckcc_vcp_enabled;
// XXX for mk4, allow some output maybe? and only support serial port, not USB
// mp_hal_stdin_rx_chr()
//
// - replaces code in stm32/mphalport.c
// - ignore all keys unless in REPL mode
//
int
mp_hal_stdin_rx_chr(void) {
for (;;) {
if (MP_STATE_PORT(pyb_stdio_uart) != NULL && uart_rx_any(MP_STATE_PORT(pyb_stdio_uart))) {
// consume it, but forward only if enabled.
int ch = uart_rx_char(MP_STATE_PORT(pyb_stdio_uart));
#if COLDCARD_DEBUG
return ch;
#else
if(ckcc_vcp_enabled) return ch;
#endif
}
#if 0
// replacements for more permissive versions found in stm32/mphalport.c
// - we don't support any h/w UARTs
// - USB VCP only sometimes, not all the time.
// - this only disconnects the REPL; can still open the VCP and read/write
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
if(ckcc_vcp_enabled && usb_vcp_is_enabled()) {
usb_vcp_send_strn(str, len);
}
}
int mp_hal_stdin_rx_chr(void) {
for (;;) {
byte c;
if (usb_vcp_recv_byte(&c) != 0) {
// USB virtual comm port support
#if COLDCARD_DEBUG
int dupterm_c = mp_uos_dupterm_rx_chr();
if (dupterm_c >= 0) {
if(ckcc_vcp_enabled) {
return c;
return dupterm_c;
}
}
#endif
#endif
MICROPY_EVENT_POLL_HOOK
}
}
// mp_hal_set_interrupt_char()
//
void
mp_hal_set_interrupt_char(int c)
{
// Replaces content of l-mpy/lib/utils/interrupt_char.c
// - many things call this at many times.
// - instead, use our ckcc.vcp_enable() call
#if COLDCARD_DEBUG
mp_interrupt_char = 3;
#else
mp_interrupt_char = -1;
#endif
}
// mp_hal_stdout_tx_strn()
//
void
mp_hal_stdout_tx_strn(const char *str, size_t len)
{
#if !COLDCARD_DEBUG
if(!ckcc_vcp_enabled) {
// allow the copyright notice and version string, then no more output
static int so_far = 0;
if(so_far > 84) return;
so_far += len;
}
#endif
if (MP_STATE_PORT(pyb_stdio_uart) != NULL) {
uart_tx_strn(MP_STATE_PORT(pyb_stdio_uart), str, len);
}
mp_uos_dupterm_tx_strn(str, len);
}
// EOF

View File

@ -12,11 +12,12 @@ MPY_CROSS = $(MPY_TOP)/mpy-cross/mpy-cross
PYTHON_MAKE_DFU = $(MPY_TOP)/tools/dfu.py
PYTHON_DO_DFU = $(MPY_TOP)/tools/pydfu.py
BUILD_DIR = l-port/build-COLDCARD_MK4
DEBUG_BUILD ?= 1
# aka ../cli/signit.py
SIGNIT = signit
MAKE_ARGS = BOARD=COLDCARD_MK4 -j 4 EXCLUDE_NGU_TESTS=1
MAKE_ARGS = BOARD=COLDCARD_MK4 -j 4 EXCLUDE_NGU_TESTS=1 DEBUG_BUILD=$(DEBUG_BUILD)
all: COLDCARD_MK4/file_time.c
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS)
@ -36,7 +37,6 @@ firmware.elf: $(BUILD_DIR)/firmware.elf
# These values used to make .DFU files. Flash memory locations.
FIRMWARE_BASE = 0x08020000
BOOTLOADER_BASE = 0x08000000
FILESYSTEM_BASE = 0x080e0000 # XXX wrong
#
# Sign and merge various parts
@ -174,15 +174,6 @@ mostly.dfu: firmware-signed.bin bootloader/bootloader.bin Makefile
m-dfu: mostly.dfu
$(PYTHON_DO_DFU) -u mostly.dfu
# Clear the internal filesystem (for dev-mistakes recovery)
# - unused?
.PHONY: wipe-fs
wipe-fs:
dd if=/dev/urandom of=tmp.bin bs=512 count=1
$(PYTHON_MAKE_DFU) -b $(FILESYSTEM_BASE):tmp.bin tmp.dfu
$(PYTHON_DO_DFU) -u tmp.dfu
rm tmp.bin tmp.dfu
# unused
stlink:
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy-stlink
@ -258,7 +249,8 @@ setup:
DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \
-v $(realpath built):/work/built:rw \
--privileged coldcard-build
repro: code-committed
#XXXrepro: code-committed
repro:
docker build -t coldcard-build - < dockerfile.build
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh)

View File

@ -189,7 +189,8 @@ results = [
( 'downgrade', 'Downgrade?', 'history', {} ),
( 'corrupt', 'Firmware?', 'lemon', {} ),
( 'logout', 'Logout Done', 'logout', {}),
( 'devmode', 'Danger! Caution!', 'bomb-spook', dict(icon_xpos=0)), # was 2
( 'devmode', 'Danger! Custom!', 'bomb-spook', dict(icon_xpos=0)), # was 2
( 'red_light', 'Danger! Caution!', 'bomb-spook', dict(icon_xpos=0)), # was 2
( 'upgrading', 'Upgrading', 'graph-up', {}),
( 'replug', 'Replug', None, {}), # visible in factory only
( 'search', 'Searching...', 'search-card', {}),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -32,7 +32,11 @@ const unsigned char screen_logout[187] = {
0x7f, 0x00, 0x38, 0x00, 0x07, 0x80, 0x77, 0x00, 0x82, 0xfe, 0xff, 0x03, 0x01, 0x03, 0xf1, 0x04, 0xf0, 0x89, 0xff, 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x6b, 0x00, 0x82, 0x3f, 0x7f, 0x03, 0xc0, 0x03, 0xc7, 0x81, 0x87, 0x03, 0x07, 0x88, 0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01, 0x7f, 0x00, 0x52, 0x00, 0x81, 0xf8, 0x06, 0x00, 0x81, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x83, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x84, 0xc0, 0x00, 0x40, 0xf0, 0x03, 0x40, 0x09, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x04, 0x40, 0x81, 0x80, 0x34, 0x00, 0x81, 0x1f, 0x05, 0x10, 0x82, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x0f, 0x03, 0x10, 0x85, 0x08, 0x1f, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x81, 0x08, 0x08, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x0f, 0x04, 0x12, 0x81, 0x13, 0x7f, 0x00, 0x1b, 0x00, 0x00
};
const unsigned char screen_devmode[303] = {
const unsigned char screen_devmode[291] = {
0x7f, 0x00, 0x37, 0x00, 0x85, 0xb0, 0x90, 0x20, 0xf0, 0x40, 0x10, 0x00, 0x82, 0x80, 0xe0, 0x05, 0xf0, 0x84, 0xf8, 0xf0, 0xe0, 0x80, 0x4d, 0x00, 0x88, 0x80, 0xe0, 0x70, 0x18, 0x8c, 0xce, 0xe6, 0xf6, 0x05, 0xfe, 0x81, 0xfc, 0x04, 0xfe, 0x86, 0x9b, 0x01, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x00, 0x8f, 0xb1, 0xf3, 0xff, 0xff, 0xf3, 0xe3, 0xf3, 0xff, 0xe3, 0xe3, 0xf3, 0xff, 0xf7, 0xf3, 0xb1, 0x4b, 0x00, 0x82, 0x1f, 0x7f, 0x0f, 0xff, 0x82, 0x7f, 0x1f, 0x11, 0x00, 0x81, 0xfc, 0x05, 0xff, 0x87, 0xfc, 0x80, 0xf1, 0xff, 0xe1, 0xc0, 0xfe, 0x05, 0xff, 0x81, 0xf8, 0x4c, 0x00, 0x82, 0x01, 0x03, 0x09, 0x07, 0x82, 0x03, 0x01, 0x15, 0x00, 0x81, 0x01, 0x0f, 0x03, 0x81, 0x01, 0x33, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x00, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x81, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x0b, 0x00, 0x81, 0xf0, 0x04, 0x08, 0x83, 0x30, 0x00, 0xc0, 0x04, 0x00, 0x83, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0x40, 0xf0, 0x03, 0x40, 0x83, 0x00, 0x00, 0x80, 0x04, 0x40, 0x88, 0x80, 0x00, 0xc0, 0x40, 0x40, 0x80, 0x40, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x1b, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x0f, 0x04, 0x12, 0x83, 0x13, 0x00, 0x1f, 0x08, 0x00, 0x81, 0x1b, 0x0b, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x83, 0x0c, 0x00, 0x0f, 0x03, 0x10, 0x84, 0x08, 0x1f, 0x00, 0x09, 0x04, 0x12, 0x84, 0x0c, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x83, 0x08, 0x00, 0x0f, 0x04, 0x10, 0x88, 0x0f, 0x00, 0x1f, 0x00, 0x00, 0x07, 0x00, 0x1f, 0x03, 0x00, 0x81, 0x1b, 0x7f, 0x00, 0x10, 0x00, 0x00
};
const unsigned char screen_red_light[303] = {
0x7f, 0x00, 0x37, 0x00, 0x85, 0xb0, 0x90, 0x20, 0xf0, 0x40, 0x10, 0x00, 0x82, 0x80, 0xe0, 0x05, 0xf0, 0x84, 0xf8, 0xf0, 0xe0, 0x80, 0x4d, 0x00, 0x88, 0x80, 0xe0, 0x70, 0x18, 0x8c, 0xce, 0xe6, 0xf6, 0x05, 0xfe, 0x81, 0xfc, 0x04, 0xfe, 0x86, 0x9b, 0x01, 0x00, 0x00, 0x01, 0x01, 0x0e, 0x00, 0x8f, 0xb1, 0xf3, 0xff, 0xff, 0xf3, 0xe3, 0xf3, 0xff, 0xe3, 0xe3, 0xf3, 0xff, 0xf7, 0xf3, 0xb1, 0x4b, 0x00, 0x82, 0x1f, 0x7f, 0x0f, 0xff, 0x82, 0x7f, 0x1f, 0x11, 0x00, 0x81, 0xfc, 0x05, 0xff, 0x87, 0xfc, 0x80, 0xf1, 0xff, 0xe1, 0xc0, 0xfe, 0x05, 0xff, 0x81, 0xf8, 0x4c, 0x00, 0x82, 0x01, 0x03, 0x09, 0x07, 0x82, 0x03, 0x01, 0x15, 0x00, 0x81, 0x01, 0x0f, 0x03, 0x81, 0x01, 0x30, 0x00, 0x81, 0xf8, 0x03, 0x08, 0x84, 0x10, 0xe0, 0x00, 0x00, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x83, 0x80, 0x00, 0x80, 0x03, 0x40, 0x84, 0x80, 0xc0, 0x00, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x81, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x0b, 0x00, 0x81, 0xf0, 0x04, 0x08, 0x83, 0x30, 0x00, 0x00, 0x04, 0x40, 0x83, 0x80, 0x00, 0xc0, 0x04, 0x00, 0x84, 0xc0, 0x00, 0x40, 0xf0, 0x03, 0x40, 0x03, 0x00, 0x83, 0x40, 0x40, 0xd8, 0x03, 0x00, 0x81, 0x80, 0x04, 0x40, 0x84, 0x80, 0x00, 0xc0, 0x80, 0x03, 0x40, 0x81, 0x80, 0x03, 0x00, 0x81, 0xf8, 0x14, 0x00, 0x81, 0x1f, 0x03, 0x10, 0x84, 0x08, 0x07, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x1f, 0x04, 0x00, 0x83, 0x1f, 0x00, 0x47, 0x03, 0x88, 0x84, 0x84, 0x7f, 0x00, 0x0f, 0x04, 0x12, 0x83, 0x13, 0x00, 0x1f, 0x08, 0x00, 0x81, 0x1b, 0x0b, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x83, 0x0c, 0x00, 0x0e, 0x03, 0x11, 0x84, 0x09, 0x1f, 0x00, 0x0f, 0x03, 0x10, 0x85, 0x08, 0x1f, 0x00, 0x00, 0x0f, 0x03, 0x10, 0x81, 0x08, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x0f, 0x04, 0x10, 0x83, 0x0f, 0x00, 0x1f, 0x04, 0x00, 0x81, 0x1f, 0x03, 0x00, 0x81, 0x1b, 0x7f, 0x00, 0x0c, 0x00, 0x00
};

View File

@ -28,6 +28,9 @@ extern const unsigned char screen_logout[];
extern const unsigned char screen_devmode[];
extern const unsigned char screen_red_light[];
extern const unsigned char screen_upgrading[];

View File

@ -161,22 +161,9 @@ system_startup(void)
se2_probe();
puts("done");
#if 0
{ uint8_t config[128] = {0};
int x = ae_config_read(config);
if(x == 0) {
puts("config[128]:");
hex_dump(config, 128);
} else {
puts("config read fail");
}
}
#endif
// protect our flash, and/or check it's protected
// - and pick pairing secret if we don't already have one
// - may also do one-time setup of 508a
// - may also do one-time setup of the secure elements
// - note: ae_setup must already be called, since it can talk to that
flash_setup();
//puts("Flash: setup done");

View File

@ -242,9 +242,10 @@ psram_wipe(void)
{
if(OCTOSPI1->CR == 0) return; // PSRAM not enabled (yet?)
puts2("PSRAM Wipe: ");
// Fast! But real; maybe 150ms
//puts2("PSRAM Wipe: ");
memset4((uint32_t *)PSRAM_BASE, rng_sample(), PSRAM_SIZE);
puts("done");
//puts("done");
}
// recover_from_psram()

View File

@ -179,10 +179,10 @@ check_is_downgrade(const uint8_t timestamp[8], const char *version)
return (memcmp(timestamp, min, 8) < 0);
}
// warn_bad_checksum()
// warn_fishy_firmware()
//
void
warn_bad_checksum(void)
warn_fishy_firmware(const uint8_t *pixels)
{
// warn the victim about corrupted flash/ident info
#if RELEASE
@ -190,11 +190,9 @@ warn_bad_checksum(void)
#else
const int wait = 10;
#endif
puts("WARN: Bad firmware");
for(int i=0; i < wait; i++) {
oled_show_progress(screen_devmode, (i*100)/wait);
oled_show_progress(pixels, (i*100)/wait);
delay_ms(250);
}
@ -337,11 +335,18 @@ verify_firmware(void)
// show big/slow warning if light is not green
if(not_green) {
warn_bad_checksum();
// When light is not green; some part of flash (not firmware area)
// is changed. these are typically false-positives, unfortunately.
puts("WARN: Red light");
warn_fishy_firmware(screen_red_light);
} else if(FW_HDR->pubkey_num == 0) {
// public signing key used; firmware not from Coinkite!
puts("WARN: Unsigned firmware");
warn_fishy_firmware(screen_devmode);
} else {
puts("good firmware");
oled_show_progress(screen_verify, 100);
puts("Good firmware");
}
oled_show_progress(screen_verify, 100);
return true;

View File

@ -48,7 +48,7 @@ fi
cd ../stm32
make setup
make all
make DEBUG_BUILD=0 all
make $TARGETS
if [ $PWD != '/work/src/stm32' ]; then

View File

@ -63,6 +63,7 @@ def simulator(request):
@pytest.fixture(scope='module')
def sim_exec(dev):
# run code in the simulator's interpretor
# - can work on real product too, if "debug build" is used.
def doit(cmd, binary=False):
s = dev.send_recv(b'EXEC' + cmd.encode('utf-8'))
@ -74,6 +75,7 @@ def sim_exec(dev):
@pytest.fixture(scope='module')
def sim_eval(dev):
# eval an expression in the simulator's interpretor
# - can work on real product too, if "debug build" is used.
def doit(cmd, timeout=None):
return dev.send_recv(b'EVAL' + cmd.encode('utf-8'), timeout=timeout).decode('utf-8')
@ -83,6 +85,7 @@ def sim_eval(dev):
@pytest.fixture(scope='module')
def sim_execfile(simulator):
# run a whole file in the simulator's interpretor
# - requires shared filesystem
import os
def doit(fname, timeout=None):
@ -518,6 +521,7 @@ def microsd_path(simulator):
# open a file from the simulated microsd
def doit(fn):
# could use: ckcc.get_sim_root_dirs() here
return '../unix/work/MicroSD/' + fn
return doit
@ -1122,11 +1126,12 @@ def nfc_read(sim_exec):
return doit
@pytest.fixture()
def nfc_write(sim_exec):
def nfc_write(sim_exec, need_keypress):
# WRITE data into NFC "chip"
def doit(ccfile):
rv = sim_exec('glob.NFC.big_write(%r)' % ccfile)
rv = sim_exec('list(glob.NFC.big_write(%r))' % ccfile)
if 'Traceback' in rv: raise pytest.fail(rv)
need_keypress('y') # to end the animation and have it check value immediately
return doit
@pytest.fixture()
@ -1146,7 +1151,6 @@ def nfc_block4rf(sim_eval):
def doit(timeout=15):
for i in range(timeout*4):
rv = sim_eval('glob.NFC.rf_on')
print('rv=%r' % rv)
if rv: break
sleep(0.250)
else:

View File

@ -13,4 +13,4 @@ zbar-py==1.0.4
# NFC and NDEF handling
nfcpy==1.0.3
ndef==0.3.3
ndef==0.2

View File

@ -445,10 +445,12 @@ def test_export_public_txt(dev, cap_menu, pick_menu_item, goto_home, cap_story,
addr_vs_path(rhs, path=lhs, addr_fmt=f)
#def test_nfc_after(num_outs, fake_txn, try_sign, nfc_read, need_keypress, cap_story, only_mk4):
@pytest.mark.qrcode
@pytest.mark.parametrize('acct_num', [ None, 0, 99, 8989])
def test_export_xpub(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number, cap_screen_qr, use_mainnet):
@pytest.mark.parametrize('use_nfc', [False, True])
def test_export_xpub(use_nfc, acct_num, dev, cap_menu, pick_menu_item, goto_home, cap_story, need_keypress, enter_number, cap_screen_qr, use_mainnet, nfc_read_text):
# XPUB's via QR
use_mainnet()
@ -475,10 +477,13 @@ def test_export_xpub(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_sto
time.sleep(0.1)
if is_xfp:
got = cap_screen_qr().decode('ascii')
if use_nfc:
need_keypress('3')
assert got == xfp2str(simulator_fixed_xfp).upper()
need_keypress('x')
continue
time.sleep(0.1)
title, story = cap_story()
assert expect in story
@ -496,8 +501,17 @@ def test_export_xpub(dev, acct_num, cap_menu, pick_menu_item, goto_home, cap_sto
expect = expect.format(acct=0)
need_keypress('y')
got_pub = cap_screen_qr().decode('ascii')
if not use_nfc:
need_keypress('y')
got_pub = cap_screen_qr().decode('ascii')
else:
assert 'Press 3' in story
assert 'NFC' in story
need_keypress('3')
got_pub = nfc_read_text()
time.sleep(0.1)
#need_keypress('y')
if got_pub[0] not in 'xt':
got_pub,*_ = slip132undo(got_pub)

View File

@ -148,6 +148,7 @@ def test_ndef_ccfile(ccfile, load_shared_mod):
def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec, nfc_read, nfc_write, nfc_block4rf):
# like "try_sign" but use NFC to send/receive PSBT/results
# XXX cap_story, pick_menu_item, goto_home => require simulator, bad.
sim_exec('from pyb import SDCard; SDCard.ejected = True; import nfc; nfc.NFCHandler.startup()')
@ -200,9 +201,8 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
need_keypress('3')
time.sleep(.1)
nfc_write(ccfile)
need_keypress('y')
time.sleep(.1)
time.sleep(.5)
if accept_ms_import:
# XXX would be better to do cap_story here, but that would limit test to simulator
@ -324,8 +324,7 @@ def try_sign_nfc(cap_story, pick_menu_item, goto_home, need_keypress, sim_exec,
@pytest.mark.parametrize('encoding', ['binary', 'hex', 'base64'])
@pytest.mark.parametrize('num_outs', [1,2])
@pytest.mark.parametrize('partial', [1, 0])
def test_nfc_signing(encoding, num_outs, partial, try_sign_nfc, fake_txn, try_sign, dev, settings_set):
# exercise the txn encode/decode from sdcard
def test_nfc_signing(encoding, num_outs, partial, try_sign_nfc, fake_txn, dev, settings_set):
xp = dev.master_xpub
def hack(psbt):

View File

@ -127,10 +127,16 @@ def test_psbt_proxy_parsing(fn, sim_execfile, sim_exec):
assert oo == rb
@pytest.mark.unfinalized
def test_speed_test(request, fake_txn, is_mark3, start_sign, end_sign, dev, need_keypress):
def test_speed_test(dev, fake_txn, is_mark3, is_mark4, start_sign, end_sign, need_keypress):
import time
# measure time to sign a larger txn
if is_mark3:
if is_mark4:
# Mk4: expect
# 20/250 => 15.5s (or 10.0 if seed is cached)
# 200/500 => 96.3s
num_in = 20
num_out = 250
elif is_mark3:
num_in = 20
num_out = 250
else:
@ -1327,9 +1333,12 @@ def test_wrong_xfp_multi(fake_txn, try_sign, segwit):
with pytest.raises(CCProtoError) as ee:
orig, result = try_sign(psbt, accept=True)
assert 'None of the keys' in str(ee)
# WEAK: device keeps them in order, but that's chance/impl defined...
assert 'found '+', '.join(sorted(wrongs)) in str(ee)
if 'Signing failed late' in str(ee):
pass
else:
assert 'None of the keys' in str(ee)
# WEAK: device keeps them in order, but that's chance/impl defined...
assert 'found '+', '.join(sorted(wrongs)) in str(ee)
@pytest.mark.parametrize('out_style', ADDR_STYLES_SINGLE)
@pytest.mark.parametrize('segwit', [False, True])
@ -1376,10 +1385,10 @@ def test_render_outs(out_style, segwit, outval, fake_txn, start_sign, end_sign,
assert addrs[0][0] in {'2', '3'}
def test_negative_fee(fake_txn, try_sign):
def test_negative_fee(dev, fake_txn, try_sign):
# Silly to sign a psbt the network won't accept, but anyway...
with pytest.raises(CCProtoError) as ee:
psbt = fake_txn(1, 1, outvals=[int(2E8)])
psbt = fake_txn(1, 1, dev.master_xpub, outvals=[int(2E8)])
orig, result = try_sign(psbt, accept=False)
msg = ee.value.args[0]
@ -1390,7 +1399,7 @@ def test_negative_fee(fake_txn, try_sign):
( 5, 'mXTN'),
( 2, 'bits'),
( 0, 'sats')])
def test_value_render(units, fake_txn, start_sign, cap_story, settings_set, settings_remove):
def test_value_render(dev, units, fake_txn, start_sign, cap_story, settings_set, settings_remove):
# Check we are rendering values in right units.
decimal, units = units
@ -1404,7 +1413,7 @@ def test_value_render(units, fake_txn, start_sign, cap_story, settings_set, sett
]]
need = sum(outputs)
psbt = fake_txn(1, len(outputs), segwit_in=True, outvals=outputs, invals=[need])
psbt = fake_txn(1, len(outputs), dev.master_xpub, segwit_in=True, outvals=outputs, invals=[need])
open('debug/values.psbt', 'wb').write(psbt)

View File

@ -25,7 +25,7 @@ wallet (on testnet, always with the same seed). But there are other options:
"Me", "Myself", "And I" and empty string. BIP45 path.
- add `--p2wsh` or `--wrap` for the other two address types
- `-s` => go to the MicroSD menu at startup
- `--mk2` => emulate mark2 hardware (older micro, etc), default is current-gen (mark3)
- `--mk2` => emulate mark2 hardware (older micro, etc), default is current-gen (mark4)
- `--mk3` => emulate mark3 hardware
- `--mk4` => emulate mark4 hardware
- `-g` => don't skip login sequence

View File

@ -23,14 +23,11 @@ if '--sflash' not in sys.argv:
#glob.settings.current = dict(sim_defaults)
if 1:
# Mk4 hacks and workaround (no simulated PSRAM)
# Install Mk4 hacks and workarounds
import mk4
def mff_noop():
print("Skip FS rebuild (simulator)")
mk4.make_flash_fs = mff_noop
mk4.make_psram_fs = mff_noop
import sim_psram as psram
import sim_mk4
import sim_psram
import sim_vdisk
if sys.argv[-1] != '-q':
import main # must be last, does not return

View File

@ -68,6 +68,7 @@ def pin_prefix(pin, buf_out):
def gate(method, buf_io, arg2):
# the "callgate" into the bootloader
import version
# - only spuratically implemented (say it like in _Clueless_)
# - none of the error checking is repeated here
@ -130,7 +131,7 @@ def gate(method, buf_io, arg2):
return 0
if method == 22:
# trick pin actions, just skip for now TODO
# trick pin actions
global SE2_STATE
from trick_pins import TRICK_SLOT_LAYOUT
@ -141,6 +142,8 @@ def gate(method, buf_io, arg2):
slot = uctypes.struct(uctypes.addressof(b), TRICK_SLOT_LAYOUT)
pc = slot.pin[0:slot.pin_len].decode()
# XXX untested/incomplete?
if arg2 == 0: # clear all
SE2_STATE.clear()
return 0
@ -163,11 +166,11 @@ def gate(method, buf_io, arg2):
return ENOENT
if arg2 == 0xBeef:
# silent version, but does reset system
print("silent wipe of secret")
print("silent wipe of secret & reset")
elif arg2 == 0xDead:
# noisy, shows screen, halts
print("wipe of secret and die w/ screen")
return 0
print("wipes secret and die w/ screen: Seed Wiped")
return EPERM
if method == 24:
# fast brick -- locks up w/ message
@ -177,12 +180,14 @@ def gate(method, buf_io, arg2):
print("Fast brick")
return 0
else:
return EPERM;
return EPERM
return ENOENT
def oneway(method, arg2):
# TODO: capture method/arg2 into an object so unit tests can read it back while we are dead
print("\n\nNOTE: One-way callgate into bootloader: method=%d arg2=%d\n\n" % (method, arg2))
import time
while 1:
@ -191,6 +196,9 @@ def oneway(method, arg2):
def is_simulator():
return True
def is_debug_build():
return True
def get_sim_root_dirs():
# return a single path and list of files to pretend to find there
@ -230,5 +238,8 @@ def get_cpi_id():
#default mk4
return 0x470 # STM32L4S5
def PSRAM():
return object()
# EOF

View File

@ -1,4 +0,0 @@
# called near end of main.py
import sim_quickstart

View File

@ -1,4 +1,5 @@
freeze_as_mpy('', [
'aes256ctr.py',
'bare_metal.py',
'ckcc.py',
'ffilib.py',
@ -8,22 +9,20 @@ freeze_as_mpy('', [
'pyb.py',
'sflash.py',
'sim_display.py',
'sim_mk4.py',
'sim_nfc.py',
'sim_psram.py',
'sim_quickstart.py',
'main2.py',
'sim_secel.py',
'sim_settings.py',
'sim_vdisk.py',
'sram2.py',
'ssd1306.py',
'stm.py',
'struct.py',
'touch.py',
'usb_test_commands.py',
'version.py',
'aes256ctr.py',
'zevvpeep.py',
'mk4.py',
'sim_psram.py',
'sim_nfc.py',
], opt=0)
#include("../../shared/manifest_mk4.py")
include("$(MPY_DIR)/extmod/uasyncio/manifest.py")

View File

@ -1,19 +0,0 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# mk4.py - Mk4 specific code, not needed on earlier devices.
#
#
import sys, uos, pyb, glob
def make_flash_fs():
print("Would rebuild /flash")
def init0():
# called very, very early
# install (fake) NFC interface code
import sim_nfc
sys.modules['nfc'] = sim_nfc
# EOF

25
unix/variant/sim_mk4.py Normal file
View File

@ -0,0 +1,25 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# sim_mk4.py - Simulate Mk4 specific code, not needed on earlier devices.
#
# - just replace/override a few things.
#
import sys, uos, pyb, glob, mk4
def mff_noop():
print("Skip FS rebuild (simulator)")
mk4.make_flash_fs = mff_noop
mk4.make_psram_fs = mff_noop
def _init0():
# called very, very early
# install (fake) NFC interface code
import sim_nfc
sys.modules['nfc'] = sim_nfc
mk4.init0 = _init0
mk4.make_flash_fs = lambda: print("Would rebuild /flash")
# EOF

View File

@ -29,7 +29,10 @@ class SimulatedNFCHandler(NFCHandler):
atime, mtime, ctime = os.stat(DATA_FILE)[-3:]
self._mtime = mtime
self._atime = atime
print("%d bytes of NDEF written to work/nfc-dump.ndef .. touch or read that file to simulate taps" % n)
print("%d bytes of NDEF written to work/nfc-dump.ndef .. Press N or touch or read that file to simulate taps" % n)
async def wipe(self, full_wipe):
print("NFC chip wiped (full=%d)" % int(full_wipe))
def is_rf_disabled(self):
return not self.rf_on

View File

@ -1,6 +1,6 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# psram.py -- access PSRAM chip on Mk4
# sim_psram.py -- SIMULATED access PSRAM chip on Mk4
#
import version, psram

View File

@ -38,12 +38,14 @@ def pin_stuff(submethod, buf_io):
PIN_ATTEMPT_SIZE_V1, CHANGE_LS_OFFSET,
PA_SUCCESSFUL, PA_IS_BLANK, PA_HAS_DURESS, PA_HAS_BRICKME,
CHANGE_WALLET_PIN, CHANGE_DURESS_PIN, CHANGE_BRICKME_PIN,
AE_LONG_SECRET_LEN,
CHANGE_SECRET, CHANGE_DURESS_SECRET, CHANGE_SECONDARY_WALLET_PIN )
if len(buf_io) != (PIN_ATTEMPT_SIZE if version.has_608 else PIN_ATTEMPT_SIZE_V1):
if len(buf_io) < (PIN_ATTEMPT_SIZE if version.has_608 else PIN_ATTEMPT_SIZE_V1):
return ERANGE
global SECRETS
after_buf = None
(magic, is_secondary,
pin, pin_len,
@ -243,6 +245,20 @@ def pin_stuff(submethod, buf_io):
else:
secret = SECRETS['ls'][off:off+32]
elif submethod == 7:
# pin_firmware_upgrade(args) process for mk4
if version.mk_num < 4:
return ENOENT
# not implemented in simulator
pass
elif submethod == 8:
# new mk4 api for long-secret
if version.mk_num < 4:
return ENOENT
buf_io[-AE_LONG_SECRET_LEN:] = SECRETS.get('ls', bytearray(416))
else:
# bogus submethod

56
unix/variant/sim_vdisk.py Normal file
View File

@ -0,0 +1,56 @@
# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC.
#
# sim_vdisk.py -- SIMULATED virtual disk, using a directory on unix FS
#
import vdisk, version, os, ckcc
_EJECTED = True
class SimBlockDev:
# replace ckcc.PSRAM block device implemented in C
def callback(self, cb):
print("sim-virtdisk: callback %s" % bool(cb))
def set_inserted(self, en):
global _EJECTED
print("sim-virtdisk: " + "inserted" if en else "ejected")
_EJECTED = bool(en)
def wipe(self):
return
vdisk.VBLKDEV = SimBlockDev()
class SimulatedVirtDisk(vdisk.VirtDisk):
def sample(self):
# Peek at the contents of the disk right now
assert _EJECTED # don't read while host might update
from utils import get_filesize
path = ckcc.get_sim_root_dirs()[0] + '/VirtDisk'
return list(sorted((fn, get_filesize(fn)) for (fn,ty,_) in os.ilistdir(path)
if ty == 0x8000))
def mount(self, readonly=False):
rv= ckcc.get_sim_root_dirs()[0] + '/VirtDisk'
print(rv)
return rv
def unmount(self):
#print("sim-virtdisk: CC unmounted; ready to view")
pass
def import_file(self, filename, sz):
# copy file into another area of PSRAM where rest of system can use it
print("sim-virtdisk: read %s" % filename)
contents = open(filename, 'rb').read(sz)
from glob import PSRAM
PSRAM.write_at(0, sz)[:] = contents
return sz
vdisk.VirtDisk = SimulatedVirtDisk
# EOF

View File

@ -39,6 +39,7 @@ has_membrane = True
has_fatram = True
has_se2 = True
has_psram = True
has_nfc = True
if '--mk1' in sys.argv:
# doubt this works still
@ -48,6 +49,7 @@ if '--mk1' in sys.argv:
has_fatram = False
has_se2 = False
has_psram = False
has_nfc = False
if '--mk2' in sys.argv:
hw_label = 'mk2'
@ -55,6 +57,7 @@ if '--mk2' in sys.argv:
has_fatram = False
has_se2 = False
has_psram = False
has_nfc = False
if '--mk3' in sys.argv:
hw_label = 'mk3'
@ -62,5 +65,6 @@ if '--mk3' in sys.argv:
has_fatram = True
has_se2 = False
has_psram = False
has_nfc = False
mk_num = int(hw_label[2:])

9
unix/work/VirtDisk/.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
*.7z
*.txt
*.json
*.psbt
*.csv
*.pdf
*.dfu
*.txn
.tmp.tmp

View File

@ -0,0 +1 @@
# placehodler