diff --git a/stm32/COLDCARD/file_time.c b/stm32/COLDCARD/file_time.c index ec8cf834..dd332922 100644 --- a/stm32/COLDCARD/file_time.c +++ b/stm32/COLDCARD/file_time.c @@ -3,12 +3,12 @@ // // AUTO-generated. // -// built: 2021-04-30 -// version: 4.1.1 +// built: 2021-05-10 +// version: 5.0.0 // #include // this overrides ports/stm32/fatfs_port.c uint32_t get_fattime(void) { - return 0x529e2020UL; + return 0x52aa2800UL; } diff --git a/stm32/COLDCARD/modckcc.c b/stm32/COLDCARD/modckcc.c index fd082d85..79d9cf2b 100644 --- a/stm32/COLDCARD/modckcc.c +++ b/stm32/COLDCARD/modckcc.c @@ -179,13 +179,12 @@ STATIC mp_obj_t is_simulator(void) } MP_DEFINE_CONST_FUN_OBJ_0(is_simulator_obj, is_simulator); -STATIC mp_obj_t is_stm32l496(void) +STATIC mp_obj_t get_cpu_id(void) { - // Are we running on a STM32L496RG6? - return ((DBGMCU->IDCODE & 0xfff) == 0x461) ? mp_const_true : mp_const_false; + // Are we running on a STM32L496RG6? If so, expect 0x461 + return MP_OBJ_NEW_SMALL_INT(DBGMCU->IDCODE & 0xfff); } -MP_DEFINE_CONST_FUN_OBJ_0(is_stm32l496_obj, is_stm32l496); - +MP_DEFINE_CONST_FUN_OBJ_0(get_cpu_id_obj, get_cpu_id); STATIC mp_obj_t vcp_enabled(mp_obj_t new_val) @@ -261,7 +260,7 @@ 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_stm32l496), MP_ROM_PTR(&is_stm32l496_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) }, diff --git a/stm32/COLDCARD/stm32l4xx_hal_conf.h b/stm32/COLDCARD/stm32l4xx_hal_conf.h old mode 100755 new mode 100644 diff --git a/stm32/MK3-Makefile b/stm32/MK3-Makefile new file mode 100644 index 00000000..02e1d95d --- /dev/null +++ b/stm32/MK3-Makefile @@ -0,0 +1,287 @@ +# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images. +# +MPY_TOP = ../external/micropython +PORT_TOP = $(MPY_TOP)/ports/stm32 +MPY_CROSS = $(MPY_TOP)/mpy-cross/mpy-cross +PYTHON_MAKE_DFU = $(MPY_TOP)/tools/dfu.py +PYTHON_DO_DFU = $(MPY_TOP)/tools/pydfu.py + +# aka ../cli/signit.py +SIGNIT = signit + +MAKE_ARGS = BOARD=COLDCARD -j 4 EXCLUDE_NGU_TESTS=1 + +all: COLDCARD/file_time.c + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) + +clean: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) clean + git clean -xf built + +# These trigger the 'all' target when we haven't completed a successful build yet +l-port/build-COLDCARD/firmware.elf: all +l-port/build-COLDCARD/firmware0.bin: all +l-port/build-COLDCARD/firmware1.bin: all + +firmware.elf: l-port/build-COLDCARD/firmware.elf + cp l-port/build-COLDCARD/firmware.elf . + +# These values used to make .DFU files. Flash memory locations. +FIRMWARE_BASE = 0x08008000 +BOOTLOADER_BASE = 0x08000000 +FILESYSTEM_BASE = 0x080e0000 + +# Our version for this release. +VERSION_STRING = 4.1.1 + +# +# Sign and merge various parts +# +firmware-signed.bin: l-port/build-COLDCARD/firmware0.bin l-port/build-COLDCARD/firmware1.bin + $(SIGNIT) sign $(VERSION_STRING) -o $@ +firmware-signed.dfu: firmware-signed.bin Makefile + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):$< $@ + +# make the DFU file which is shared for upgrades +dfu: firmware-signed.dfu + +# Build a binary, signed w/ production key +# - always rebuild binary for this one +.PHONY: dev.dfu +dev.dfu: l-port/build-COLDCARD/firmware?.bin + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) + $(SIGNIT) sign $(VERSION_STRING) -k 1 -o dev.bin + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):dev.bin dev.dfu + +.PHONY: remake +remake: + rm -rf l-port/build-COLDCARD/firmware?.bin l-port/build-COLDCARD/frozen_mpy* + +# This is fast for Coinkite devs, but no DFU support in the wild. +up: dev.dfu + $(PYTHON_DO_DFU) -u dev.dfu + +# Slow, but works with unmod-ed board: use USB protocol to upgrade (2 minutes) +dev: dev.dfu + ckcc upgrade dev.dfu + +COLDCARD/file_time.c: Makefile make_filetime.py + ./make_filetime.py COLDCARD/file_time.c $(VERSION_STRING) + +# Make a factory release: using key #1 +# - when executed in a repro w/o the required key, it defaults to key zero +# - and that's what happens inside the Docker build +production.bin: firmware-signed.bin Makefile + $(SIGNIT) sign $(VERSION_STRING) -r firmware-signed.bin -k 1 -o $@ + +# This is release of the bootloader that will be built into the release firmware. +BOOTLOADER_VERSION = 2.0.1 + +.PHONY: release +release: code-committed + $(MAKE) clean + $(MAKE) repro + test -f built/production.bin + $(MAKE) release-products + $(MAKE) tag-source + +# Make a release-candidate, faster. +.PHONY: rc1 +rc1: + $(MAKE) repro + test -f built/production.bin + $(MAKE) release-products + +# This target just combines latest version of production firmware with bootrom into a DFU +# file, stored in ../releases with appropriately dated file name. +.PHONY: release-products +release-products: NEW_VERSION = $(shell $(SIGNIT) version built/production.bin) +release-products: RELEASE_FNAME = ../releases/$(NEW_VERSION)-coldcard.dfu +release-products: built/production.bin + test ! -f $(RELEASE_FNAME) + cp built/file_time.c COLDCARD/file_time.c + -git commit COLDCARD/file_time.c -m "For $(NEW_VERSION)" + $(SIGNIT) sign $(VERSION_STRING) -r built/production.bin -k 1 -o built/production.bin + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \ + -b $(BOOTLOADER_BASE):bootloader/releases/$(BOOTLOADER_VERSION)/bootloader.bin \ + $(RELEASE_FNAME) + @echo + @echo 'Made release: ' $(RELEASE_FNAME) + @echo + +built/production.bin: + @echo "To make production build, must run docker code" + @false + +# Use DFU to install the latest production version you have on hand +dfu-latest: + $(PYTHON_DO_DFU) -u `ls -t1 ../releases/*.dfu | head -1` + +# Use slow USB upload and reboot method. +latest: + ckcc upgrade `ls -t1 ../releases/*.dfu | head -1` + +.PHONY: code-committed +code-committed: + @echo "" + @echo "Are all changes commited already?" + git diff --stat --exit-code . + @echo '... yes' + +# Sign a message with the contents of ../releases on the developer's machine +.PHONY: sign-release +sign-release: + (cd ../releases; shasum -a 256 *.dfu *.md | sort -rk 2 | \ + gpg --clearsign -u A3A31BAD5A2A5B10 --digest-algo SHA256 --output signatures.txt --yes - ) + git commit -m "Signed for release." ../releases/signatures.txt + +# Tag source code associate with built release version. +# - do "make release" before this step! +# - also edit/commit ChangeLog text too +# - update & sign signatures file +# - and tag everything +tag-source: PUBLIC_VERSION = $(shell $(SIGNIT) version built/production.bin) +tag-source: sign-release code-committed + git commit --allow-empty -am "New release: "$(PUBLIC_VERSION) + echo "Taging version: " $(PUBLIC_VERSION) + git tag -a $(PUBLIC_VERSION) -m "Release "$(PUBLIC_VERSION) + git push + git push --tags + +# DFU file of boot and main code +# - bootloader is last so it can fail if already installed (maybe) +# +mostly.dfu: firmware-signed.bin bootloader/bootloader.bin Makefile + $(PYTHON_MAKE_DFU) \ + -b $(FIRMWARE_BASE):firmware-signed.bin \ + -b $(BOOTLOADER_BASE):bootloader/bootloader.bin $@ + +# send everything +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 + +# useless, will be ignored by bootloader +unsigned-dfu: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy + +# see COLDCARD/mpconfigboard.mk +tags: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) tags +checksum: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) checksum +files: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) files + +# OLD dev junk? +# compile and freeze python code +PY_FILES = $(shell find ../shared -name \*.py) +ALL_MPY_FILES = $(addprefix build/, $(PY_FILES:../shared/%.py=%.mpy)) +MPY_FILES = $(filter-out build/obsolete/%, $(ALL_MPY_FILES)) + +# In another window: +# +# openocd -f openocd_stm32l4x6.cfg +# +# Can do: +# - "load" which writes the flash (medium speed, lots of output on st-util) +# - "cont" starts/continues system +# - "br main" sets breakpoints +# - "mon reset" to reset micro +# - and so on +# +debug: + arm-none-eabi-gdb built/firmware.elf -x gogo.gdb + +# detailed listing, very handy +OBJDUMP = arm-none-eabi-objdump +firmware.lss: l-port/build-COLDCARD/firmware.elf + $(OBJDUMP) -h -S $< > $@ + +# Dump sizes of all frozen py files; requires recent build. +.PHONY: sizes +sizes: + wc -c l-port/build-COLDCARD/frozen_mpy/*.mpy | sort -n + +# Measure flash impact of a single file. Great for before/after. +# make F=foo.py size +# where: foo.py is anything in ../shared +size: + $(MPY_CROSS) -o tmp.mpy -s $F ../shared/$F + wc -c tmp.mpy + +# one time setup, after repo checkout +setup: + cd $(MPY_TOP) ; git submodule update --init lib/stm32lib + cd $(MPY_TOP)/lib/stm32lib ; sed -i.orig -e 's/#define VECT_TAB_OFFSET 0x00/ /' \ + CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.c + cd ../external/libngu; make min-one-time + cd $(MPY_TOP)/mpy-cross ; make + -ln -s $(PORT_TOP) l-port + -ln -s $(MPY_TOP) l-mpy + cd $(PORT_TOP)/boards; if [ ! -L COLDCARD ]; then \ + ln -s ../../../../../stm32/COLDCARD COLDCARD; fi + + +# Caution: docker container has read access to your source tree +# - a readonly copy of source tree, and one output directory +# - build products are copied to there, see repro-build.sh +# - works from this repo, but starts with copy of HEAD +DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \ + -v $(realpath built):/work/built:rw \ + --privileged coldcard-build +repro: code-committed + docker build -t coldcard-build - < dockerfile.build + (cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh) + +# debug: shell into docker container +shell: + docker run -it $(DOCK_RUN_ARGS) sh + +# debug: allow docker to write into source tree +#DOCK_RUN_ARGS := -v $(realpath ..):/work/src:rw --privileged coldcard-build + +PUBLISHED_BIN = $(wildcard ../releases/*-v$(VERSION_STRING)-coldcard.dfu) + +# final step in repro-building: check you got the right bytes +# - but you don't have the production signing key, so that section is removed +check-repro: TRIM_SIG = sed -e 's/^00003f[89abcdef]0 .*/(firmware signature here)/' +check-repro: firmware-signed.bin +ifeq ($(PUBLISHED_BIN),) + @echo "" + @echo "Need published binary for: $(VERSION_STRING)" + @echo "" + @echo "Copy it into ../releases" + @echo "" +else + @echo Comparing against: $(PUBLISHED_BIN) + test -n "$(PUBLISHED_BIN)" -a -f $(PUBLISHED_BIN) + $(RM) -f check-fw.bin check-bootrom.bin + $(SIGNIT) split $(PUBLISHED_BIN) check-fw.bin check-bootrom.bin + $(SIGNIT) check check-fw.bin + $(SIGNIT) check firmware-signed.bin + hexdump -C firmware-signed.bin | $(TRIM_SIG) > repro-got.txt + hexdump -C check-fw.bin | $(TRIM_SIG) > repro-want.txt + diff repro-got.txt repro-want.txt + @echo "" + @echo "SUCCESS. " + @echo "" + @echo "You have built a bit-for-bit identical copy of Coldcard firmware for v$(VERSION_STRING)" +endif + + +# EOF diff --git a/stm32/MK4-Makefile b/stm32/MK4-Makefile new file mode 100644 index 00000000..900fe895 --- /dev/null +++ b/stm32/MK4-Makefile @@ -0,0 +1,293 @@ +# (c) Copyright 2021 by Coinkite Inc. This file is covered by license found in COPYING-CC. +# +# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images. +# +# MARK 4 with different chip and layout +# +MPY_TOP = ../external/micropython +PORT_TOP = $(MPY_TOP)/ports/stm32 +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 + +# aka ../cli/signit.py +SIGNIT = signit + +MAKE_ARGS = BOARD=COLDCARD_MK4 -j 4 EXCLUDE_NGU_TESTS=1 + +all: COLDCARD_MK4/file_time.c + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) + +clean: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) clean + git clean -xf built + +# These trigger the 'all' target when we haven't completed a successful build yet +$(BUILD_DIR)/firmware.elf: all +$(BUILD_DIR)/firmware0.bin: all +$(BUILD_DIR)/firmware1.bin: all + +firmware.elf: $(BUILD_DIR)/firmware.elf + cp $(BUILD_DIR)/firmware.elf . + +# These values used to make .DFU files. Flash memory locations. +FIRMWARE_BASE = 0x08010000 +BOOTLOADER_BASE = 0x08000000 +FILESYSTEM_BASE = 0x080e0000 # XXX + +# Our version for this release. +VERSION_STRING = 5.0.0 + +# +# Sign and merge various parts +# +firmware-signed.bin: $(BUILD_DIR)/firmware0.bin $(BUILD_DIR)/firmware1.bin + $(SIGNIT) sign -b $(BUILD_DIR) -m 4 $(VERSION_STRING) -o $@ +firmware-signed.dfu: firmware-signed.bin Makefile + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):$< $@ + +# make the DFU file which is shared for upgrades +dfu: firmware-signed.dfu + +# Build a binary, signed w/ production key +# - always rebuild binary for this one +.PHONY: dev.dfu +dev.dfu: $(BUILD_DIR)/firmware?.bin + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) + $(SIGNIT) sign -b $(BUILD_DIR) -m 4 $(VERSION_STRING) -k 1 -o dev.bin + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):dev.bin dev.dfu + +.PHONY: remake +remake: + rm -rf $(BUILD_DIR)/firmware?.bin $(BUILD_DIR)/frozen_mpy* + +# This is fast for Coinkite devs, but no DFU support in the wild. +up: dev.dfu + echo 'dfu' | nc localhost 4444 + $(PYTHON_DO_DFU) -u dev.dfu + +# Slow, but works with unmod-ed board: use USB protocol to upgrade (2 minutes) +dev: dev.dfu + ckcc upgrade dev.dfu + +COLDCARD_MK4/file_time.c: make_filetime.py Makefile + ./make_filetime.py COLDCARD_MK4/file_time.c $(VERSION_STRING) + +# Make a factory release: using key #1 +# - when executed in a repro w/o the required key, it defaults to key zero +# - and that's what happens inside the Docker build +production.bin: firmware-signed.bin Makefile + $(SIGNIT) sign -m 4 $(VERSION_STRING) -r firmware-signed.bin -k 1 -o $@ + +# This is release of the bootloader that will be built into the release firmware. +BOOTLOADER_VERSION = 2.0.1 + +.PHONY: release +release: code-committed + $(MAKE) clean + $(MAKE) repro + test -f built/production.bin + $(MAKE) release-products + $(MAKE) tag-source + +# Make a release-candidate, faster. +.PHONY: rc1 +rc1: + $(MAKE) repro + test -f built/production.bin + $(MAKE) release-products + +# This target just combines latest version of production firmware with bootrom into a DFU +# file, stored in ../releases with appropriately dated file name. +.PHONY: release-products +release-products: NEW_VERSION = $(shell $(SIGNIT) version built/production.bin) +release-products: RELEASE_FNAME = ../releases/$(NEW_VERSION)-coldcard.dfu +release-products: built/production.bin + test ! -f $(RELEASE_FNAME) + cp built/file_time.c COLDCARD_MK4/file_time.c + -git commit COLDCARD_MK4/file_time.c -m "For $(NEW_VERSION)" + $(SIGNIT) sign -m 4 $(VERSION_STRING) -r built/production.bin -k 1 -o built/production.bin + $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \ + -b $(BOOTLOADER_BASE):bootloader/releases/$(BOOTLOADER_VERSION)/bootloader.bin \ + $(RELEASE_FNAME) + @echo + @echo 'Made release: ' $(RELEASE_FNAME) + @echo + +built/production.bin: + @echo "To make production build, must run docker code" + @false + +# Use DFU to install the latest production version you have on hand +dfu-latest: + $(PYTHON_DO_DFU) -u `ls -t1 ../releases/*.dfu | head -1` + +# Use slow USB upload and reboot method. +latest: + ckcc upgrade `ls -t1 ../releases/*.dfu | head -1` + +.PHONY: code-committed +code-committed: + @echo "" + @echo "Are all changes commited already?" + git diff --stat --exit-code . + @echo '... yes' + +# Sign a message with the contents of ../releases on the developer's machine +.PHONY: sign-release +sign-release: + (cd ../releases; shasum -a 256 *.dfu *.md | sort -rk 2 | \ + gpg --clearsign -u A3A31BAD5A2A5B10 --digest-algo SHA256 --output signatures.txt --yes - ) + git commit -m "Signed for release." ../releases/signatures.txt + +# Tag source code associate with built release version. +# - do "make release" before this step! +# - also edit/commit ChangeLog text too +# - update & sign signatures file +# - and tag everything +tag-source: PUBLIC_VERSION = $(shell $(SIGNIT) version built/production.bin) +tag-source: sign-release code-committed + git commit --allow-empty -am "New release: "$(PUBLIC_VERSION) + echo "Taging version: " $(PUBLIC_VERSION) + git tag -a $(PUBLIC_VERSION) -m "Release "$(PUBLIC_VERSION) + git push + git push --tags + +# DFU file of boot and main code +# - bootloader is last so it can fail if already installed (maybe) +# +mostly.dfu: firmware-signed.bin bootloader/bootloader.bin Makefile + $(PYTHON_MAKE_DFU) \ + -b $(FIRMWARE_BASE):firmware-signed.bin \ + -b $(BOOTLOADER_BASE):bootloader/bootloader.bin $@ + +# send everything +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 + +# useless, will be ignored by bootloader +unsigned-dfu: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy + +# see COLDCARD_MK4/mpconfigboard.mk +tags: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) tags +checksum: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) checksum +files: + cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) files + +# OLD dev junk? +# compile and freeze python code +PY_FILES = $(shell find ../shared -name \*.py) +ALL_MPY_FILES = $(addprefix build/, $(PY_FILES:../shared/%.py=%.mpy)) +MPY_FILES = $(filter-out build/obsolete/%, $(ALL_MPY_FILES)) + +# In another window: +# +# openocd -f openocd_stm32l4x6.cfg +# +# Can do: +# - "load" which writes the flash (medium speed, lots of output on st-util) +# - "cont" starts/continues system +# - "br main" sets breakpoints +# - "mon reset" to reset micro +# - and so on +# +debug: + arm-none-eabi-gdb $(BUILD_DIR)/firmware.elf -x gogo-mk4.gdb + +# detailed listing, very handy +OBJDUMP = arm-none-eabi-objdump +firmware.lss: $(BUILD_DIR)/firmware.elf + $(OBJDUMP) -h -S $< > $@ + +# Dump sizes of all frozen py files; requires recent build. +.PHONY: sizes +sizes: + wc -c $(BUILD_DIR)/frozen_mpy/*.mpy | sort -n + +# Measure flash impact of a single file. Great for before/after. +# make F=foo.py size +# where: foo.py is anything in ../shared +size: + $(MPY_CROSS) -o tmp.mpy -s $F ../shared/$F + wc -c tmp.mpy + +# one time setup, after repo checkout +setup: + cd $(MPY_TOP) ; git submodule update --init lib/stm32lib + cd $(MPY_TOP)/lib/stm32lib ; sed -i.orig -e 's/#define VECT_TAB_OFFSET 0x00/ /' \ + CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.c + cd ../external/libngu; make min-one-time + cd $(MPY_TOP)/mpy-cross ; make + -ln -s $(PORT_TOP) l-port + -ln -s $(MPY_TOP) l-mpy + cd $(PORT_TOP)/boards; if [ ! -L COLDCARD ]; then \ + ln -s ../../../../../stm32/COLDCARD COLDCARD; fi + cd $(PORT_TOP)/boards; if [ ! -L COLDCARD_MK4 ]; then \ + ln -s ../../../../../stm32/COLDCARD_MK4 COLDCARD_MK4; fi + + +# Caution: docker container has read access to your source tree +# - a readonly copy of source tree, and one output directory +# - build products are copied to there, see repro-build.sh +# - works from this repo, but starts with copy of HEAD +DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \ + -v $(realpath built):/work/built:rw \ + --privileged coldcard-build +repro: code-committed + docker build -t coldcard-build - < dockerfile.build + (cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh) + +# debug: shell into docker container +shell: + docker run -it $(DOCK_RUN_ARGS) sh + +# debug: allow docker to write into source tree +#DOCK_RUN_ARGS := -v $(realpath ..):/work/src:rw --privileged coldcard-build + +PUBLISHED_BIN = $(wildcard ../releases/*-v$(VERSION_STRING)-coldcard.dfu) + +# final step in repro-building: check you got the right bytes +# - but you don't have the production signing key, so that section is removed +check-repro: TRIM_SIG = sed -e 's/^00003f[89abcdef]0 .*/(firmware signature here)/' +check-repro: firmware-signed.bin +ifeq ($(PUBLISHED_BIN),) + @echo "" + @echo "Need published binary for: $(VERSION_STRING)" + @echo "" + @echo "Copy it into ../releases" + @echo "" +else + @echo Comparing against: $(PUBLISHED_BIN) + test -n "$(PUBLISHED_BIN)" -a -f $(PUBLISHED_BIN) + $(RM) -f check-fw.bin check-bootrom.bin + $(SIGNIT) split $(PUBLISHED_BIN) check-fw.bin check-bootrom.bin + $(SIGNIT) check check-fw.bin + $(SIGNIT) check firmware-signed.bin + hexdump -C firmware-signed.bin | $(TRIM_SIG) > repro-got.txt + hexdump -C check-fw.bin | $(TRIM_SIG) > repro-want.txt + diff repro-got.txt repro-want.txt + @echo "" + @echo "SUCCESS. " + @echo "" + @echo "You have built a bit-for-bit identical copy of Coldcard firmware for v$(VERSION_STRING)" +endif + + +# EOF diff --git a/stm32/Makefile b/stm32/Makefile index 02e1d95d..21737bb3 100644 --- a/stm32/Makefile +++ b/stm32/Makefile @@ -1,287 +1 @@ -# (c) Copyright 2018 by Coinkite Inc. This file is covered by license found in COPYING-CC. -# -# Build micropython for stm32 (an ARM processor). Also handles signing of resulting firmware images. -# -MPY_TOP = ../external/micropython -PORT_TOP = $(MPY_TOP)/ports/stm32 -MPY_CROSS = $(MPY_TOP)/mpy-cross/mpy-cross -PYTHON_MAKE_DFU = $(MPY_TOP)/tools/dfu.py -PYTHON_DO_DFU = $(MPY_TOP)/tools/pydfu.py - -# aka ../cli/signit.py -SIGNIT = signit - -MAKE_ARGS = BOARD=COLDCARD -j 4 EXCLUDE_NGU_TESTS=1 - -all: COLDCARD/file_time.c - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) - -clean: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) clean - git clean -xf built - -# These trigger the 'all' target when we haven't completed a successful build yet -l-port/build-COLDCARD/firmware.elf: all -l-port/build-COLDCARD/firmware0.bin: all -l-port/build-COLDCARD/firmware1.bin: all - -firmware.elf: l-port/build-COLDCARD/firmware.elf - cp l-port/build-COLDCARD/firmware.elf . - -# These values used to make .DFU files. Flash memory locations. -FIRMWARE_BASE = 0x08008000 -BOOTLOADER_BASE = 0x08000000 -FILESYSTEM_BASE = 0x080e0000 - -# Our version for this release. -VERSION_STRING = 4.1.1 - -# -# Sign and merge various parts -# -firmware-signed.bin: l-port/build-COLDCARD/firmware0.bin l-port/build-COLDCARD/firmware1.bin - $(SIGNIT) sign $(VERSION_STRING) -o $@ -firmware-signed.dfu: firmware-signed.bin Makefile - $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):$< $@ - -# make the DFU file which is shared for upgrades -dfu: firmware-signed.dfu - -# Build a binary, signed w/ production key -# - always rebuild binary for this one -.PHONY: dev.dfu -dev.dfu: l-port/build-COLDCARD/firmware?.bin - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) - $(SIGNIT) sign $(VERSION_STRING) -k 1 -o dev.bin - $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):dev.bin dev.dfu - -.PHONY: remake -remake: - rm -rf l-port/build-COLDCARD/firmware?.bin l-port/build-COLDCARD/frozen_mpy* - -# This is fast for Coinkite devs, but no DFU support in the wild. -up: dev.dfu - $(PYTHON_DO_DFU) -u dev.dfu - -# Slow, but works with unmod-ed board: use USB protocol to upgrade (2 minutes) -dev: dev.dfu - ckcc upgrade dev.dfu - -COLDCARD/file_time.c: Makefile make_filetime.py - ./make_filetime.py COLDCARD/file_time.c $(VERSION_STRING) - -# Make a factory release: using key #1 -# - when executed in a repro w/o the required key, it defaults to key zero -# - and that's what happens inside the Docker build -production.bin: firmware-signed.bin Makefile - $(SIGNIT) sign $(VERSION_STRING) -r firmware-signed.bin -k 1 -o $@ - -# This is release of the bootloader that will be built into the release firmware. -BOOTLOADER_VERSION = 2.0.1 - -.PHONY: release -release: code-committed - $(MAKE) clean - $(MAKE) repro - test -f built/production.bin - $(MAKE) release-products - $(MAKE) tag-source - -# Make a release-candidate, faster. -.PHONY: rc1 -rc1: - $(MAKE) repro - test -f built/production.bin - $(MAKE) release-products - -# This target just combines latest version of production firmware with bootrom into a DFU -# file, stored in ../releases with appropriately dated file name. -.PHONY: release-products -release-products: NEW_VERSION = $(shell $(SIGNIT) version built/production.bin) -release-products: RELEASE_FNAME = ../releases/$(NEW_VERSION)-coldcard.dfu -release-products: built/production.bin - test ! -f $(RELEASE_FNAME) - cp built/file_time.c COLDCARD/file_time.c - -git commit COLDCARD/file_time.c -m "For $(NEW_VERSION)" - $(SIGNIT) sign $(VERSION_STRING) -r built/production.bin -k 1 -o built/production.bin - $(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \ - -b $(BOOTLOADER_BASE):bootloader/releases/$(BOOTLOADER_VERSION)/bootloader.bin \ - $(RELEASE_FNAME) - @echo - @echo 'Made release: ' $(RELEASE_FNAME) - @echo - -built/production.bin: - @echo "To make production build, must run docker code" - @false - -# Use DFU to install the latest production version you have on hand -dfu-latest: - $(PYTHON_DO_DFU) -u `ls -t1 ../releases/*.dfu | head -1` - -# Use slow USB upload and reboot method. -latest: - ckcc upgrade `ls -t1 ../releases/*.dfu | head -1` - -.PHONY: code-committed -code-committed: - @echo "" - @echo "Are all changes commited already?" - git diff --stat --exit-code . - @echo '... yes' - -# Sign a message with the contents of ../releases on the developer's machine -.PHONY: sign-release -sign-release: - (cd ../releases; shasum -a 256 *.dfu *.md | sort -rk 2 | \ - gpg --clearsign -u A3A31BAD5A2A5B10 --digest-algo SHA256 --output signatures.txt --yes - ) - git commit -m "Signed for release." ../releases/signatures.txt - -# Tag source code associate with built release version. -# - do "make release" before this step! -# - also edit/commit ChangeLog text too -# - update & sign signatures file -# - and tag everything -tag-source: PUBLIC_VERSION = $(shell $(SIGNIT) version built/production.bin) -tag-source: sign-release code-committed - git commit --allow-empty -am "New release: "$(PUBLIC_VERSION) - echo "Taging version: " $(PUBLIC_VERSION) - git tag -a $(PUBLIC_VERSION) -m "Release "$(PUBLIC_VERSION) - git push - git push --tags - -# DFU file of boot and main code -# - bootloader is last so it can fail if already installed (maybe) -# -mostly.dfu: firmware-signed.bin bootloader/bootloader.bin Makefile - $(PYTHON_MAKE_DFU) \ - -b $(FIRMWARE_BASE):firmware-signed.bin \ - -b $(BOOTLOADER_BASE):bootloader/bootloader.bin $@ - -# send everything -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 - -# useless, will be ignored by bootloader -unsigned-dfu: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) deploy - -# see COLDCARD/mpconfigboard.mk -tags: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) tags -checksum: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) checksum -files: - cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) files - -# OLD dev junk? -# compile and freeze python code -PY_FILES = $(shell find ../shared -name \*.py) -ALL_MPY_FILES = $(addprefix build/, $(PY_FILES:../shared/%.py=%.mpy)) -MPY_FILES = $(filter-out build/obsolete/%, $(ALL_MPY_FILES)) - -# In another window: -# -# openocd -f openocd_stm32l4x6.cfg -# -# Can do: -# - "load" which writes the flash (medium speed, lots of output on st-util) -# - "cont" starts/continues system -# - "br main" sets breakpoints -# - "mon reset" to reset micro -# - and so on -# -debug: - arm-none-eabi-gdb built/firmware.elf -x gogo.gdb - -# detailed listing, very handy -OBJDUMP = arm-none-eabi-objdump -firmware.lss: l-port/build-COLDCARD/firmware.elf - $(OBJDUMP) -h -S $< > $@ - -# Dump sizes of all frozen py files; requires recent build. -.PHONY: sizes -sizes: - wc -c l-port/build-COLDCARD/frozen_mpy/*.mpy | sort -n - -# Measure flash impact of a single file. Great for before/after. -# make F=foo.py size -# where: foo.py is anything in ../shared -size: - $(MPY_CROSS) -o tmp.mpy -s $F ../shared/$F - wc -c tmp.mpy - -# one time setup, after repo checkout -setup: - cd $(MPY_TOP) ; git submodule update --init lib/stm32lib - cd $(MPY_TOP)/lib/stm32lib ; sed -i.orig -e 's/#define VECT_TAB_OFFSET 0x00/ /' \ - CMSIS/STM32L4xx/Source/Templates/system_stm32l4xx.c - cd ../external/libngu; make min-one-time - cd $(MPY_TOP)/mpy-cross ; make - -ln -s $(PORT_TOP) l-port - -ln -s $(MPY_TOP) l-mpy - cd $(PORT_TOP)/boards; if [ ! -L COLDCARD ]; then \ - ln -s ../../../../../stm32/COLDCARD COLDCARD; fi - - -# Caution: docker container has read access to your source tree -# - a readonly copy of source tree, and one output directory -# - build products are copied to there, see repro-build.sh -# - works from this repo, but starts with copy of HEAD -DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \ - -v $(realpath built):/work/built:rw \ - --privileged coldcard-build -repro: code-committed - docker build -t coldcard-build - < dockerfile.build - (cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh) - -# debug: shell into docker container -shell: - docker run -it $(DOCK_RUN_ARGS) sh - -# debug: allow docker to write into source tree -#DOCK_RUN_ARGS := -v $(realpath ..):/work/src:rw --privileged coldcard-build - -PUBLISHED_BIN = $(wildcard ../releases/*-v$(VERSION_STRING)-coldcard.dfu) - -# final step in repro-building: check you got the right bytes -# - but you don't have the production signing key, so that section is removed -check-repro: TRIM_SIG = sed -e 's/^00003f[89abcdef]0 .*/(firmware signature here)/' -check-repro: firmware-signed.bin -ifeq ($(PUBLISHED_BIN),) - @echo "" - @echo "Need published binary for: $(VERSION_STRING)" - @echo "" - @echo "Copy it into ../releases" - @echo "" -else - @echo Comparing against: $(PUBLISHED_BIN) - test -n "$(PUBLISHED_BIN)" -a -f $(PUBLISHED_BIN) - $(RM) -f check-fw.bin check-bootrom.bin - $(SIGNIT) split $(PUBLISHED_BIN) check-fw.bin check-bootrom.bin - $(SIGNIT) check check-fw.bin - $(SIGNIT) check firmware-signed.bin - hexdump -C firmware-signed.bin | $(TRIM_SIG) > repro-got.txt - hexdump -C check-fw.bin | $(TRIM_SIG) > repro-want.txt - diff repro-got.txt repro-want.txt - @echo "" - @echo "SUCCESS. " - @echo "" - @echo "You have built a bit-for-bit identical copy of Coldcard firmware for v$(VERSION_STRING)" -endif - - -# EOF +include MK4-Makefile diff --git a/stm32/bootloader/Makefile b/stm32/bootloader/Makefile index b03961c6..734e28be 100644 --- a/stm32/bootloader/Makefile +++ b/stm32/bootloader/Makefile @@ -123,7 +123,7 @@ $(TARGET_ELF): $(OBJS) $(LINKER_SCRIPT) Makefile # raw binary, forced to right size, pad w/ 0xff %.bin: $(TARGET_ELF) $(OBJCOPY) -O binary --pad-to $(BL_FLASH_LAST) --gap-fill 0xff $< $@.tmp - dd bs=30720 count=1 if=$@.tmp of=$@ + dd bs=$(shell printf "%d" $(BL_FLASH_SIZE)) count=1 if=$@.tmp of=$@ # dfu packaged file %.dfu: %.bin diff --git a/stm32/bootloader/README.md b/stm32/bootloader/README.md index a298f0de..773a2ea8 100644 --- a/stm32/bootloader/README.md +++ b/stm32/bootloader/README.md @@ -74,8 +74,12 @@ your key storage per-system unique. This is a useful command, but only works on non-production units: +Mk1-3: dfu-util -d 0483:df11 -a 0 -s 0x08007800:256 -U pairing.bin +Mk4: + dfu-util -d 0483:df11 -a 0 -s 0x0800e000:256 -U pairing.bin + # Resources @@ -85,6 +89,6 @@ This is a useful command, but only works on non-production units: - measure OLED reset and CS pulse lengths, and SPI clk during boot w/ internal RC oscilator - HAL code for SPI should be removed and replaced with a few one-liners -- GPIO code maybe removed as well? +- GPIO code may be removed as well? diff --git a/stm32/bootloader/sigheader.h b/stm32/bootloader/sigheader.h index 6be16829..2e4d6fe2 100644 --- a/stm32/bootloader/sigheader.h +++ b/stm32/bootloader/sigheader.h @@ -30,7 +30,8 @@ typedef struct { uint32_t firmware_length; // must be 512-aligned, and marks start of flash filesystem uint32_t install_flags; // flags about this release uint32_t hw_compat; // which hardware can run this release - uint32_t future[7]; // reserved words + uint8_t best_ts[8]; // for downgrade protection, recommended min timestamp + uint32_t future[5]; // reserved words uint8_t signature[64]; // signature over secp256k1 } coldcardFirmwareHeader_t; @@ -48,9 +49,12 @@ typedef struct { // practical limit for our-protocol USB upgrades: 786432 (or else settings damaged) #define FW_MAX_LENGTH (0x100000 - 0x8000) +// .. for Mk4: 2Mbytes, less bootrom of 64k. SPI flash not limiting factor +#define FW_MAX_LENGTH_MK4 (0x200000 - 0x10000) + // Arguments to be used w/ python's struct module. -#define FWH_PY_FORMAT "flash copy on this bootup? +// - we just did a firmware upgrade on this bootup #define RBF_FRESH_VERSION 0x01 // - factory mode: flash not yet locked-down #define RBF_FACTORY_MODE 0x02 diff --git a/stm32/bootloader/sigheader.py b/stm32/bootloader/sigheader.py index a6b9d28b..f7f9d9e6 100644 --- a/stm32/bootloader/sigheader.py +++ b/stm32/bootloader/sigheader.py @@ -33,9 +33,12 @@ FW_MIN_LENGTH = (256*1024) # practical limit for our-protocol USB upgrades: 786432 (or else settings damaged) FW_MAX_LENGTH = (0x100000 - 0x8000) +# .. for Mk4: 2Mbytes, less bootrom of 64k. SPI flash not limiting factor +FW_MAX_LENGTH_MK4 = (0x200000 - 0x10000) + # Arguments to be used w/ python's struct module. -FWH_PY_FORMAT = "flash copy on this bootup? +# - we just did a firmware upgrade on this bootup RBF_FRESH_VERSION = 0x01 # - factory mode: flash not yet locked-down RBF_FACTORY_MODE = 0x02 diff --git a/stm32/gogo.gdb b/stm32/gogo-mk4.gdb similarity index 76% rename from stm32/gogo.gdb rename to stm32/gogo-mk4.gdb index cfa5a23a..c5f3bf2b 100644 --- a/stm32/gogo.gdb +++ b/stm32/gogo-mk4.gdb @@ -1,7 +1,5 @@ -#exec-file l-port/build-COLDCARD/firmware.elf -exec-file built/firmware.elf -#add-symbol-file l-port/build-COLDCARD/firmware.elf 0x8000000 -add-symbol-file bootloader/bootloader.elf 0x8000000 +# NOTE: main firmware.elf file provided on command line, see Makefile +add-symbol-file mk4-bootloader/bootloader.elf 0x8000000 # hex for all numbers set output-radix 16 diff --git a/stm32/openocd-mk4.cfg b/stm32/openocd-mk4.cfg new file mode 100644 index 00000000..ee28e5f4 --- /dev/null +++ b/stm32/openocd-mk4.cfg @@ -0,0 +1,63 @@ +# This script configures OpenOCD for use with an ST-Link V2 programmer/debugger +# and an STM32L4S5 target microcontroller. +# +# To flash your firmware: +# +# $ openocd -f openocd_stm32l4x6.cfg \ +# -c "stm_flash build-BOARD/firmware0.bin 0x08000000 build-BOARD/firmware1.bin 0x08004000" +# +# For a gdb server on port 3333: +# +# $ openocd -f openocd_stm32l4x6.cfg + + +source [find interface/stlink.cfg] +transport select hla_swd +source [find target/stm32l4x.cfg] + +# from +#reset_config srst_only +reset_config none separate + +init + +proc stm_flash { BIN0 ADDR0 BIN1 ADDR1 } { + reset halt + sleep 100 + wait_halt 2 + flash write_image erase $BIN0 $ADDR0 + sleep 100 + verify_image $BIN0 $ADDR0 + sleep 100 + flash write_image erase $BIN1 $ADDR1 + sleep 100 + verify_image $BIN1 $ADDR1 + sleep 100 + reset run + shutdown +} + +proc stm_erase {} { + reset halt + sleep 100 + stm32l4x mass_erase 0 + sleep 100 + shutdown +} + +proc dfu {} { + # reset and get it started; doesn't work from "reset halt" nor "reset init" + reset run + halt + + # SYSCFG->MEMRMP setup for system flash @ 0x0 + # do __HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH() + mww 0x40010000 0x1 + + # do "mdw 0x0 2" to learn these two values + reg msp 0x20003560 + reg pc 0x1fff51db + + resume + sleep 250 +}