Virtdisk working better
This commit is contained in:
parent
132f1d31c2
commit
6cc0483c21
@ -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
2
external/libngu
vendored
@ -1 +1 @@
|
||||
Subproject commit 606381cacd20fe908c20f095294e3de5dd2dc6c5
|
||||
Subproject commit 174f6c983eb3a0579294d753799b450fa9a6b007
|
||||
2
external/micropython
vendored
2
external/micropython
vendored
@ -1 +1 @@
|
||||
Subproject commit 5917fc199c7e1dd2ef7f9ada8bc48abe0488e9f3
|
||||
Subproject commit 778d3e461607b646592ce7606a14fc7164616d09
|
||||
@ -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=[
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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...')
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -8,4 +8,3 @@ freeze_as_mpy('', [
|
||||
'trick_pins.py',
|
||||
'graphics_mk4.py',
|
||||
], opt=0)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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...')
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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!
|
||||
136
shared/vdisk.py
136
shared/vdisk.py
@ -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.
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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 |
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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[];
|
||||
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ fi
|
||||
cd ../stm32
|
||||
|
||||
make setup
|
||||
make all
|
||||
make DEBUG_BUILD=0 all
|
||||
make $TARGETS
|
||||
|
||||
if [ $PWD != '/work/src/stm32' ]; then
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -13,4 +13,4 @@ zbar-py==1.0.4
|
||||
|
||||
# NFC and NDEF handling
|
||||
nfcpy==1.0.3
|
||||
ndef==0.3.3
|
||||
ndef==0.2
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
# called near end of main.py
|
||||
|
||||
import sim_quickstart
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
25
unix/variant/sim_mk4.py
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
56
unix/variant/sim_vdisk.py
Normal 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
|
||||
@ -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
9
unix/work/VirtDisk/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
*.7z
|
||||
*.txt
|
||||
*.json
|
||||
*.psbt
|
||||
*.csv
|
||||
*.pdf
|
||||
*.dfu
|
||||
*.txn
|
||||
.tmp.tmp
|
||||
1
unix/work/VirtDisk/README.md
Normal file
1
unix/work/VirtDisk/README.md
Normal file
@ -0,0 +1 @@
|
||||
# placehodler
|
||||
Loading…
Reference in New Issue
Block a user