initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
Vendored
+161
@@ -0,0 +1,161 @@
|
||||
on:
|
||||
push:
|
||||
|
||||
name: generate executables
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
name: windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: repo
|
||||
uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
path: asb
|
||||
fetch-depth: 0
|
||||
|
||||
- name: python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: git
|
||||
run: |
|
||||
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
|
||||
git config --global user.name "apollo3-uploader-builder"
|
||||
git config --global pull.ff only
|
||||
cd asb
|
||||
git status
|
||||
git pull
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: install
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
pip --version
|
||||
pip install -r asb/requirements.txt
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cd asb
|
||||
pyinstaller --onefile asb.py
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: copy
|
||||
run: |
|
||||
Remove-Item -Recurse -Force asb\dist\windows
|
||||
mkdir asb\dist\windows
|
||||
Move-Item -Path asb\dist\asb.exe -Destination asb\dist\windows\asb.exe
|
||||
|
||||
- name: commit
|
||||
run: |
|
||||
cd asb
|
||||
git add dist\windows\*
|
||||
git commit -m "generated windows executable"
|
||||
git push
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
linux:
|
||||
name: linux
|
||||
needs: windows
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: repo
|
||||
uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
path: asb
|
||||
fetch-depth: 0
|
||||
|
||||
- name: python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: git
|
||||
run: |
|
||||
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
|
||||
git config --global user.name "apollo3-uploader-builder"
|
||||
git config --global pull.ff only
|
||||
cd asb
|
||||
git pull
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: install
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
pip --version
|
||||
pip install -r asb/requirements.txt
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cd asb
|
||||
pyinstaller --onefile asb.py
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: copy
|
||||
run: |
|
||||
rm -rf ./asb/dist/linux
|
||||
mkdir -p ./asb/dist/linux
|
||||
mv ./asb/dist/asb ./asb/dist/linux/asb
|
||||
|
||||
- name: commit
|
||||
run: |
|
||||
cd ./asb
|
||||
git add ./dist/linux/*
|
||||
git commit -m "generated linux executable"
|
||||
git push
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
macosx:
|
||||
name: macosx
|
||||
needs: linux
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: repo
|
||||
uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
path: asb
|
||||
fetch-depth: 0
|
||||
|
||||
- name: python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: git
|
||||
run: |
|
||||
git config --global user.email "apollo3-uploader-builder@sparkfun.com"
|
||||
git config --global user.name "apollo3-uploader-builder"
|
||||
git config --global pull.ff only
|
||||
cd asb
|
||||
git pull
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: install
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip setuptools wheel
|
||||
pip --version
|
||||
pip install -r asb/requirements.txt
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cd asb
|
||||
pyinstaller --onefile asb.py
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
|
||||
- name: copy
|
||||
run: |
|
||||
rm -rf ./asb/dist/macosx
|
||||
mkdir -p ./asb/dist/macosx
|
||||
mv ./asb/dist/asb ./asb/dist/macosx/asb
|
||||
|
||||
- name: commit
|
||||
run: |
|
||||
cd ./asb
|
||||
git add ./dist/macosx/*
|
||||
git commit -m "generated macosx executable"
|
||||
git push
|
||||
cd ${GITHUB_WORKSPACE}
|
||||
@@ -0,0 +1,127 @@
|
||||
# macosx
|
||||
.DS_Store
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
@@ -0,0 +1,3 @@
|
||||

|
||||
|
||||
# Apollo3 Uploader - Ambiq Secure Bootloader (SVL)
|
||||
@@ -0,0 +1,527 @@
|
||||
#!/usr/bin/env python3
|
||||
# Utility functioins
|
||||
|
||||
import sys
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.PublicKey import RSA
|
||||
from Crypto.Signature import PKCS1_v1_5
|
||||
from Crypto.Hash import SHA256
|
||||
import array
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
import binascii
|
||||
|
||||
|
||||
ivVal0 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
|
||||
FLASH_PAGE_SIZE = 0x2000 # 8K
|
||||
MAX_DOWNLOAD_SIZE = 0x48000 # 288K
|
||||
AM_SECBOOT_DEFAULT_NONSECURE_MAIN = 0xC000
|
||||
|
||||
AM_SECBOOT_AESCBC_BLOCK_SIZE_WORDS = 4
|
||||
AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES = 4*AM_SECBOOT_AESCBC_BLOCK_SIZE_WORDS
|
||||
|
||||
AM_SECBOOT_MIN_KEYIDX_INFO0 = 8 ## KeyIdx 8 - 15
|
||||
AM_SECBOOT_MAX_KEYIDX_INFO0 = 15
|
||||
AM_SECBOOT_MIN_KEYIDX_INFO1 = 0 ## KeyIdx 0 - 7
|
||||
AM_SECBOOT_MAX_KEYIDX_INFO1 = 7
|
||||
AM_SECBOOT_KEYIDX_BYTES = 16
|
||||
|
||||
# Encryption Algorithm
|
||||
AM_SECBOOT_ENC_ALGO_NONE = 0
|
||||
AM_SECBOOT_ENC_ALGO_AES128 = 1
|
||||
AM_SECBOOT_ENC_ALGO_MAX = AM_SECBOOT_ENC_ALGO_AES128
|
||||
# String constants
|
||||
helpEncAlgo = 'Encryption Algo? (0(default) = none, 1 = AES128)'
|
||||
|
||||
# Authentication Algorithm
|
||||
AM_SECBOOT_AUTH_ALGO_NONE = 0
|
||||
AM_SECBOOT_AUTH_ALGO_SHA256HMAC = 1
|
||||
AM_SECBOOT_AUTH_ALGO_MAX = AM_SECBOOT_AUTH_ALGO_SHA256HMAC
|
||||
# String constants
|
||||
helpAuthAlgo = 'Authentication Algo? (0(default) = none, 1 = SHA256)'
|
||||
|
||||
|
||||
FLASH_INVALID = 0xFFFFFFFF
|
||||
|
||||
# KeyWrap Mode
|
||||
AM_SECBOOT_KEYWRAP_NONE = 0
|
||||
AM_SECBOOT_KEYWRAP_XOR = 1
|
||||
AM_SECBOOT_KEYWRAP_AES128 = 2
|
||||
AM_SECBOOT_KEYWRAP_MAX = AM_SECBOOT_KEYWRAP_AES128
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Magic Numbers
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_IMAGE_MAGIC_MAIN = 0xC0
|
||||
AM_IMAGE_MAGIC_CHILD = 0xCC
|
||||
AM_IMAGE_MAGIC_NONSECURE = 0xCB
|
||||
AM_IMAGE_MAGIC_INFO0 = 0xCF
|
||||
|
||||
# Dummy for creating images for customer - not understood by SBL
|
||||
# This could be any value from the definition:
|
||||
# #define AM_IMAGE_MAGIC_CUST(x) ((((x) & 0xF0) == 0xC0) && ((x) != 0xC0) && ((x) != 0xCC) && ((x) != 0xCB) && ((x) != 0xCF))
|
||||
AM_IMAGE_MAGIC_CUSTPATCH = 0xC1
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Image Types
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_SBL = 0
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_AM3P = 1
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_PATCH = 2
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_MAIN = 3
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_CHILD = 4
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH = 5
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE = 6
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_INFO0 = 7
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA = 32
|
||||
AM_SECBOOT_WIRED_IMAGETYPE_INVALID = 0xFF
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Wired Message Types
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_SECBOOT_WIRED_MSGTYPE_HELLO = 0
|
||||
AM_SECBOOT_WIRED_MSGTYPE_STATUS = 1
|
||||
AM_SECBOOT_WIRED_MSGTYPE_OTADESC = 2
|
||||
AM_SECBOOT_WIRED_MSGTYPE_UPDATE = 3
|
||||
AM_SECBOOT_WIRED_MSGTYPE_ABORT = 4
|
||||
AM_SECBOOT_WIRED_MSGTYPE_RECOVER = 5
|
||||
AM_SECBOOT_WIRED_MSGTYPE_RESET = 6
|
||||
AM_SECBOOT_WIRED_MSGTYPE_ACK = 7
|
||||
AM_SECBOOT_WIRED_MSGTYPE_DATA = 8
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Wired Message ACK Status
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_SUCCESS = 0
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_FAILURE = 1
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_INFO0 = 2
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_CRC = 3
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_SEC = 4
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_MSG_TOO_BIG = 5
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_UNKNOWN_MSGTYPE = 6
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_ADDR = 7
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_OPERATION = 8
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_INVALID_PARAM = 9
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_SEQ = 10
|
||||
AM_SECBOOT_WIRED_ACK_STATUS_TOO_MUCH_DATA = 11
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Definitions related to Image Headers
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_HMAC_SIG_SIZE = 32
|
||||
AM_KEK_SIZE = 16
|
||||
AM_CRC_SIZE = 4
|
||||
|
||||
AM_MAX_UART_MSG_SIZE = 8192 # 8K buffer in SBL
|
||||
|
||||
# Wiredupdate Image Header
|
||||
AM_WU_IMAGEHDR_OFFSET_SIG = 16
|
||||
AM_WU_IMAGEHDR_OFFSET_IV = 48
|
||||
AM_WU_IMAGEHDR_OFFSET_KEK = 64
|
||||
AM_WU_IMAGEHDR_OFFSET_IMAGETYPE = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
||||
AM_WU_IMAGEHDR_OFFSET_OPTIONS = (AM_WU_IMAGEHDR_OFFSET_IMAGETYPE + 1)
|
||||
AM_WU_IMAGEHDR_OFFSET_KEY = (AM_WU_IMAGEHDR_OFFSET_IMAGETYPE + 4)
|
||||
AM_WU_IMAGEHDR_OFFSET_ADDR = (AM_WU_IMAGEHDR_OFFSET_KEY + 4)
|
||||
AM_WU_IMAGEHDR_OFFSET_SIZE = (AM_WU_IMAGEHDR_OFFSET_ADDR + 4)
|
||||
|
||||
AM_WU_IMAGEHDR_START_HMAC = (AM_WU_IMAGEHDR_OFFSET_SIG + AM_HMAC_SIG_SIZE)
|
||||
AM_WU_IMAGEHDR_START_ENCRYPT = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
||||
AM_WU_IMAGEHDR_SIZE = (AM_WU_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE + 16)
|
||||
|
||||
|
||||
# Image Header
|
||||
AM_IMAGEHDR_SIZE_MAIN = 256
|
||||
AM_IMAGEHDR_SIZE_AUX = (112 + AM_KEK_SIZE)
|
||||
|
||||
AM_IMAGEHDR_OFFSET_CRC = 4
|
||||
AM_IMAGEHDR_OFFSET_SIG = 16
|
||||
AM_IMAGEHDR_OFFSET_IV = 48
|
||||
AM_IMAGEHDR_OFFSET_KEK = 64
|
||||
AM_IMAGEHDR_OFFSET_SIGCLR = (AM_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
||||
AM_IMAGEHDR_START_CRC = (AM_IMAGEHDR_OFFSET_CRC + AM_CRC_SIZE)
|
||||
AM_IMAGEHDR_START_HMAC_INST = (AM_IMAGEHDR_OFFSET_SIG + AM_HMAC_SIG_SIZE)
|
||||
AM_IMAGEHDR_START_ENCRYPT = (AM_IMAGEHDR_OFFSET_KEK + AM_KEK_SIZE)
|
||||
AM_IMAGEHDR_START_HMAC = (AM_IMAGEHDR_OFFSET_SIGCLR + AM_HMAC_SIG_SIZE)
|
||||
AM_IMAGEHDR_OFFSET_ADDR = AM_IMAGEHDR_START_HMAC
|
||||
AM_IMAGEHDR_OFFSET_VERKEY = (AM_IMAGEHDR_OFFSET_ADDR + 4)
|
||||
AM_IMAGEHDR_OFFSET_CHILDPTR = (AM_IMAGEHDR_OFFSET_VERKEY + 4)
|
||||
|
||||
# Recover message
|
||||
AM_WU_RECOVERY_HDR_SIZE = 44
|
||||
AM_WU_RECOVERY_HDR_OFFSET_CUSTID = 8
|
||||
AM_WU_RECOVERY_HDR_OFFSET_RECKEY = (AM_WU_RECOVERY_HDR_OFFSET_CUSTID + 4)
|
||||
AM_WU_RECOVERY_HDR_OFFSET_NONCE = (AM_WU_RECOVERY_HDR_OFFSET_RECKEY + 16)
|
||||
AM_WU_RECOVERY_HDR_OFFSET_RECBLOB = (AM_WU_RECOVERY_HDR_OFFSET_NONCE + 16)
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# INFOSPACE related definitions
|
||||
#
|
||||
#******************************************************************************
|
||||
AM_SECBOOT_INFO0_SIGN_PROGRAMMED0 = 0x48EAAD88
|
||||
AM_SECBOOT_INFO0_SIGN_PROGRAMMED1 = 0xC9705737
|
||||
AM_SECBOOT_INFO0_SIGN_PROGRAMMED2 = 0x0A6B8458
|
||||
AM_SECBOOT_INFO0_SIGN_PROGRAMMED3 = 0xE41A9D74
|
||||
|
||||
AM_SECBOOT_INFO0_SIGN_UINIT0 = 0x5B75A5FA
|
||||
AM_SECBOOT_INFO0_SIGN_UINIT1 = 0x7B9C8674
|
||||
AM_SECBOOT_INFO0_SIGN_UINIT2 = 0x869A96FE
|
||||
AM_SECBOOT_INFO0_SIGN_UINIT3 = 0xAEC90860
|
||||
|
||||
INFO_SIZE_BYTES = (8 * 1024)
|
||||
INFO_MAX_AUTH_KEY_WORDS = 32
|
||||
INFO_MAX_ENC_KEY_WORDS = 32
|
||||
|
||||
INFO_MAX_AUTH_KEYS = (INFO_MAX_AUTH_KEY_WORDS*4//AM_SECBOOT_KEYIDX_BYTES)
|
||||
INFO_MAX_ENC_KEYS = (INFO_MAX_ENC_KEY_WORDS*4//AM_SECBOOT_KEYIDX_BYTES)
|
||||
|
||||
INFO0_SIGNATURE0_O = 0x00000000
|
||||
INFO0_SIGNATURE1_O = 0x00000004
|
||||
INFO0_SIGNATURE2_O = 0x00000008
|
||||
INFO0_SIGNATURE3_O = 0x0000000c
|
||||
INFO0_SECURITY_O = 0x00000010
|
||||
INFO0_CUSTOMER_TRIM_O = 0x00000014
|
||||
INFO0_CUSTOMER_TRIM2_O = 0x00000018
|
||||
INFO0_SECURITY_OVR_O = 0x00000020
|
||||
INFO0_SECURITY_WIRED_CFG_O = 0x00000024
|
||||
INFO0_SECURITY_WIRED_IFC_CFG0_O = 0x00000028
|
||||
INFO0_SECURITY_WIRED_IFC_CFG1_O = 0x0000002C
|
||||
INFO0_SECURITY_WIRED_IFC_CFG2_O = 0x00000030
|
||||
INFO0_SECURITY_WIRED_IFC_CFG3_O = 0x00000034
|
||||
INFO0_SECURITY_WIRED_IFC_CFG4_O = 0x00000038
|
||||
INFO0_SECURITY_WIRED_IFC_CFG5_O = 0x0000003C
|
||||
INFO0_SECURITY_VERSION_O = 0x00000040
|
||||
INFO0_SECURITY_SRAM_RESV_O = 0x00000050
|
||||
AM_REG_INFO0_SECURITY_SRAM_RESV_SRAM_RESV_M = 0x0000FFFF
|
||||
INFO0_WRITE_PROTECT_L_O = 0x000001f8
|
||||
INFO0_WRITE_PROTECT_H_O = 0x000001fc
|
||||
INFO0_COPY_PROTECT_L_O = 0x00000200
|
||||
INFO0_COPY_PROTECT_H_O = 0x00000204
|
||||
INFO0_WRITE_PROTECT_SBL_L_O = 0x000009f8
|
||||
INFO0_WRITE_PROTECT_SBL_H_O = 0x000009fc
|
||||
INFO0_COPY_PROTECT_SBL_L_O = 0x00000A00
|
||||
INFO0_COPY_PROTECT_SBL_H_O = 0x00000A04
|
||||
INFO0_MAIN_PTR1_O = 0x00000C00
|
||||
INFO0_MAIN_PTR2_O = 0x00000C04
|
||||
INFO0_KREVTRACK_O = 0x00000C08
|
||||
INFO0_AREVTRACK_O = 0x00000C0C
|
||||
INFO0_MAIN_CNT0_O = 0x00000FF8
|
||||
INFO0_MAIN_CNT1_O = 0x00000FFC
|
||||
|
||||
INFO0_CUST_KEK_W0_O = 0x00001800
|
||||
INFO0_CUST_KEK_W1_O = 0x00001804
|
||||
INFO0_CUST_KEK_W2_O = 0x00001808
|
||||
INFO0_CUST_KEK_W3_O = 0x0000180c
|
||||
INFO0_CUST_KEK_W4_O = 0x00001810
|
||||
INFO0_CUST_KEK_W5_O = 0x00001814
|
||||
INFO0_CUST_KEK_W6_O = 0x00001818
|
||||
INFO0_CUST_KEK_W7_O = 0x0000181c
|
||||
INFO0_CUST_KEK_W8_O = 0x00001820
|
||||
INFO0_CUST_KEK_W9_O = 0x00001824
|
||||
INFO0_CUST_KEK_W10_O = 0x00001828
|
||||
INFO0_CUST_KEK_W11_O = 0x0000182c
|
||||
INFO0_CUST_KEK_W12_O = 0x00001830
|
||||
INFO0_CUST_KEK_W13_O = 0x00001834
|
||||
INFO0_CUST_KEK_W14_O = 0x00001838
|
||||
INFO0_CUST_KEK_W15_O = 0x0000183c
|
||||
INFO0_CUST_KEK_W16_O = 0x00001840
|
||||
INFO0_CUST_KEK_W17_O = 0x00001844
|
||||
INFO0_CUST_KEK_W18_O = 0x00001848
|
||||
INFO0_CUST_KEK_W19_O = 0x0000184c
|
||||
INFO0_CUST_KEK_W20_O = 0x00001850
|
||||
INFO0_CUST_KEK_W21_O = 0x00001854
|
||||
INFO0_CUST_KEK_W22_O = 0x00001858
|
||||
INFO0_CUST_KEK_W23_O = 0x0000185c
|
||||
INFO0_CUST_KEK_W24_O = 0x00001860
|
||||
INFO0_CUST_KEK_W25_O = 0x00001864
|
||||
INFO0_CUST_KEK_W26_O = 0x00001868
|
||||
INFO0_CUST_KEK_W27_O = 0x0000186c
|
||||
INFO0_CUST_KEK_W28_O = 0x00001870
|
||||
INFO0_CUST_KEK_W29_O = 0x00001874
|
||||
INFO0_CUST_KEK_W30_O = 0x00001878
|
||||
INFO0_CUST_KEK_W31_O = 0x0000187c
|
||||
INFO0_CUST_AUTH_W0_O = 0x00001880
|
||||
INFO0_CUST_AUTH_W1_O = 0x00001884
|
||||
INFO0_CUST_AUTH_W2_O = 0x00001888
|
||||
INFO0_CUST_AUTH_W3_O = 0x0000188c
|
||||
INFO0_CUST_AUTH_W4_O = 0x00001890
|
||||
INFO0_CUST_AUTH_W5_O = 0x00001894
|
||||
INFO0_CUST_AUTH_W6_O = 0x00001898
|
||||
INFO0_CUST_AUTH_W7_O = 0x0000189c
|
||||
INFO0_CUST_AUTH_W8_O = 0x000018a0
|
||||
INFO0_CUST_AUTH_W9_O = 0x000018a4
|
||||
INFO0_CUST_AUTH_W10_O = 0x000018a8
|
||||
INFO0_CUST_AUTH_W11_O = 0x000018ac
|
||||
INFO0_CUST_AUTH_W12_O = 0x000018b0
|
||||
INFO0_CUST_AUTH_W13_O = 0x000018b4
|
||||
INFO0_CUST_AUTH_W14_O = 0x000018b8
|
||||
INFO0_CUST_AUTH_W15_O = 0x000018bc
|
||||
INFO0_CUST_AUTH_W16_O = 0x000018c0
|
||||
INFO0_CUST_AUTH_W17_O = 0x000018c4
|
||||
INFO0_CUST_AUTH_W18_O = 0x000018c8
|
||||
INFO0_CUST_AUTH_W19_O = 0x000018cc
|
||||
INFO0_CUST_AUTH_W20_O = 0x000018d0
|
||||
INFO0_CUST_AUTH_W21_O = 0x000018d4
|
||||
INFO0_CUST_AUTH_W22_O = 0x000018d8
|
||||
INFO0_CUST_AUTH_W23_O = 0x000018dc
|
||||
INFO0_CUST_AUTH_W24_O = 0x000018e0
|
||||
INFO0_CUST_AUTH_W25_O = 0x000018e4
|
||||
INFO0_CUST_AUTH_W26_O = 0x000018e8
|
||||
INFO0_CUST_AUTH_W27_O = 0x000018ec
|
||||
INFO0_CUST_AUTH_W28_O = 0x000018f0
|
||||
INFO0_CUST_AUTH_W29_O = 0x000018f4
|
||||
INFO0_CUST_AUTH_W30_O = 0x000018f8
|
||||
INFO0_CUST_AUTH_W31_O = 0x000018fc
|
||||
INFO0_CUST_PUBKEY_W0_O = 0x00001900
|
||||
INFO0_CUST_PUBKEY_W1_O = 0x00001904
|
||||
INFO0_CUST_PUBKEY_W2_O = 0x00001908
|
||||
INFO0_CUST_PUBKEY_W3_O = 0x0000190c
|
||||
INFO0_CUST_PUBKEY_W4_O = 0x00001910
|
||||
INFO0_CUST_PUBKEY_W5_O = 0x00001914
|
||||
INFO0_CUST_PUBKEY_W6_O = 0x00001918
|
||||
INFO0_CUST_PUBKEY_W7_O = 0x0000191c
|
||||
INFO0_CUST_PUBKEY_W8_O = 0x00001920
|
||||
INFO0_CUST_PUBKEY_W9_O = 0x00001924
|
||||
INFO0_CUST_PUBKEY_W10_O = 0x00001928
|
||||
INFO0_CUST_PUBKEY_W11_O = 0x0000192c
|
||||
INFO0_CUST_PUBKEY_W12_O = 0x00001930
|
||||
INFO0_CUST_PUBKEY_W13_O = 0x00001934
|
||||
INFO0_CUST_PUBKEY_W14_O = 0x00001938
|
||||
INFO0_CUST_PUBKEY_W15_O = 0x0000193c
|
||||
INFO0_CUST_PUBKEY_W16_O = 0x00001940
|
||||
INFO0_CUST_PUBKEY_W17_O = 0x00001944
|
||||
INFO0_CUST_PUBKEY_W18_O = 0x00001948
|
||||
INFO0_CUST_PUBKEY_W19_O = 0x0000194c
|
||||
INFO0_CUST_PUBKEY_W20_O = 0x00001950
|
||||
INFO0_CUST_PUBKEY_W21_O = 0x00001954
|
||||
INFO0_CUST_PUBKEY_W22_O = 0x00001958
|
||||
INFO0_CUST_PUBKEY_W23_O = 0x0000195c
|
||||
INFO0_CUST_PUBKEY_W24_O = 0x00001960
|
||||
INFO0_CUST_PUBKEY_W25_O = 0x00001964
|
||||
INFO0_CUST_PUBKEY_W26_O = 0x00001968
|
||||
INFO0_CUST_PUBKEY_W27_O = 0x0000196c
|
||||
INFO0_CUST_PUBKEY_W28_O = 0x00001970
|
||||
INFO0_CUST_PUBKEY_W29_O = 0x00001974
|
||||
INFO0_CUST_PUBKEY_W30_O = 0x00001978
|
||||
INFO0_CUST_PUBKEY_W31_O = 0x0000197c
|
||||
INFO0_CUST_PUBKEY_W32_O = 0x00001980
|
||||
INFO0_CUST_PUBKEY_W33_O = 0x00001984
|
||||
INFO0_CUST_PUBKEY_W34_O = 0x00001988
|
||||
INFO0_CUST_PUBKEY_W35_O = 0x0000198c
|
||||
INFO0_CUST_PUBKEY_W36_O = 0x00001990
|
||||
INFO0_CUST_PUBKEY_W37_O = 0x00001994
|
||||
INFO0_CUST_PUBKEY_W38_O = 0x00001998
|
||||
INFO0_CUST_PUBKEY_W39_O = 0x0000199c
|
||||
INFO0_CUST_PUBKEY_W40_O = 0x000019a0
|
||||
INFO0_CUST_PUBKEY_W41_O = 0x000019a4
|
||||
INFO0_CUST_PUBKEY_W42_O = 0x000019a8
|
||||
INFO0_CUST_PUBKEY_W43_O = 0x000019ac
|
||||
INFO0_CUST_PUBKEY_W44_O = 0x000019b0
|
||||
INFO0_CUST_PUBKEY_W45_O = 0x000019b4
|
||||
INFO0_CUST_PUBKEY_W46_O = 0x000019b8
|
||||
INFO0_CUST_PUBKEY_W47_O = 0x000019bc
|
||||
INFO0_CUST_PUBKEY_W48_O = 0x000019c0
|
||||
INFO0_CUST_PUBKEY_W49_O = 0x000019c4
|
||||
INFO0_CUST_PUBKEY_W50_O = 0x000019c8
|
||||
INFO0_CUST_PUBKEY_W51_O = 0x000019cc
|
||||
INFO0_CUST_PUBKEY_W52_O = 0x000019d0
|
||||
INFO0_CUST_PUBKEY_W53_O = 0x000019d4
|
||||
INFO0_CUST_PUBKEY_W54_O = 0x000019d8
|
||||
INFO0_CUST_PUBKEY_W55_O = 0x000019dc
|
||||
INFO0_CUST_PUBKEY_W56_O = 0x000019e0
|
||||
INFO0_CUST_PUBKEY_W57_O = 0x000019e4
|
||||
INFO0_CUST_PUBKEY_W58_O = 0x000019e8
|
||||
INFO0_CUST_PUBKEY_W59_O = 0x000019ec
|
||||
INFO0_CUST_PUBKEY_W60_O = 0x000019f0
|
||||
INFO0_CUST_PUBKEY_W61_O = 0x000019f4
|
||||
INFO0_CUST_PUBKEY_W62_O = 0x000019f8
|
||||
INFO0_CUST_PUBKEY_W63_O = 0x000019fc
|
||||
INFO0_CUSTOMER_KEY0_O = 0x00001a00
|
||||
INFO0_CUSTOMER_KEY1_O = 0x00001a04
|
||||
INFO0_CUSTOMER_KEY2_O = 0x00001a08
|
||||
INFO0_CUSTOMER_KEY3_O = 0x00001a0c
|
||||
INFO0_CUST_PUBHASH_W0_O = 0x00001a10
|
||||
INFO0_CUST_PUBHASH_W1_O = 0x00001a14
|
||||
INFO0_CUST_PUBHASH_W2_O = 0x00001a18
|
||||
INFO0_CUST_PUBHASH_W3_O = 0x00001a1c
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# CRC using ethernet poly, as used by Corvette hardware for validation
|
||||
#
|
||||
#******************************************************************************
|
||||
def crc32(L):
|
||||
return (binascii.crc32(L) & 0xFFFFFFFF)
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Pad the text to the block_size. bZeroPad determines how to handle text which
|
||||
# is already multiple of block_size
|
||||
#
|
||||
#******************************************************************************
|
||||
def pad_to_block_size(text, block_size, bZeroPad):
|
||||
text_length = len(text)
|
||||
amount_to_pad = block_size - (text_length % block_size)
|
||||
if (amount_to_pad == block_size):
|
||||
if (bZeroPad == 0):
|
||||
amount_to_pad = 0
|
||||
for i in range(0, amount_to_pad, 1):
|
||||
text += bytes(chr(amount_to_pad), 'ascii')
|
||||
return text
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# AES CBC encryption
|
||||
#
|
||||
#******************************************************************************
|
||||
def encrypt_app_aes(cleartext, encKey, iv):
|
||||
key = array.array('B', encKey).tostring()
|
||||
ivVal = array.array('B', iv).tostring()
|
||||
plaintext = array.array('B', cleartext).tostring()
|
||||
|
||||
encryption_suite = AES.new(key, AES.MODE_CBC, ivVal)
|
||||
cipher_text = encryption_suite.encrypt(plaintext)
|
||||
|
||||
return cipher_text
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# AES 128 CBC encryption
|
||||
#
|
||||
#******************************************************************************
|
||||
def encrypt_app_aes128(cleartext, encKey, iv):
|
||||
key = array.array('B', encKey).tostring()
|
||||
ivVal = array.array('B', iv).tostring()
|
||||
plaintext = array.array('B', cleartext).tostring()
|
||||
|
||||
encryption_suite = AES.new(key, AES.MODE_CBC, ivVal)
|
||||
cipher_text = encryption_suite.encrypt(plaintext)
|
||||
|
||||
return cipher_text
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# SHA256 HMAC
|
||||
#
|
||||
#******************************************************************************
|
||||
def compute_hmac(key, data):
|
||||
sig = hmac.new(array.array('B', key).tostring(), array.array('B', data).tostring(), hashlib.sha256).digest()
|
||||
return sig
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# RSA PKCS1_v1_5 sign
|
||||
#
|
||||
#******************************************************************************
|
||||
def compute_rsa_sign(prvKeyFile, data):
|
||||
key = open(prvKeyFile, "r").read()
|
||||
rsakey = RSA.importKey(key)
|
||||
signer = PKCS1_v1_5.new(rsakey)
|
||||
digest = SHA256.new()
|
||||
digest.update(bytes(data))
|
||||
sign = signer.sign(digest)
|
||||
return sign
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# RSA PKCS1_v1_5 sign verification
|
||||
#
|
||||
#******************************************************************************
|
||||
def verify_rsa_sign(pubKeyFile, data, sign):
|
||||
key = open(pubKeyFile, "r").read()
|
||||
rsakey = RSA.importKey(key)
|
||||
#print(hex(rsakey.n))
|
||||
verifier = PKCS1_v1_5.new(rsakey)
|
||||
digest = SHA256.new()
|
||||
digest.update(bytes(data))
|
||||
return verifier.verify(digest, sign)
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Fill one word in bytearray
|
||||
#
|
||||
#******************************************************************************
|
||||
def fill_word(barray, offset, w):
|
||||
barray[offset + 0] = (w >> 0) & 0x000000ff;
|
||||
barray[offset + 1] = (w >> 8) & 0x000000ff;
|
||||
barray[offset + 2] = (w >> 16) & 0x000000ff;
|
||||
barray[offset + 3] = (w >> 24) & 0x000000ff;
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Turn a 32-bit number into a series of bytes for transmission.
|
||||
#
|
||||
# This command will split a 32-bit integer into an array of bytes, ordered
|
||||
# LSB-first for transmission over the UART.
|
||||
#
|
||||
#******************************************************************************
|
||||
def int_to_bytes(n):
|
||||
A = [n & 0xFF,
|
||||
(n >> 8) & 0xFF,
|
||||
(n >> 16) & 0xFF,
|
||||
(n >> 24) & 0xFF]
|
||||
|
||||
return A
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Extract a word from a byte array
|
||||
#
|
||||
#******************************************************************************
|
||||
def word_from_bytes(B, n):
|
||||
return (B[n] + (B[n + 1] << 8) + (B[n + 2] << 16) + (B[n + 3] << 24))
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# automatically figure out the integer format (base 10 or 16)
|
||||
#
|
||||
#******************************************************************************
|
||||
def auto_int(x):
|
||||
return int(x, 0)
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# User controllable Prints control
|
||||
#
|
||||
#******************************************************************************
|
||||
# Defined print levels
|
||||
AM_PRINT_LEVEL_MIN = 0
|
||||
AM_PRINT_LEVEL_NONE = AM_PRINT_LEVEL_MIN
|
||||
AM_PRINT_LEVEL_ERROR = 1
|
||||
AM_PRINT_LEVEL_INFO = 2
|
||||
AM_PRINT_LEVEL_VERBOSE = 4
|
||||
AM_PRINT_LEVEL_DEBUG = 5
|
||||
AM_PRINT_LEVEL_MAX = AM_PRINT_LEVEL_DEBUG
|
||||
|
||||
# Global variable to control the prints
|
||||
AM_PRINT_VERBOSITY = AM_PRINT_LEVEL_INFO
|
||||
|
||||
helpPrintLevel = 'Set Log Level (0: None), (1: Error), (2: INFO), (4: Verbose), (5: Debug) [Default = Info]'
|
||||
|
||||
def am_set_print_level(level):
|
||||
global AM_PRINT_VERBOSITY
|
||||
AM_PRINT_VERBOSITY = level
|
||||
|
||||
def am_print(*args, level=AM_PRINT_LEVEL_INFO, **kwargs):
|
||||
global AM_PRINT_VERBOSITY
|
||||
if (AM_PRINT_VERBOSITY >= level):
|
||||
print(*args, **kwargs)
|
||||
@@ -0,0 +1,899 @@
|
||||
#!/usr/bin/env python3
|
||||
# Combination of the three steps to take an 'application.bin' file and run it on a SparkFun Artemis module
|
||||
|
||||
# Information:
|
||||
# This script performs the three main tasks:
|
||||
# 1. Convert 'application.bin' to an OTA update blob
|
||||
# 2. Convert the OTA blob into a wired update blob
|
||||
# 3. Push the wired update blob into the Artemis module
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from Crypto.Cipher import AES
|
||||
import array
|
||||
import hashlib
|
||||
import hmac
|
||||
import os
|
||||
import binascii
|
||||
import serial
|
||||
import serial.tools.list_ports as list_ports
|
||||
import time
|
||||
# from sf_am_defines import *
|
||||
from sys import exit
|
||||
|
||||
from am_defines import *
|
||||
from keys_info import keyTblAes, keyTblHmac, minAesKeyIdx, maxAesKeyIdx, minHmacKeyIdx, maxHmacKeyIdx, INFO_KEY, FLASH_KEY
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Global Variables
|
||||
#
|
||||
#******************************************************************************
|
||||
loadTries = 0 #If we fail, try again. Tracks the number of tries we've attempted
|
||||
loadSuccess = False
|
||||
blob2wiredfile = ''
|
||||
uploadbinfile = ''
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Generate the image blob as per command line parameters
|
||||
#
|
||||
#******************************************************************************
|
||||
def bin2blob_process(loadaddress, appFile, magicNum, crcI, crcB, authI, authB, protection, authKeyIdx, output, encKeyIdx, version, erasePrev, child0, child1, authalgo, encalgo):
|
||||
|
||||
global blob2wiredfile
|
||||
|
||||
app_binarray = bytearray()
|
||||
# Open the file, and read it into an array of integers.
|
||||
with appFile as f_app:
|
||||
app_binarray.extend(f_app.read())
|
||||
f_app.close()
|
||||
|
||||
encVal = 0
|
||||
if (encalgo != 0):
|
||||
encVal = 1
|
||||
if ((encKeyIdx < minAesKeyIdx) or (encKeyIdx > maxAesKeyIdx)):
|
||||
am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR);
|
||||
return
|
||||
if (encalgo == 2):
|
||||
if (encKeyIdx & 0x1):
|
||||
am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR);
|
||||
return
|
||||
keySize = 32
|
||||
else:
|
||||
keySize = 16
|
||||
if (authalgo != 0):
|
||||
if ((authKeyIdx < minHmacKeyIdx) or (authKeyIdx > maxHmacKeyIdx) or (authKeyIdx & 0x1)):
|
||||
am_print("Invalid authKey Idx ", authKeyIdx, level=AM_PRINT_LEVEL_ERROR);
|
||||
return
|
||||
|
||||
if (magicNum == AM_IMAGE_MAGIC_MAIN):
|
||||
hdr_length = AM_IMAGEHDR_SIZE_MAIN; #fixed header length
|
||||
elif ((magicNum == AM_IMAGE_MAGIC_CHILD) or (magicNum == AM_IMAGE_MAGIC_CUSTPATCH) or (magicNum == AM_IMAGE_MAGIC_NONSECURE) or (magicNum == AM_IMAGE_MAGIC_INFO0)):
|
||||
hdr_length = AM_IMAGEHDR_SIZE_AUX; #fixed header length
|
||||
else:
|
||||
am_print("magic number", hex(magicNum), " not supported", level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
am_print("Header Size = ", hex(hdr_length))
|
||||
|
||||
#generate mutable byte array for the header
|
||||
hdr_binarray = bytearray([0x00]*hdr_length);
|
||||
|
||||
orig_app_length = (len(app_binarray))
|
||||
am_print("original app_size ",hex(orig_app_length), "(",orig_app_length,")")
|
||||
|
||||
am_print("load_address ",hex(loadaddress), "(",loadaddress,")")
|
||||
if (loadaddress & 0x3):
|
||||
am_print("load address needs to be word aligned", level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
|
||||
if (magicNum == AM_IMAGE_MAGIC_INFO0):
|
||||
if (orig_app_length & 0x3):
|
||||
am_print("INFO0 blob length needs to be multiple of 4", level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
if ((loadaddress + orig_app_length) > INFO_SIZE_BYTES):
|
||||
am_print("INFO0 Offset and length exceed size", level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
|
||||
if (encVal == 1):
|
||||
block_size = AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES
|
||||
app_binarray = pad_to_block_size(app_binarray, block_size, 1)
|
||||
else:
|
||||
# Add Padding
|
||||
app_binarray = pad_to_block_size(app_binarray, 4, 0)
|
||||
|
||||
app_length = (len(app_binarray))
|
||||
am_print("app_size ",hex(app_length), "(",app_length,")")
|
||||
|
||||
# Create Image blobs
|
||||
|
||||
# w0
|
||||
blobLen = hdr_length + app_length
|
||||
w0 = (magicNum << 24) | ((encVal & 0x1) << 23) | blobLen
|
||||
|
||||
am_print("w0 =", hex(w0))
|
||||
fill_word(hdr_binarray, 0, w0)
|
||||
|
||||
# w2
|
||||
securityVal = ((authI << 1) | crcI) << 4 | (authB << 1) | crcB
|
||||
am_print("Security Value ", hex(securityVal))
|
||||
w2 = ((securityVal << 24) & 0xff000000) | ((authalgo) & 0xf) | ((authKeyIdx << 4) & 0xf0) | ((encalgo << 8) & 0xf00) | ((encKeyIdx << 12) & 0xf000)
|
||||
fill_word(hdr_binarray, 8, w2)
|
||||
am_print("w2 = ",hex(w2))
|
||||
|
||||
|
||||
if (magicNum == AM_IMAGE_MAGIC_INFO0):
|
||||
# Insert the INFO0 size and offset
|
||||
addrWord = ((orig_app_length>>2) << 16) | ((loadaddress>>2) & 0xFFFF)
|
||||
versionKeyWord = INFO_KEY
|
||||
else:
|
||||
# Insert the application binary load address.
|
||||
addrWord = loadaddress | (protection & 0x3)
|
||||
# Initialize versionKeyWord
|
||||
versionKeyWord = (version & 0x7FFF) | ((erasePrev & 0x1) << 15)
|
||||
|
||||
am_print("addrWord = ",hex(addrWord))
|
||||
fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_ADDR, addrWord)
|
||||
|
||||
am_print("versionKeyWord = ",hex(versionKeyWord))
|
||||
fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_VERKEY, versionKeyWord)
|
||||
|
||||
# Initialize child (Child Ptr/ Feature key)
|
||||
am_print("child0/feature = ",hex(child0))
|
||||
fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CHILDPTR, child0)
|
||||
am_print("child1 = ",hex(child1))
|
||||
fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CHILDPTR + 4, child1)
|
||||
|
||||
authKeyIdx = authKeyIdx - minHmacKeyIdx
|
||||
if (authB != 0): # Authentication needed
|
||||
am_print("Boot Authentication Enabled")
|
||||
# am_print("Key used for HMAC")
|
||||
# am_print([hex(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
|
||||
# Initialize the clear image HMAC
|
||||
sigClr = compute_hmac(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES:(authKeyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], (hdr_binarray[AM_IMAGEHDR_START_HMAC:hdr_length] + app_binarray))
|
||||
am_print("HMAC Clear")
|
||||
am_print([hex(n) for n in sigClr])
|
||||
# Fill up the HMAC
|
||||
for x in range(0, AM_HMAC_SIG_SIZE):
|
||||
hdr_binarray[AM_IMAGEHDR_OFFSET_SIGCLR + x] = sigClr[x]
|
||||
|
||||
# All the header fields part of the encryption are now final
|
||||
if (encVal == 1):
|
||||
am_print("Encryption Enabled")
|
||||
encKeyIdx = encKeyIdx - minAesKeyIdx
|
||||
ivValAes = os.urandom(AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES)
|
||||
am_print("Initialization Vector")
|
||||
am_print([hex(ivValAes[n]) for n in range (0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES)])
|
||||
keyAes = os.urandom(keySize)
|
||||
am_print("AES Key used for encryption")
|
||||
am_print([hex(keyAes[n]) for n in range (0, keySize)])
|
||||
# Encrypted Part
|
||||
am_print("Encrypting blob of size " , (hdr_length - AM_IMAGEHDR_START_ENCRYPT + app_length))
|
||||
enc_binarray = encrypt_app_aes((hdr_binarray[AM_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray), keyAes, ivValAes)
|
||||
# am_print("Key used for encrypting AES Key")
|
||||
# am_print([hex(keyTblAes[encKeyIdx*keySize + n]) for n in range (0, keySize)])
|
||||
# Encrypted Key
|
||||
enc_key = encrypt_app_aes(keyAes, keyTblAes[encKeyIdx*keySize:encKeyIdx*keySize + keySize], ivVal0)
|
||||
am_print("Encrypted Key")
|
||||
am_print([hex(enc_key[n]) for n in range (0, keySize)])
|
||||
# Fill up the IV
|
||||
for x in range(0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES):
|
||||
hdr_binarray[AM_IMAGEHDR_OFFSET_IV + x] = ivValAes[x]
|
||||
# Fill up the Encrypted Key
|
||||
for x in range(0, keySize):
|
||||
hdr_binarray[AM_IMAGEHDR_OFFSET_KEK + x] = enc_key[x]
|
||||
else:
|
||||
enc_binarray = hdr_binarray[AM_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray
|
||||
|
||||
|
||||
if (authI != 0): # Install Authentication needed
|
||||
am_print("Install Authentication Enabled")
|
||||
# am_print("Key used for HMAC")
|
||||
# am_print([hex(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
|
||||
# Initialize the top level HMAC
|
||||
sig = compute_hmac(keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES:(authKeyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], (hdr_binarray[AM_IMAGEHDR_START_HMAC_INST:AM_IMAGEHDR_START_ENCRYPT] + enc_binarray))
|
||||
am_print("Generated Signature")
|
||||
am_print([hex(n) for n in sig])
|
||||
# Fill up the HMAC
|
||||
for x in range(0, AM_HMAC_SIG_SIZE):
|
||||
hdr_binarray[AM_IMAGEHDR_OFFSET_SIG + x] = sig[x]
|
||||
# compute the CRC for the blob - this is done on a clear image
|
||||
crc = crc32(hdr_binarray[AM_IMAGEHDR_START_CRC:hdr_length] + app_binarray)
|
||||
am_print("crc = ",hex(crc));
|
||||
w1 = crc
|
||||
fill_word(hdr_binarray, AM_IMAGEHDR_OFFSET_CRC, w1)
|
||||
|
||||
# now output all three binary arrays in the proper order
|
||||
output = output + '_OTA_blob.bin'
|
||||
blob2wiredfile = output # save the output of bin2blob for use by blob2wired
|
||||
am_print("Writing to file ", output)
|
||||
with open(output, mode = 'wb') as out:
|
||||
out.write(hdr_binarray[0:AM_IMAGEHDR_START_ENCRYPT])
|
||||
out.write(enc_binarray)
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Generate the image blob as per command line parameters
|
||||
#
|
||||
#******************************************************************************
|
||||
def blob2wired_process(appFile, imagetype, loadaddress, authalgo, encalgo, authKeyIdx, encKeyIdx, optionsVal, maxSize, output):
|
||||
global uploadbinfile
|
||||
|
||||
app_binarray = bytearray()
|
||||
# Open the file, and read it into an array of integers.
|
||||
print('testing: ' + appFile )
|
||||
with open(appFile,'rb') as f_app:
|
||||
app_binarray.extend(f_app.read())
|
||||
f_app.close()
|
||||
|
||||
# Make sure it is page multiple
|
||||
if ((maxSize & (FLASH_PAGE_SIZE - 1)) != 0):
|
||||
am_print ("split needs to be multiple of flash page size", level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
|
||||
if (encalgo != 0):
|
||||
if ((encKeyIdx < minAesKeyIdx) or (encKeyIdx > maxAesKeyIdx)):
|
||||
am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR)
|
||||
return
|
||||
if (encalgo == 2):
|
||||
if (encKeyIdx & 0x1):
|
||||
am_print("Invalid encKey Idx ", encKeyIdx, level=AM_PRINT_LEVEL_ERROR);
|
||||
return
|
||||
keySize = 32
|
||||
else:
|
||||
keySize = 16
|
||||
if (authalgo != 0):
|
||||
if ((authKeyIdx < minHmacKeyIdx) or (authKeyIdx > maxHmacKeyIdx) or (authKeyIdx & 0x1)):
|
||||
am_print("Invalid authKey Idx ", authKeyIdx, level=AM_PRINT_LEVEL_ERROR);
|
||||
return
|
||||
|
||||
hdr_length = AM_WU_IMAGEHDR_SIZE; #fixed header length
|
||||
am_print("Header Size = ", hex(hdr_length))
|
||||
|
||||
orig_app_length = (len(app_binarray))
|
||||
|
||||
if (encalgo != 0):
|
||||
block_size = keySize
|
||||
app_binarray = pad_to_block_size(app_binarray, block_size, 1)
|
||||
else:
|
||||
# Add Padding
|
||||
app_binarray = pad_to_block_size(app_binarray, 4, 0)
|
||||
|
||||
app_length = (len(app_binarray))
|
||||
am_print("app_size ",hex(app_length), "(",app_length,")")
|
||||
|
||||
if (app_length + hdr_length > maxSize):
|
||||
am_print("Image size bigger than max - Creating Split image")
|
||||
|
||||
start = 0
|
||||
# now output all three binary arrays in the proper order
|
||||
output = output + '_Wired_OTA_blob.bin'
|
||||
uploadbinfile = output; # save the name of the output from blob2wired
|
||||
out = open(output, mode = 'wb')
|
||||
|
||||
while (start < app_length):
|
||||
#generate mutable byte array for the header
|
||||
hdr_binarray = bytearray([0x00]*hdr_length);
|
||||
|
||||
if (app_length - start > maxSize):
|
||||
end = start + maxSize
|
||||
else:
|
||||
end = app_length
|
||||
|
||||
if (imagetype == AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA):
|
||||
key = INFO_KEY
|
||||
# word offset
|
||||
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress>>2)
|
||||
else:
|
||||
key = FLASH_KEY
|
||||
# load address
|
||||
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress)
|
||||
# Create imageType & options
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_IMAGETYPE] = imagetype
|
||||
# Set the options only for the first block
|
||||
if (start == 0):
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_OPTIONS] = optionsVal
|
||||
else:
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_OPTIONS] = 0
|
||||
|
||||
# Create Info0 Update Blob for wired update
|
||||
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_KEY, key)
|
||||
# update size
|
||||
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_SIZE, end-start)
|
||||
|
||||
w0 = ((authalgo & 0xf) | ((authKeyIdx << 8) & 0xf00) | ((encalgo << 16) & 0xf0000) | ((encKeyIdx << 24) & 0x0f000000))
|
||||
|
||||
fill_word(hdr_binarray, 0, w0)
|
||||
|
||||
if (encalgo != 0):
|
||||
keyIdx = encKeyIdx - minAesKeyIdx
|
||||
ivValAes = os.urandom(AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES)
|
||||
am_print("Initialization Vector")
|
||||
am_print([hex(n) for n in ivValAes])
|
||||
keyAes = os.urandom(keySize)
|
||||
am_print("AES Key used for encryption")
|
||||
am_print([hex(keyAes[n]) for n in range (0, keySize)])
|
||||
# Encrypted Part - after security header
|
||||
enc_binarray = encrypt_app_aes((hdr_binarray[AM_WU_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray[start:end]), keyAes, ivValAes)
|
||||
# am_print("Key used for encrypting AES Key")
|
||||
# am_print([hex(keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, keySize)])
|
||||
# Encrypted Key
|
||||
enc_key = encrypt_app_aes(keyAes, keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES:(keyIdx*AM_SECBOOT_KEYIDX_BYTES + keySize)], ivVal0)
|
||||
am_print("Encrypted Key")
|
||||
am_print([hex(enc_key[n]) for n in range (0, keySize)])
|
||||
# Fill up the IV
|
||||
for x in range(0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES):
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_IV + x] = ivValAes[x]
|
||||
# Fill up the Encrypted Key
|
||||
for x in range(0, keySize):
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_KEK + x] = enc_key[x]
|
||||
else:
|
||||
enc_binarray = hdr_binarray[AM_WU_IMAGEHDR_START_ENCRYPT:hdr_length] + app_binarray[start:end]
|
||||
|
||||
|
||||
if (authalgo != 0): # Authentication needed
|
||||
keyIdx = authKeyIdx - minHmacKeyIdx
|
||||
# am_print("Key used for HMAC")
|
||||
# am_print([hex(keyTblHmac[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
|
||||
# Initialize the HMAC - Sign is computed on image following the signature
|
||||
sig = compute_hmac(keyTblHmac[keyIdx*AM_SECBOOT_KEYIDX_BYTES:(keyIdx*AM_SECBOOT_KEYIDX_BYTES+AM_HMAC_SIG_SIZE)], hdr_binarray[AM_WU_IMAGEHDR_START_HMAC:AM_WU_IMAGEHDR_START_ENCRYPT] + enc_binarray)
|
||||
am_print("HMAC")
|
||||
am_print([hex(n) for n in sig])
|
||||
# Fill up the HMAC
|
||||
for x in range(0, AM_HMAC_SIG_SIZE):
|
||||
hdr_binarray[AM_WU_IMAGEHDR_OFFSET_SIG + x] = sig[x]
|
||||
|
||||
am_print("Writing to file ", output)
|
||||
am_print("Image from ", str(hex(start)), " to ", str(hex(end)), " will be loaded at", str(hex(loadaddress)))
|
||||
out.write(hdr_binarray[0:AM_WU_IMAGEHDR_START_ENCRYPT])
|
||||
out.write(enc_binarray)
|
||||
|
||||
# Reset start for next chunk
|
||||
start = end
|
||||
loadaddress = loadaddress + maxSize
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Main function
|
||||
#
|
||||
#******************************************************************************
|
||||
def upload(args, verboseprint):
|
||||
|
||||
global loadTries
|
||||
global loadSuccess
|
||||
|
||||
# Open a serial port, and communicate with Device
|
||||
#
|
||||
# Max flashing time depends on the amount of SRAM available.
|
||||
# For very large images, the flashing happens page by page.
|
||||
# However if the image can fit in the free SRAM, it could take a long time
|
||||
# for the whole image to be flashed at the end.
|
||||
# The largest image which can be stored depends on the max SRAM.
|
||||
# Assuming worst case ~100 ms/page of flashing time, and allowing for the
|
||||
# image to be close to occupying full SRAM (256K) which is 128 pages.
|
||||
|
||||
connection_timeout = 5
|
||||
|
||||
print('Connecting over serial port {}...'.format(args.port), flush=True)
|
||||
|
||||
#Check to see if the com port is available
|
||||
try:
|
||||
with serial.Serial(args.port, args.baud, timeout=connection_timeout) as ser:
|
||||
pass
|
||||
except:
|
||||
|
||||
#Show a list of com ports and recommend one
|
||||
print("Detected Serial Ports:")
|
||||
devices = list_ports.comports()
|
||||
port = None
|
||||
for dev in devices:
|
||||
print(dev.description)
|
||||
# The SparkFun BlackBoard has CH340 in the description
|
||||
if 'CH340' in dev.description:
|
||||
print("The port you selected was not found. But we did detect a CH340 on " + dev.device + " so you might try again on that port.")
|
||||
break
|
||||
elif 'FTDI' in dev.description:
|
||||
print("The port you selected was not found. But we did detect an FTDI on " + dev.device + " so you might try again on that port.")
|
||||
break
|
||||
elif 'USB Serial Device' in dev.description:
|
||||
print("The port you selected was not found. But we did detect a USB Serial Device on " + dev.device + " so you might try again on that port.")
|
||||
break
|
||||
else:
|
||||
print("Com Port not found - Did you select the right one?")
|
||||
|
||||
exit()
|
||||
|
||||
#Begin talking over com port
|
||||
|
||||
#The auto-bootload sequence is good but not fullproof. The bootloader
|
||||
#fails to correctly catch the BOOT signal about 1 out of ten times.
|
||||
#Auto-retry this number of times before we give up.
|
||||
|
||||
while loadTries < 3:
|
||||
loadSuccess = False
|
||||
|
||||
with serial.Serial(args.port, args.baud, timeout=connection_timeout) as ser:
|
||||
#DTR is driven low when serial port open. DTR has now pulled RST low.
|
||||
|
||||
time.sleep(0.005) #3ms and 10ms work well. Not 50, and not 0.
|
||||
|
||||
#Setting RTS/DTR high causes the bootload pin to go high, then fall across 100ms
|
||||
ser.setDTR(0) #Set DTR high
|
||||
ser.setRTS(0) #Set RTS high - support the CH340E
|
||||
|
||||
#Give bootloader a chance to run and check bootload pin before communication begins. But must initiate com before bootloader timeout of 250ms.
|
||||
time.sleep(0.100) #100ms works well
|
||||
|
||||
ser.reset_input_buffer() # reset the input bufer to discard any UART traffic that the device may have generated
|
||||
|
||||
connect_device(ser, args, verboseprint)
|
||||
|
||||
if(loadSuccess == True):
|
||||
print("Tries =", loadTries)
|
||||
print('Upload complete!')
|
||||
exit()
|
||||
else:
|
||||
print("Fail")
|
||||
|
||||
loadTries = loadTries + 1
|
||||
|
||||
print("Tries =", loadTries)
|
||||
print("Upload failed")
|
||||
exit()
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Communicate with Device
|
||||
#
|
||||
# Given a serial port, connects to the target device using the
|
||||
# UART.
|
||||
#
|
||||
#******************************************************************************
|
||||
def connect_device(ser, args, verboseprint):
|
||||
|
||||
global loadSuccess
|
||||
|
||||
# Send Hello
|
||||
#generate mutable byte array for the header
|
||||
hello = bytearray([0x00]*4)
|
||||
fill_word(hello, 0, ((8 << 16) | AM_SECBOOT_WIRED_MSGTYPE_HELLO))
|
||||
verboseprint('Sending Hello.')
|
||||
response = send_command(hello, 88, ser, verboseprint)
|
||||
|
||||
#Check if response failed
|
||||
if response == False:
|
||||
verboseprint("Failed to respond")
|
||||
return
|
||||
|
||||
verboseprint("Received response for Hello")
|
||||
word = word_from_bytes(response, 4)
|
||||
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_STATUS):
|
||||
# Received Status
|
||||
print("Bootloader connected")
|
||||
|
||||
verboseprint("Received Status")
|
||||
verboseprint("length = ", hex((word >> 16)))
|
||||
verboseprint("version = ", hex(word_from_bytes(response, 8)))
|
||||
verboseprint("Max Storage = ", hex(word_from_bytes(response, 12)))
|
||||
verboseprint("Status = ", hex(word_from_bytes(response, 16)))
|
||||
verboseprint("State = ", hex(word_from_bytes(response, 20)))
|
||||
verboseprint("AMInfo = ")
|
||||
for x in range(24, 88, 4):
|
||||
verboseprint(hex(word_from_bytes(response, x)))
|
||||
|
||||
abort = args.abort
|
||||
if (abort != -1):
|
||||
# Send OTA Desc
|
||||
verboseprint('Sending Abort command.')
|
||||
abortMsg = bytearray([0x00]*8);
|
||||
fill_word(abortMsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_ABORT))
|
||||
fill_word(abortMsg, 4, abort)
|
||||
if send_ackd_command(abortMsg, ser, verboseprint) == False:
|
||||
verboseprint("Failed to ack command")
|
||||
return
|
||||
|
||||
|
||||
otadescaddr = args.otadesc
|
||||
if (otadescaddr != 0xFFFFFFFF):
|
||||
# Send OTA Desc
|
||||
verboseprint('Sending OTA Descriptor = ', hex(otadescaddr))
|
||||
otaDesc = bytearray([0x00]*8);
|
||||
fill_word(otaDesc, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_OTADESC))
|
||||
fill_word(otaDesc, 4, otadescaddr)
|
||||
if send_ackd_command(otaDesc, ser, verboseprint) == False:
|
||||
verboseprint("Failed to ack command")
|
||||
return
|
||||
|
||||
|
||||
imageType = args.imagetype
|
||||
if (uploadbinfile != ''):
|
||||
|
||||
# Read the binary file from the command line.
|
||||
with open(uploadbinfile, mode='rb') as binfile:
|
||||
application = binfile.read()
|
||||
# Gather the important binary metadata.
|
||||
totalLen = len(application)
|
||||
# Send Update command
|
||||
verboseprint('Sending Update Command.')
|
||||
|
||||
# It is assumed that maxSize is 256b multiple
|
||||
maxImageSize = args.split
|
||||
if ((maxImageSize & (FLASH_PAGE_SIZE - 1)) != 0):
|
||||
verboseprint ("split needs to be multiple of flash page size")
|
||||
return
|
||||
|
||||
# Each Block of image consists of AM_WU_IMAGEHDR_SIZE Bytes Image header and the Image blob
|
||||
maxUpdateSize = AM_WU_IMAGEHDR_SIZE + maxImageSize
|
||||
numUpdates = (totalLen + maxUpdateSize - 1) // maxUpdateSize # Integer division
|
||||
verboseprint("number of updates needed = ", numUpdates)
|
||||
|
||||
end = totalLen
|
||||
for numUpdates in range(numUpdates, 0 , -1):
|
||||
start = (numUpdates-1)*maxUpdateSize
|
||||
crc = crc32(application[start:end])
|
||||
applen = end - start
|
||||
verboseprint("Sending block of size ", str(hex(applen)), " from ", str(hex(start)), " to ", str(hex(end)))
|
||||
end = end - applen
|
||||
|
||||
update = bytearray([0x00]*16);
|
||||
fill_word(update, 0, ((20 << 16) | AM_SECBOOT_WIRED_MSGTYPE_UPDATE))
|
||||
fill_word(update, 4, applen)
|
||||
fill_word(update, 8, crc)
|
||||
# Size = 0 => We're not piggybacking any data to IMAGE command
|
||||
fill_word(update, 12, 0)
|
||||
|
||||
if send_ackd_command(update, ser, verboseprint) == False:
|
||||
verboseprint("Failed to ack command")
|
||||
return
|
||||
|
||||
# Loop over the bytes in the image, and send them to the target.
|
||||
resp = 0
|
||||
# Max chunk size is AM_MAX_UART_MSG_SIZE adjusted for the header for Data message
|
||||
maxChunkSize = AM_MAX_UART_MSG_SIZE - 12
|
||||
for x in range(0, applen, maxChunkSize):
|
||||
# Split the application into chunks of maxChunkSize bytes.
|
||||
# This is the max chunk size supported by the UART bootloader
|
||||
if ((x + maxChunkSize) > applen):
|
||||
chunk = application[start+x:start+applen]
|
||||
# print(str(hex(start+x)), " to ", str(hex(applen)))
|
||||
else:
|
||||
chunk = application[start+x:start+x+maxChunkSize]
|
||||
# print(str(hex(start+x)), " to ", str(hex(start + x + maxChunkSize)))
|
||||
|
||||
chunklen = len(chunk)
|
||||
|
||||
# Build a data packet with a "data command" a "length" and the actual
|
||||
# payload bytes, and send it to the target.
|
||||
dataMsg = bytearray([0x00]*8);
|
||||
fill_word(dataMsg, 0, (((chunklen + 12) << 16) | AM_SECBOOT_WIRED_MSGTYPE_DATA))
|
||||
# seqNo
|
||||
fill_word(dataMsg, 4, x)
|
||||
|
||||
verboseprint("Sending Data Packet of length ", chunklen)
|
||||
if send_ackd_command(dataMsg + chunk, ser, verboseprint) == False:
|
||||
verboseprint("Failed to ack command")
|
||||
return
|
||||
|
||||
if (args.raw != ''):
|
||||
|
||||
# Read the binary file from the command line.
|
||||
with open(args.raw, mode='rb') as rawfile:
|
||||
blob = rawfile.read()
|
||||
# Send Raw command
|
||||
verboseprint('Sending Raw Command.')
|
||||
ser.write(blob)
|
||||
|
||||
if (args.reset != 0):
|
||||
# Send reset
|
||||
verboseprint('Sending Reset Command.')
|
||||
resetmsg = bytearray([0x00]*8);
|
||||
fill_word(resetmsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_RESET))
|
||||
# options
|
||||
fill_word(resetmsg, 4, args.reset)
|
||||
if send_ackd_command(resetmsg, ser, verboseprint) == False:
|
||||
verboseprint("Failed to ack command")
|
||||
return
|
||||
|
||||
|
||||
#Success! We're all done
|
||||
loadSuccess = True
|
||||
else:
|
||||
# Received Wrong message
|
||||
verboseprint("Received Unknown Message")
|
||||
word = word_from_bytes(response, 4)
|
||||
verboseprint("msgType = ", hex(word & 0xFFFF))
|
||||
verboseprint("Length = ", hex(word >> 16))
|
||||
verboseprint([hex(n) for n in response])
|
||||
#print("!!!Wired Upgrade Unsuccessful!!!....Terminating the script")
|
||||
|
||||
#exit()
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Send ACK'd command
|
||||
#
|
||||
# Sends a command, and waits for an ACK.
|
||||
#
|
||||
#******************************************************************************
|
||||
def send_ackd_command(command, ser, verboseprint):
|
||||
|
||||
response = send_command(command, 20, ser, verboseprint)
|
||||
|
||||
#Check if response failed
|
||||
if response == False:
|
||||
verboseprint("Response not valid")
|
||||
return False #Return error
|
||||
|
||||
word = word_from_bytes(response, 4)
|
||||
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_ACK):
|
||||
# Received ACK
|
||||
if (word_from_bytes(response, 12) != AM_SECBOOT_WIRED_ACK_STATUS_SUCCESS):
|
||||
verboseprint("Received NACK")
|
||||
verboseprint("msgType = ", hex(word_from_bytes(response, 8)))
|
||||
verboseprint("error = ", hex(word_from_bytes(response, 12)))
|
||||
verboseprint("seqNo = ", hex(word_from_bytes(response, 16)))
|
||||
#print("!!!Wired Upgrade Unsuccessful!!!....Terminating the script")
|
||||
verboseprint("Upload failed: No ack to command")
|
||||
|
||||
return False #Return error
|
||||
|
||||
return response
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Send command
|
||||
#
|
||||
# Sends a command, and waits for the response.
|
||||
#
|
||||
#******************************************************************************
|
||||
def send_command(params, response_len, ser, verboseprint):
|
||||
|
||||
# Compute crc
|
||||
crc = crc32(params)
|
||||
# print([hex(n) for n in int_to_bytes(crc)])
|
||||
# print([hex(n) for n in params])
|
||||
# send crc first
|
||||
ser.write(int_to_bytes(crc))
|
||||
|
||||
# Next, send the parameters.
|
||||
ser.write(params)
|
||||
|
||||
response = ''
|
||||
response = ser.read(response_len)
|
||||
|
||||
# Make sure we got the number of bytes we asked for.
|
||||
if len(response) != response_len:
|
||||
verboseprint('No response for command 0x{:08X}'.format(word_from_bytes(params, 0) & 0xFFFF))
|
||||
n = len(response)
|
||||
if (n != 0):
|
||||
verboseprint("received bytes ", len(response))
|
||||
verboseprint([hex(n) for n in response])
|
||||
return False
|
||||
|
||||
return response
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Send a command that uses an array of bytes as its parameters.
|
||||
#
|
||||
#******************************************************************************
|
||||
def send_bytewise_command(command, params, response_len, ser):
|
||||
# Send the command first.
|
||||
ser.write(int_to_bytes(command))
|
||||
|
||||
# Next, send the parameters.
|
||||
ser.write(params)
|
||||
|
||||
response = ''
|
||||
response = ser.read(response_len)
|
||||
|
||||
# Make sure we got the number of bytes we asked for.
|
||||
if len(response) != response_len:
|
||||
print("Upload failed: No reponse to command")
|
||||
verboseprint('No response for command 0x{:08X}'.format(command))
|
||||
exit()
|
||||
|
||||
return response
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Errors
|
||||
#
|
||||
#******************************************************************************
|
||||
class BootError(Exception):
|
||||
pass
|
||||
|
||||
class NoAckError(BootError):
|
||||
pass
|
||||
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description =
|
||||
'Combination script to upload application binaries to Artemis module. Includes:\n\t\'- bin2blob: create OTA blob from binary image\'\n\t\'- blob2wired: create wired update image from OTA blob\'\n\t\'- upload: send wired update image to Apollo3 Artemis module via serial port\'\n\nThere are many command-line arguments. They have been labeled by which steps they apply to\n')
|
||||
|
||||
parser.add_argument('-a', dest = 'abort', default=-1, type=int, choices = [0,1,-1],
|
||||
help = 'upload: Should it send abort command? (0 = abort, 1 = abort and quit, -1 = no abort) (default is -1)')
|
||||
|
||||
parser.add_argument('--authalgo', dest = 'authalgo', type=auto_int, default=0, choices=range(0, AM_SECBOOT_AUTH_ALGO_MAX+1),
|
||||
help = 'bin2blob, blob2wired: ' + str(helpAuthAlgo))
|
||||
|
||||
parser.add_argument('--authI', dest = 'authI', type=auto_int, default=0, choices=[0,1],
|
||||
help = 'bin2blob: Install Authentication check enabled (Default = N)?')
|
||||
|
||||
parser.add_argument('--authB', dest = 'authB', type=auto_int, default=0, choices=[0,1],
|
||||
help = 'bin2blob: Boot Authentication check enabled (Default = N)?')
|
||||
|
||||
parser.add_argument('--authkey', dest = 'authkey', type=auto_int, default=(minHmacKeyIdx), choices = range(minHmacKeyIdx, maxHmacKeyIdx + 1),
|
||||
help = 'bin2blob, blob2wired: Authentication Key Idx? (' + str(minHmacKeyIdx) + ' to ' + str(maxHmacKeyIdx) + ')')
|
||||
|
||||
parser.add_argument('-b', dest='baud', default=115200, type=int,
|
||||
help = 'upload: Baud Rate (default is 115200)')
|
||||
|
||||
parser.add_argument('--bin', dest='appFile', type=argparse.FileType('rb'),
|
||||
help='bin2blob: binary file (blah.bin)')
|
||||
|
||||
parser.add_argument('-clean', dest='clean', default=0, type=int,
|
||||
help = 'All: whether or not to remove intermediate files')
|
||||
|
||||
parser.add_argument('--child0', dest = 'child0', type=auto_int, default=hex(0xFFFFFFFF),
|
||||
help = 'bin2blob: child (blobPtr#0 for Main / feature key for AM3P)')
|
||||
|
||||
parser.add_argument('--child1', dest = 'child1', type=auto_int, default=hex(0xFFFFFFFF),
|
||||
help = 'bin2blob: child (blobPtr#1 for Main)')
|
||||
|
||||
parser.add_argument('--crcI', dest = 'crcI', type=auto_int, default=1, choices=[0,1],
|
||||
help = 'bin2blob: Install CRC check enabled (Default = Y)?')
|
||||
|
||||
parser.add_argument('--crcB', dest = 'crcB', type=auto_int, default=0, choices=[0,1],
|
||||
help = 'bin2blob: Boot CRC check enabled (Default = N)?')
|
||||
|
||||
parser.add_argument('--encalgo', dest = 'encalgo', type=auto_int, default=0, choices = range(0, AM_SECBOOT_ENC_ALGO_MAX+1),
|
||||
help = 'bin2blob, blob2wired: ' + str(helpEncAlgo))
|
||||
|
||||
parser.add_argument('--erasePrev', dest = 'erasePrev', type=auto_int, default=0, choices=[0,1],
|
||||
help = 'bin2blob: erasePrev (Valid only for main)')
|
||||
|
||||
# parser.add_argument('-f', dest='binfile', default='',
|
||||
# help = 'upload: Binary file to program into the target device')
|
||||
|
||||
parser.add_argument('-i', dest = 'imagetype', default=AM_SECBOOT_WIRED_IMAGETYPE_INVALID, type=auto_int,
|
||||
choices = [
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_SBL),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_AM3P),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_PATCH),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_MAIN),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_CHILD),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_INFO0),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA),
|
||||
(AM_SECBOOT_WIRED_IMAGETYPE_INVALID)
|
||||
],
|
||||
help = 'blob2wired, upload: ImageType ('
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_SBL) + ': SBL, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_AM3P) + ': AM3P, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_PATCH) + ': Patch, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_MAIN) + ': Main, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_CHILD) + ': Child, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_CUSTPATCH) + ': CustOTA, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_NONSECURE) + ': NonSecure, '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0) + ': Info0 '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INFO0_NOOTA) + ': Info0_NOOTA) '
|
||||
+ str(AM_SECBOOT_WIRED_IMAGETYPE_INVALID) + ': Invalid) '
|
||||
'- default[Invalid]')
|
||||
|
||||
parser.add_argument('--kek', dest = 'kek', type=auto_int, default=(minAesKeyIdx), choices = range(minAesKeyIdx, maxAesKeyIdx+1),
|
||||
help = 'KEK index? (' + str(minAesKeyIdx) + ' to ' + str(maxAesKeyIdx) + ')')
|
||||
|
||||
parser.add_argument('--load-address-wired', dest='loadaddress_blob', type=auto_int, default=hex(0x60000),
|
||||
help='blob2wired: Load address of the binary - Where in flash the blob will be stored (could be different than install address of binary within).')
|
||||
|
||||
parser.add_argument('--load-address-blob', dest='loadaddress_image', type=auto_int, default=hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN),
|
||||
help='bin2blob: Load address of the binary.')
|
||||
|
||||
parser.add_argument('--loglevel', dest='loglevel', type=auto_int, default=AM_PRINT_LEVEL_INFO,
|
||||
choices = range(AM_PRINT_LEVEL_MIN, AM_PRINT_LEVEL_MAX+1),
|
||||
help='bin2blob, blob2wired: ' + str(helpPrintLevel))
|
||||
|
||||
parser.add_argument('--magic-num', dest='magic_num', default=hex(AM_IMAGE_MAGIC_NONSECURE),
|
||||
type=lambda x: x.lower(),
|
||||
# type = str.lower,
|
||||
choices = [
|
||||
hex(AM_IMAGE_MAGIC_MAIN),
|
||||
hex(AM_IMAGE_MAGIC_CHILD),
|
||||
hex(AM_IMAGE_MAGIC_CUSTPATCH),
|
||||
hex(AM_IMAGE_MAGIC_NONSECURE),
|
||||
hex(AM_IMAGE_MAGIC_INFO0)
|
||||
],
|
||||
help = 'bin2blob: Magic Num ('
|
||||
+ str(hex(AM_IMAGE_MAGIC_MAIN)) + ': Main, '
|
||||
+ str(hex(AM_IMAGE_MAGIC_CHILD)) + ': Child, '
|
||||
+ str(hex(AM_IMAGE_MAGIC_CUSTPATCH)) + ': CustOTA, '
|
||||
+ str(hex(AM_IMAGE_MAGIC_NONSECURE)) + ': NonSecure, '
|
||||
+ str(hex(AM_IMAGE_MAGIC_INFO0)) + ': Info0) '
|
||||
'- default[Main]'
|
||||
)
|
||||
|
||||
parser.add_argument('-o', dest = 'output', default='wuimage',
|
||||
help = 'all: Output filename (without the extension) [also used for intermediate filenames]')
|
||||
|
||||
parser.add_argument('-ota', dest = 'otadesc', type=auto_int, default=0xFE000,
|
||||
help = 'upload: OTA Descriptor Page address (hex) - (Default is 0xFE000 - at the end of main flash) - enter 0xFFFFFFFF to instruct SBL to skip OTA')
|
||||
|
||||
parser.add_argument('--options', dest = 'options', type=auto_int, default=0x1,
|
||||
help = 'blob2wired: Options (16b hex value) - bit0 instructs to perform OTA of the image after wired download (set to 0 if only downloading & skipping OTA flow)')
|
||||
|
||||
parser.add_argument('-p', dest = 'protection', type=auto_int, default=0, choices = [0x0, 0x1, 0x2, 0x3],
|
||||
help = 'bin2blob: protection info 2 bit C W')
|
||||
|
||||
parser.add_argument('-port', dest = 'port', help = 'upload: Serial COMx Port')
|
||||
|
||||
parser.add_argument('-r', dest = 'reset', default=1, type=auto_int, choices = [0,1,2],
|
||||
help = 'upload: Should it send reset command after image download? (0 = no reset, 1 = POI, 2 = POR) (default is 1)')
|
||||
|
||||
parser.add_argument('--raw', dest='raw', default='',
|
||||
help = 'upload: Binary file for raw message')
|
||||
|
||||
parser.add_argument('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE),
|
||||
help='blob2wired, upload: Specify the max block size if the image will be downloaded in pieces')
|
||||
|
||||
parser.add_argument('--version', dest = 'version', type=auto_int, default=0,
|
||||
help = 'bin2blob: version (15 bit)')
|
||||
|
||||
parser.add_argument("-v", "--verbose", default=0, help="All: Enable verbose output",
|
||||
action="store_true")
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
args.magic_num = int(args.magic_num, 16)
|
||||
|
||||
|
||||
return args
|
||||
|
||||
|
||||
|
||||
#******************************************************************************
|
||||
#
|
||||
# Main function.
|
||||
#
|
||||
#******************************************************************************
|
||||
|
||||
# example calling:
|
||||
# python artemis_bin_to_board.py --bin application.bin --load-address-blob 0x20000 --magic-num 0xCB -o application --version 0x0 --load-address-wired 0xC000 -i 6 --options 0x1 -b 921600 -port COM4 -r 1 -v
|
||||
|
||||
def main():
|
||||
# Read the arguments.
|
||||
args = parse_arguments()
|
||||
am_set_print_level(args.loglevel)
|
||||
|
||||
global blob2wiredfile
|
||||
|
||||
bin2blob_process(args.loadaddress_blob, args.appFile, args.magic_num, args.crcI, args.crcB, args.authI, args.authB, args.protection, args.authkey, args.output, args.kek, args.version, args.erasePrev, args.child0, args.child1, args.authalgo, args.encalgo)
|
||||
blob2wired_process( blob2wiredfile, args.imagetype, args.loadaddress_image, args.authalgo, args.encalgo, args.authkey, args.kek, args.options, args.split, args.output)
|
||||
|
||||
# todo: link the bin2blob step with the blob2wired step by input/output files
|
||||
|
||||
|
||||
#Create print function for verbose output if caller deems it: https://stackoverflow.com/questions/5980042/how-to-implement-the-verbose-or-v-option-into-a-script
|
||||
if args.verbose:
|
||||
def verboseprint(*args):
|
||||
# Print each argument separately so caller doesn't need to
|
||||
# stuff everything to be printed into a single string
|
||||
for arg in args:
|
||||
print(arg, end=''),
|
||||
print()
|
||||
else:
|
||||
verboseprint = lambda *a: None # do-nothing function
|
||||
|
||||
upload(args, verboseprint)
|
||||
|
||||
if(args.clean == 1):
|
||||
print('Cleaning up intermediate files') # todo: why isnt this showing w/ -clean option?
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
BIN
Binary file not shown.
Vendored
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python3
|
||||
from am_defines import *
|
||||
|
||||
minAesKeyIdx = 8
|
||||
maxAesKeyIdx = 15
|
||||
minHmacKeyIdx = 8
|
||||
maxHmacKeyIdx = 15
|
||||
|
||||
###### Following are just dummy keys - Should be substituted with real keys #######
|
||||
keyTblAes = [
|
||||
# Info0 Keys - Starting at index 8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
|
||||
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
|
||||
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
|
||||
0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5,
|
||||
0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
|
||||
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
|
||||
]
|
||||
|
||||
keyTblHmac = [
|
||||
# Info0 Keys - Starting at index 8
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
|
||||
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
|
||||
]
|
||||
|
||||
custKey = [
|
||||
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
|
||||
]
|
||||
|
||||
# These are dummy values. Contact AMBIQ to get the real Recovery Key
|
||||
recoveryKey = [
|
||||
0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE, 0xEF, 0xBE, 0xAD, 0xDE,
|
||||
]
|
||||
|
||||
###################################################################################
|
||||
|
||||
wrapKey = custKey
|
||||
minWrapMode = AM_SECBOOT_KEYWRAP_NONE
|
||||
|
||||
INFO_KEY = 0xd894e09e
|
||||
FLASH_KEY = 0x12344321
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
###### Requirements ######`
|
||||
pyinstaller == 3.6
|
||||
pycryptodome
|
||||
pyserial
|
||||
Reference in New Issue
Block a user