initial commit

This commit is contained in:
2022-10-23 23:45:43 -07:00
commit e190fa5193
6450 changed files with 8626944 additions and 0 deletions
@@ -0,0 +1,56 @@
#******************************************************************************
#
# Makefile - Rules for building the libraries, examples and docs.
#
# Copyright (c) 2020, Ambiq Micro
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# Third party software included in this distribution is subject to the
# additional license terms as defined in the /docs/licenses directory.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This is part of revision 2.4.2 of the AmbiqSuite Development Package.
#
#******************************************************************************
SUBDIRS=${wildcard */}
all:
@for i in ${SUBDIRS}; \
do \
if [ -f $${i}/Makefile ]; then \
$(MAKE) -C $${i} || exit $$?; fi; \
done
clean:
@for i in ${SUBDIRS}; \
do \
if [ -f $${i}/Makefile ]; then \
$(MAKE) -C $${i} clean; fi; \
done
@@ -0,0 +1,38 @@
#*******************************************************************************
#
# Simple Makefile to prepare binaries for AMOTA.
#
#*******************************************************************************
FLAG_ADDR_APOLLO2 = 0x6000
LOAD_ADDR_APOLLO2 = 0x8000
BOOTBIN_APOLLO2 =../../../boards/apollo2_evb/examples/multi_boot/iar/bin/multi_boot.bin
# Apollo2-BLUE EVB
UPDATEBIN_APOLLO2_BLUE =../../../boards/apollo2_blue_evb/examples/ble_freertos_amota_blinky/iar/bin/ble_freertos_amota_blinky.bin
APPBIN_APOLLO2_BLUE =../../../boards/apollo2_blue_evb/examples/ble_freertos_amota/iar/bin/ble_freertos_amota.bin
APPBIN_APOLLO2_BLUE_ETHERMIND =../../../boards/apollo2_blue_evb/examples/mindtree_amota/iar/bin/mindtree_amota.bin
all: $(BOOTBIN_APOLLO2) \
$(APPBIN_APOLLO2_BLUE) $(UPDATEBIN_APOLLO2_BLUE) \
python3 bootloader_binary_combiner.py --bootbin "${BOOTBIN_APOLLO2}" --appbin "${APPBIN_APOLLO2_BLUE}" --flag-addr ${FLAG_ADDR_APOLLO2} --load-address ${LOAD_ADDR_APOLLO2} -o starter_binary_apollo2_blue
python3 ota_binary_converter.py --appbin "${UPDATEBIN_APOLLO2_BLUE}" --load-address ${LOAD_ADDR_APOLLO2} -o update_binary_apollo2_blue
$(BOOTBIN_APOLLO2):
$(MAKE) -C ../../../boards/apollo2_evb/examples/multi_boot/iar/ $(MAKECMDGOALS)
$(APPBIN_APOLLO2_BLUE):
$(MAKE) -C ../../../boards/apollo2_blue_evb/examples/ble_freertos_amota/iar/ $(MAKECMDGOALS)
$(UPDATEBIN_APOLLO2_BLUE):
$(MAKE) -C ../../../boards/apollo2_blue_evb/examples/ble_freertos_amota_blinky/iar/ $(MAKECMDGOALS)
clean:
rm -rf *.bin
$(MAKE) -C ../../../boards/apollo2_evb/examples/multi_boot/iar/ $(MAKECMDGOALS)
$(MAKE) -C ../../../boards/apollo2_blue_evb/examples/ble_freertos_amota/iar/ $(MAKECMDGOALS)
$(MAKE) -C ../../../boards/apollo2_blue_evb/examples/ble_freertos_amota_blinky/iar/ $(MAKECMDGOALS)
@@ -0,0 +1,265 @@
#!/usr/bin/env python3
import argparse
#******************************************************************************
#
# CRC function that matches the CRC used by the Apollo bootloader.
#
#******************************************************************************
poly32 = 0x1EDC6F41
def crc32(L):
rem = 0
for b in L:
rem = rem ^ (b << 24)
for i in range(8):
if rem & 0x80000000:
rem = ((rem << 1) ^ poly32)
else:
rem = (rem << 1)
rem = rem & 0xFFFFFFFF
return rem
#******************************************************************************
#
# Read in the binary files and output the merged binary
#
#******************************************************************************
def process(boot_loader_filename, app_filename, output, loadaddr, overridegpio,
overridepolarity, flagtype, flagaddr):
# Open the file, and read it into an array of integers.
with open(boot_loader_filename, mode = 'rb') as f_bl:
boot_loader_binarray = f_bl.read()
f_bl.close()
# Open the file, and read it into an array of integers.
with open(app_filename, mode = 'rb') as f_app:
app_binarray = f_app.read()
f_app.close()
boot_length = len(boot_loader_binarray)
flag_address = int(flagaddr, 16)
print("boot size ",boot_length)
# Make sure bootloader does not overlap with flag page
if boot_length >= flag_address:
print("ERROR boot loader image is too big");
return
app_length = len(app_binarray)
load_address = int(loadaddr, 16)
pad_length = load_address - len(boot_loader_binarray)
print("pad_length ",pad_length);
# generate mutable byte array for the boot loader
pad_binarray = bytearray([0]*pad_length);
flag_type = int(flagtype, 16)
# check flash flag storage type
if flag_type == 0:
# flag stored before application (usually 0x3c00)
# this is where we will write the flash flag page info
flag_page_location = flag_address - len(boot_loader_binarray)
# Insert the application binary load address.
print("load_address ",hex(load_address), "(",load_address,")")
pad_binarray[flag_page_location + 0] = (load_address >> 0) & 0x000000ff;
pad_binarray[flag_page_location + 1] = (load_address >> 8) & 0x000000ff;
pad_binarray[flag_page_location + 2] = (load_address >> 16) & 0x000000ff;
pad_binarray[flag_page_location + 3] = (load_address >> 24) & 0x000000ff;
# put the application binary size into the padding array @ 0x3c04
app_length = len(app_binarray)
print("app_size ",hex(app_length), "(",app_length,")")
pad_binarray[flag_page_location + 4] = (app_length >> 0) & 0x000000ff
pad_binarray[flag_page_location + 5] = (app_length >> 8) & 0x000000ff
pad_binarray[flag_page_location + 6] = (app_length >> 16) & 0x000000ff
pad_binarray[flag_page_location + 7] = (app_length >> 24) & 0x000000ff
# compute the CRC for the application and write it to 0x3c08
app_crc = crc32(app_binarray)
print("crc = ",hex(app_crc));
pad_binarray[flag_page_location + 8] = (app_crc >> 0) & 0x000000ff
pad_binarray[flag_page_location + 9] = (app_crc >> 8) & 0x000000ff
pad_binarray[flag_page_location + 10] = (app_crc >> 16) & 0x000000ff
pad_binarray[flag_page_location + 11] = (app_crc >> 24) & 0x000000ff
# override gpio. default 0
override_gpio = int(overridegpio, 16)
pad_binarray[flag_page_location + 12] = (override_gpio >> 0) & 0x000000ff
pad_binarray[flag_page_location + 13] = (override_gpio >> 8) & 0x000000ff
pad_binarray[flag_page_location + 14] = (override_gpio >> 16) & 0x000000ff
pad_binarray[flag_page_location + 15] = (override_gpio >> 24) & 0x000000ff
# override polarity. default 0
override_polarity = int(overridepolarity)
pad_binarray[flag_page_location + 16] = (override_polarity >> 0) & 0x000000ff
pad_binarray[flag_page_location + 17] = (override_polarity >> 8) & 0x000000ff
pad_binarray[flag_page_location + 18] = (override_polarity >> 16) & 0x000000ff
pad_binarray[flag_page_location + 19] = (override_polarity >> 24) & 0x000000ff
# copy the reset vector stack pointer (SP) from the application binary to pad_binary
pad_binarray[flag_page_location + 20] = app_binarray[0];
pad_binarray[flag_page_location + 21] = app_binarray[1];
pad_binarray[flag_page_location + 22] = app_binarray[2];
pad_binarray[flag_page_location + 23] = app_binarray[3];
# copy the reset vector program counter (PC) from the application binary to pad_binary
pad_binarray[flag_page_location + 24] = app_binarray[4];
pad_binarray[flag_page_location + 25] = app_binarray[5];
pad_binarray[flag_page_location + 26] = app_binarray[6];
pad_binarray[flag_page_location + 27] = app_binarray[7];
# bEncrypted
pad_binarray[flag_page_location + 28] = 0x00
pad_binarray[flag_page_location + 29] = 0x00
pad_binarray[flag_page_location + 30] = 0x00
pad_binarray[flag_page_location + 31] = 0x00
# CRC
crc_flag = crc32(pad_binarray[flag_page_location:(flag_page_location + 32)])
print("info crc = ",hex(crc_flag));
pad_binarray[flag_page_location + 32] = (crc_flag >> 0) & 0x000000ff
pad_binarray[flag_page_location + 33] = (crc_flag >> 8) & 0x000000ff
pad_binarray[flag_page_location + 34] = (crc_flag >> 16) & 0x000000ff
pad_binarray[flag_page_location + 35] = (crc_flag >> 24) & 0x000000ff
# now output all three binary arrays in the proper order
with open(output + '.bin', mode = 'wb') as out:
out.write(boot_loader_binarray)
out.write(pad_binarray)
out.write(app_binarray)
else:
# flag stored after application (usually last page of the internal flash, e.g. 0x7F800 for 512KB flash)
page_length = 56; #fixed page length
#generate mutable byte array for the boot loader
page_binarray = bytearray([0]*page_length);
# Insert the application binary load address.
print("load_address ",hex(load_address), "(",load_address,")")
page_binarray[0] = (load_address >> 0) & 0x000000ff;
page_binarray[1] = (load_address >> 8) & 0x000000ff;
page_binarray[2] = (load_address >> 16) & 0x000000ff;
page_binarray[3] = (load_address >> 24) & 0x000000ff;
# put the application binary size into the padding array @ 0x3c04
app_length = len(app_binarray)
print("app_size ",hex(app_length), "(",app_length,")")
page_binarray[4] = (app_length >> 0) & 0x000000ff
page_binarray[5] = (app_length >> 8) & 0x000000ff
page_binarray[6] = (app_length >> 16) & 0x000000ff
page_binarray[7] = (app_length >> 24) & 0x000000ff
# compute the CRC for the application and write it to 0x3c08
app_crc = crc32(app_binarray)
print("crc = ",hex(app_crc));
page_binarray[8] = (app_crc >> 0) & 0x000000ff
page_binarray[9] = (app_crc >> 8) & 0x000000ff
page_binarray[10] = (app_crc >> 16) & 0x000000ff
page_binarray[11] = (app_crc >> 24) & 0x000000ff
# override gpio. default 0
override_gpio = int(overridegpio, 16)
page_binarray[12] = (override_gpio >> 0) & 0x000000ff
page_binarray[13] = (override_gpio >> 8) & 0x000000ff
page_binarray[14] = (override_gpio >> 16) & 0x000000ff
page_binarray[15] = (override_gpio >> 24) & 0x000000ff
# override polarity. default 0
override_polarity = int(overridepolarity)
page_binarray[16] = (override_polarity >> 0) & 0x000000ff
page_binarray[17] = (override_polarity >> 8) & 0x000000ff
page_binarray[18] = (override_polarity >> 16) & 0x000000ff
page_binarray[19] = (override_polarity >> 24) & 0x000000ff
# copy the reset vector stack pointer (SP) from the application binary to pad_binary
page_binarray[20] = app_binarray[0];
page_binarray[21] = app_binarray[1];
page_binarray[22] = app_binarray[2];
page_binarray[23] = app_binarray[3];
# copy the reset vector program counter (PC) from the application binary to pad_binary
page_binarray[24] = app_binarray[4];
page_binarray[25] = app_binarray[5];
page_binarray[26] = app_binarray[6];
page_binarray[27] = app_binarray[7];
# bEncrypted
page_binarray[28] = 0x00
page_binarray[29] = 0x00
page_binarray[30] = 0x00
page_binarray[31] = 0x00
# CRC
crc_flag = crc32(pad_binarray[flag_page_location:(flag_page_location + 32)])
page_binarray[32] = (crc_flag >> 0) & 0x000000ff
page_binarray[33] = (crc_flag >> 8) & 0x000000ff
page_binarray[34] = (crc_flag >> 16) & 0x000000ff
page_binarray[35] = (crc_flag >> 24) & 0x000000ff
#generate a mutable fill array between application end and flag page start
fill_binarray = bytearray([0xff]*(flag_address - app_length - load_address));
# now output all three binary arrays in the proper order
with open(output + '.bin', mode = 'wb') as out:
out.write(boot_loader_binarray)
out.write(pad_binarray)
out.write(app_binarray)
out.write(fill_binarray)
out.write(page_binarray)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Combine Bootloader & main image in to a single download, with flag page.')
parser.add_argument('--bootbin', dest='bootbin', default='../../../boards/apollo2_evb/examples/multi_boot/keil/bin/multi_boot.bin',
help='Bootloader binary file (multi_boot.bin)')
parser.add_argument('--appbin', dest='appbin', default='../../../boards/apollo2_evb_em9304/examples/freertos_amota/keil/bin/freertos_amota.bin',
help='Application binary file (app.bin)')
parser.add_argument('--load-address', dest='loadaddress', default='0x4000',
help='Load address of the application. Default = 0x4000 (for Apollo), set to 0x6000 for Apollo2')
parser.add_argument('--override-gpio', dest='overridegpio', default='0xffffffff',
help = 'Override GPIO number in hex. (Can be used to force a new image load)\n(0xffffffff to disable)')
parser.add_argument('--override-polarity', dest='overridepolarity', default=0, type=int,
help = 'Polarity for the override pin.')
#rma: add arg flag page address
parser.add_argument('--flag-type', dest='flagtype', default='0x0',
help = 'User specified flash flag page placement type. (0 = before application; 1 = after application)')
#rma: add arg flag page address
parser.add_argument('--flag-address', dest='flagaddress', default='0x3c00',
help = 'User specified flash flag page address, 0x3c00 as default (input 0x7f800 for last page of a 512KB flash Apollo device).')
parser.add_argument('-o', dest = 'output', default = 'binary_array',
help = 'Output filename (without the extension)')
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
process(args.bootbin, args.appbin, args.output, args.loadaddress,
args.overridegpio, args.overridepolarity,
args.flagtype, args.flagaddress)
if __name__ == '__main__':
main()
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
import argparse
#******************************************************************************
#
# CRC function that matches the CRC used by the Apollo bootloader.
#
#******************************************************************************
poly32 = 0x1EDC6F41
def crc32(L):
rem = 0
for b in L:
rem = rem ^ (b << 24)
for i in range(8):
if rem & 0x80000000:
rem = ((rem << 1) ^ poly32)
else:
rem = (rem << 1)
rem = rem & 0xFFFFFFFF
return rem
#******************************************************************************
#
# Read in the binary files and output the merged binary
#
#******************************************************************************
#def process(boot_loader_filename, app_filename, output):
def process(load_address, app_filename, secinfo_file_name, app_ver, bin_type, str_type, output, align):
# Open the file, and read it into an array of integers.
with open(app_filename, mode = 'rb') as f_app:
app_binarray = f_app.read()
f_app.close()
sec_binarray = bytearray([])
seclen = 0
# Open the file, and read it into an array of integers.
if len(secinfo_file_name) > 0:
with open(secinfo_file_name, mode='rb') as f_sec:
sec_binarray= f_sec.read()
seclen = len(sec_binarray)
app_length = len(app_binarray)
hdr_length = 48; #fixed header length
print("hdr_length ",hdr_length);
#generate mutable byte array for the boot loader
hdr_binarray = bytearray([0]*hdr_length);
# Insert encryption flag, always 0
hdr_binarray[0] = 0x00;
hdr_binarray[1] = 0x00;
hdr_binarray[2] = 0x00;
hdr_binarray[3] = 0x00;
# Insert the application binary load address.
loadaddress = int(load_address,16)
print("load_address ",hex(loadaddress), "(",load_address,")")
hdr_binarray[4] = (loadaddress >> 0) & 0x000000ff;
hdr_binarray[5] = (loadaddress >> 8) & 0x000000ff;
hdr_binarray[6] = (loadaddress >> 16) & 0x000000ff;
hdr_binarray[7] = (loadaddress >> 24) & 0x000000ff;
app_length = len(app_binarray)
print("app_size ",hex(app_length), "(",app_length,")")
app_crc = crc32(app_binarray)
print("app_crc = ",hex(app_crc));
print("Security Info Length", hex(seclen))
pad_binarray = bytearray([])
pad_size = 0
if (seclen > 0):
if (seclen % int(align) != 0):
# Add Padding
pad_size = int(align) - (seclen % int(align))
pad_binarray = bytearray([0]*pad_size);
# put the application binary size
# Total blob length
hdr_binarray[8] = ((app_length + pad_size + seclen) >> 0) & 0x000000ff
hdr_binarray[9] = ((app_length + pad_size + seclen) >> 8) & 0x000000ff
hdr_binarray[10] = ((app_length + pad_size + seclen) >> 16) & 0x000000ff
hdr_binarray[11] = ((app_length + pad_size + seclen) >> 24) & 0x000000ff
# compute the CRC for the blob
crc = crc32(sec_binarray + pad_binarray + app_binarray)
print("blob crc = ",hex(crc));
hdr_binarray[12] = (crc >> 0) & 0x000000ff
hdr_binarray[13] = (crc >> 8) & 0x000000ff
hdr_binarray[14] = (crc >> 16) & 0x000000ff
hdr_binarray[15] = (crc >> 24) & 0x000000ff
hdr_binarray[16] = ((seclen + pad_size) >> 0) & 0x000000ff
hdr_binarray[17] = ((seclen + pad_size) >> 8) & 0x000000ff
hdr_binarray[18] = ((seclen + pad_size) >> 16) & 0x000000ff
hdr_binarray[19] = ((seclen + pad_size) >> 24) & 0x000000ff
# word RFU
hdr_binarray[20] = 0xff
hdr_binarray[21] = 0xff
hdr_binarray[22] = 0xff
hdr_binarray[23] = 0xff
# word RFU
hdr_binarray[24] = 0xFF
hdr_binarray[25] = 0xFF
hdr_binarray[26] = 0xFF
hdr_binarray[27] = 0xFF
# word RFU
hdr_binarray[28] = 0xFF
hdr_binarray[29] = 0xFF
hdr_binarray[30] = 0xFF
hdr_binarray[31] = 0xFF
# application software version here
print("app_ver",int(app_ver,16), "(",app_ver,")")
appver = int(app_ver,16)
hdr_binarray[32] = (appver >> 0) & 0x000000ff;
hdr_binarray[33] = (appver >> 8) & 0x000000ff;
hdr_binarray[34] = (appver >> 16) & 0x000000ff;
hdr_binarray[35] = (appver >> 24) & 0x000000ff;
# binary type here
print("bin_type",int(bin_type,16), "(",bin_type,")")
bintype = int(bin_type,16)
hdr_binarray[36] = (bintype >> 0) & 0x000000ff;
hdr_binarray[37] = (bintype >> 8) & 0x000000ff;
hdr_binarray[38] = (bintype >> 16) & 0x000000ff;
hdr_binarray[39] = (bintype >> 24) & 0x000000ff;
# storage type here, 0 = internal, 1 = external
print("str_type",int(str_type,16), "(",str_type,")")
strtype = int(str_type,16)
if strtype != 0:
strtype = 1
hdr_binarray[40] = (strtype >> 0) & 0x000000ff;
hdr_binarray[41] = (strtype >> 8) & 0x000000ff;
hdr_binarray[42] = (strtype >> 16) & 0x000000ff;
hdr_binarray[43] = (strtype >> 24) & 0x000000ff;
# word RFU
hdr_binarray[44] = 0xff
hdr_binarray[45] = 0xff
hdr_binarray[46] = 0xff
hdr_binarray[47] = 0xff
# now output all three binary arrays in the proper order
with open(output + '.bin', mode = 'wb') as out:
out.write(hdr_binarray)
if (seclen > 0):
print('Adding Security Info of {} bytes...'.format(seclen), flush=True)
out.write(sec_binarray)
if (pad_size != 0):
# Add Padding
print('Adding padding of {} bytes...'.format(pad_size), flush=True)
out.write(pad_binarray)
out.write(app_binarray)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Combine two binary files in to a single download.')
parser.add_argument('--load-address', dest='loadaddress', default='0x4000',
help='Load address of the application.')
parser.add_argument('--appbin', dest='appbin', default='../keil/bin/freertos_fit_amota.bin',
help='Application binary file (app.bin)')
parser.add_argument('--secbin', dest = 'secbin', default='',
help = 'Binary file for (optional) security information')
# add arg version
parser.add_argument('--version', dest='app_ver', default='0x0',
help = 'Software version of the OTA image.')
# add arg binary type
parser.add_argument('--binary-type', dest='bin_type', default='0x0',
help = 'Binary type (0 = Firmware, 1 = Data) to be tranferred OTA')
# add arg binary type
parser.add_argument('--storage-type', dest='str_type', default='0x0',
help = 'Storage type to for the image OTA.')
parser.add_argument('-o', dest = 'output', default = 'binary_array',
help = 'Output filename (without the extension)')
parser.add_argument('-a', dest = 'alignment', default=4,
help = 'Desired alignment for appbin in image blob')
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
process(args.loadaddress, args.appbin, args.secbin, args.app_ver, args.bin_type, args.str_type, args.output, args.alignment)
if __name__ == '__main__':
main()
@@ -0,0 +1,27 @@
#*******************************************************************************
#
# Simple Makefile to prepare binaries for AMOTA for Apollo3.
#
#*******************************************************************************
TOOL_CHAIN?=iar
# Apollo3-BLUE EVB
UPDATEBIN_APOLLO3_BLUE =../../../boards/apollo3_evb/examples/ble_freertos_amota/$(TOOL_CHAIN)/bin/ble_freertos_amota.bin
APPBIN_APOLLO3_BLUE =../../../boards/apollo3_evb/examples/ble_freertos_amota/$(TOOL_CHAIN)/bin/ble_freertos_amota.bin
all: $(APPBIN_APOLLO3_BLUE) $(UPDATEBIN_APOLLO3_BLUE) $(UPDATEBIN_APOLLO3_BLUE_ETHERMIND) $(APPBIN_APOLLO3_BLUE_ETHERMIND)
# Apollo3 Cordio
cp $(APPBIN_APOLLO3_BLUE) starter_binary_apollo3_blue.bin
cp ../../apollo3_scripts/keys_info0.py ../../apollo3_scripts/keys_info.py
python3 ../../apollo3_scripts/create_cust_image_blob.py --bin $(APPBIN_APOLLO3_BLUE) --load-address 0xc000 --magic-num 0xcb -o ../../apollo3_scripts/temp_main_nosecure_ota --version 0x0
python3 ota_binary_converter.py --appbin ../../apollo3_scripts/temp_main_nosecure_ota.bin -o update_binary_apollo3_blue
@rm -rf ../../apollo3_scripts/temp_main_nosecure_ota.bin
$(APPBIN_APOLLO3_BLUE):
$(MAKE) -C ../../../boards/apollo3_evb/examples/ble_freertos_amota/$(TOOL_CHAIN)/ $(MAKECMDGOALS)
clean:
rm -rf *.bin
rm -rf $(APPBIN_APOLLO3_BLUE)
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
import argparse
#******************************************************************************
#
# CRC function that matches the CRC used by the Apollo bootloader.
#
#******************************************************************************
poly32 = 0x1EDC6F41
def crc32(L):
rem = 0
for b in L:
rem = rem ^ (b << 24)
for i in range(8):
if rem & 0x80000000:
rem = ((rem << 1) ^ poly32)
else:
rem = (rem << 1)
rem = rem & 0xFFFFFFFF
return rem
#******************************************************************************
#
# Read in the binary files and output the merged binary
#
#******************************************************************************
#def process(boot_loader_filename, app_filename, output):
def process(load_address, app_filename, secinfo_file_name, app_ver, bin_type, str_type, output, align):
# Open the file, and read it into an array of integers.
with open(app_filename, mode = 'rb') as f_app:
app_binarray = f_app.read()
f_app.close()
sec_binarray = bytearray([])
seclen = 0
# Open the file, and read it into an array of integers.
if len(secinfo_file_name) > 0:
with open(secinfo_file_name, mode='rb') as f_sec:
sec_binarray= f_sec.read()
seclen = len(sec_binarray)
app_length = len(app_binarray)
hdr_length = 48; #fixed header length
print("hdr_length ",hdr_length);
#generate mutable byte array for the boot loader
hdr_binarray = bytearray([0]*hdr_length);
# Insert encryption flag, always 0
hdr_binarray[0] = 0x00;
hdr_binarray[1] = 0x00;
hdr_binarray[2] = 0x00;
hdr_binarray[3] = 0x00;
# Insert the application binary load address.
loadaddress = int(load_address,16)
print("load_address ",hex(loadaddress), "(",load_address,")")
hdr_binarray[4] = (loadaddress >> 0) & 0x000000ff;
hdr_binarray[5] = (loadaddress >> 8) & 0x000000ff;
hdr_binarray[6] = (loadaddress >> 16) & 0x000000ff;
hdr_binarray[7] = (loadaddress >> 24) & 0x000000ff;
app_length = len(app_binarray)
print("app_size ",hex(app_length), "(",app_length,")")
app_crc = crc32(app_binarray)
print("app_crc = ",hex(app_crc));
print("Security Info Length", hex(seclen))
pad_binarray = bytearray([])
pad_size = 0
if (seclen > 0):
if (seclen % int(align) != 0):
# Add Padding
pad_size = int(align) - (seclen % int(align))
pad_binarray = bytearray([0]*pad_size);
# put the application binary size
# Total blob length
hdr_binarray[8] = ((app_length + pad_size + seclen) >> 0) & 0x000000ff
hdr_binarray[9] = ((app_length + pad_size + seclen) >> 8) & 0x000000ff
hdr_binarray[10] = ((app_length + pad_size + seclen) >> 16) & 0x000000ff
hdr_binarray[11] = ((app_length + pad_size + seclen) >> 24) & 0x000000ff
# compute the CRC for the blob
crc = crc32(sec_binarray + pad_binarray + app_binarray)
print("blob crc = ",hex(crc));
hdr_binarray[12] = (crc >> 0) & 0x000000ff
hdr_binarray[13] = (crc >> 8) & 0x000000ff
hdr_binarray[14] = (crc >> 16) & 0x000000ff
hdr_binarray[15] = (crc >> 24) & 0x000000ff
hdr_binarray[16] = ((seclen + pad_size) >> 0) & 0x000000ff
hdr_binarray[17] = ((seclen + pad_size) >> 8) & 0x000000ff
hdr_binarray[18] = ((seclen + pad_size) >> 16) & 0x000000ff
hdr_binarray[19] = ((seclen + pad_size) >> 24) & 0x000000ff
# word RFU
hdr_binarray[20] = 0xff
hdr_binarray[21] = 0xff
hdr_binarray[22] = 0xff
hdr_binarray[23] = 0xff
# word RFU
hdr_binarray[24] = 0xFF
hdr_binarray[25] = 0xFF
hdr_binarray[26] = 0xFF
hdr_binarray[27] = 0xFF
# word RFU
hdr_binarray[28] = 0xFF
hdr_binarray[29] = 0xFF
hdr_binarray[30] = 0xFF
hdr_binarray[31] = 0xFF
# application software version here
print("app_ver",int(app_ver,16), "(",app_ver,")")
appver = int(app_ver,16)
hdr_binarray[32] = (appver >> 0) & 0x000000ff;
hdr_binarray[33] = (appver >> 8) & 0x000000ff;
hdr_binarray[34] = (appver >> 16) & 0x000000ff;
hdr_binarray[35] = (appver >> 24) & 0x000000ff;
# binary type here
print("bin_type",int(bin_type,16), "(",bin_type,")")
bintype = int(bin_type,16)
hdr_binarray[36] = (bintype >> 0) & 0x000000ff;
hdr_binarray[37] = (bintype >> 8) & 0x000000ff;
hdr_binarray[38] = (bintype >> 16) & 0x000000ff;
hdr_binarray[39] = (bintype >> 24) & 0x000000ff;
# storage type here, 0 = internal, 1 = external
print("str_type",int(str_type,16), "(",str_type,")")
strtype = int(str_type,16)
if strtype != 0:
strtype = 1
hdr_binarray[40] = (strtype >> 0) & 0x000000ff;
hdr_binarray[41] = (strtype >> 8) & 0x000000ff;
hdr_binarray[42] = (strtype >> 16) & 0x000000ff;
hdr_binarray[43] = (strtype >> 24) & 0x000000ff;
# word RFU
hdr_binarray[44] = 0xff
hdr_binarray[45] = 0xff
hdr_binarray[46] = 0xff
hdr_binarray[47] = 0xff
# now output all three binary arrays in the proper order
with open(output + '.bin', mode = 'wb') as out:
out.write(hdr_binarray)
if (seclen > 0):
print('Adding Security Info of {} bytes...'.format(seclen), flush=True)
out.write(sec_binarray)
if (pad_size != 0):
# Add Padding
print('Adding padding of {} bytes...'.format(pad_size), flush=True)
out.write(pad_binarray)
out.write(app_binarray)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Combine two binary files in to a single download.')
parser.add_argument('--load-address', dest='loadaddress', default='0x4000',
help='Load address of the application.')
parser.add_argument('--appbin', dest='appbin', default='../keil/bin/freertos_fit_amota.bin',
help='Application binary file (app.bin)')
parser.add_argument('--secbin', dest = 'secbin', default='',
help = 'Binary file for (optional) security information')
# add arg version
parser.add_argument('--version', dest='app_ver', default='0x0',
help = 'Software version of the OTA image.')
# add arg binary type
parser.add_argument('--binary-type', dest='bin_type', default='0x0',
help = 'Binary type (0 = Firmware, 1 = Data) to be tranferred OTA')
# add arg binary type
parser.add_argument('--storage-type', dest='str_type', default='0x0',
help = 'Storage type to for the image OTA.')
parser.add_argument('-o', dest = 'output', default = 'binary_array',
help = 'Output filename (without the extension)')
parser.add_argument('-a', dest = 'alignment', default=4,
help = 'Desired alignment for appbin in image blob')
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
process(args.loadaddress, args.appbin, args.secbin, args.app_ver, args.bin_type, args.str_type, args.output, args.alignment)
if __name__ == '__main__':
main()
@@ -0,0 +1,209 @@
/*********************************************************************
* (c) SEGGER Microcontroller GmbH & Co. KG *
* The Embedded Experts *
* www.segger.com *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : AMA3B2KK-KBR.JLinkScript
Purpose : Handle reset for AmbiqMicro AMA3B2KK series of MCUs
Literature:
[1] J-Link User Guide (UM08001_JLink.pdf)
Additional information:
For more information about public functions that can be implemented
in order to customize J-Link actions, please refer to [1]
*********************************************************************/
/*********************************************************************
*
* ResetTarget()
* Reset and wait until CPU is halted.
*********************************************************************/
void ResetTarget(void) {
// Register Address Values
int AIRCR_ADDR ;
int DHCSR_ADDR ;
int DEMCR_ADDR ;
int AHBAP_REG_CTRL ;
int AHBAP_REG_ADDR ;
int AHBAP_REG_DATA ;
int DP_REG_SELECT ;
int MCUCTRL_SCRATCH0 ;
int MCUCTRL_BOOTLDR ;
int JDEC_PID ;
// Internal Variables
int Ctrl;
int demcr;
int scratch0;
int bootldr;
int jdecpid;
int v;
int Tries;
int Done;
int nonsecure;
int timeout;
// Initialize the Register Address and Internal vars.
AIRCR_ADDR = 0xE000ED0C;
DHCSR_ADDR = 0xE000EDF0;
DEMCR_ADDR = 0xE000EDFC;
MCUCTRL_SCRATCH0 = 0x400401B0;
MCUCTRL_BOOTLDR = 0x400401A0;
JDEC_PID = 0xF0000FE0;
AHBAP_REG_CTRL = 0;
AHBAP_REG_ADDR = 1;
AHBAP_REG_DATA = 3;
DP_REG_SELECT = 2;
nonsecure = 1;
timeout = 0;
// Check global variable to detect whether debugger is using JTAG or SWO and configure JTAG is necessary.
if (MAIN_ActiveTIF == JLINK_TIF_JTAG) {
JLINK_CORESIGHT_Configure("IRPre=0;DRPre=0;IRPost=0;DRPost=0;IRLenDevice=4");
} else {
JLINK_CORESIGHT_Configure(""); // For SWD, no special setup is needed, just output the switching sequence
}
// Power-up complete DAP
Ctrl = 0
| (1 << 30) // System power-up
| (1 << 28) // Debug popwer-up
| (1 << 5) // Clear STICKYERR
;
JLINK_CORESIGHT_WriteDP(1, Ctrl);
// Select AHB-AP and configure it
JLINK_CORESIGHT_WriteDP(DP_REG_SELECT, (0 << 4) | (0 << 24)); // Select AP[0] (AHB-AP) bank 0
JLINK_CORESIGHT_WriteAP(AHBAP_REG_CTRL, (1 << 4) | (1 << 24) | (1 << 25) | (1 << 29) | (2 << 0)); // Auto-increment, Private access, HMASTER = DEBUG, Access size: word
// Enable Debug and Halt the MCU Core.
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DHCSR_ADDR);
v = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
v &= 0x3F; // Mask out "debug" bits
v |= 0xA05F0000; // Debug key to make a write to the DHCSR a valid one
v |= 0x00000002; // Halt the core
v |= 0x00000001; // Enable debug functionalities of the core
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DHCSR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, v);
// Read the Peripheral ID.
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, JDEC_PID);
jdecpid = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
Report1("JDEC PID ", jdecpid);
// Is this Apollo3-Blue or Apollo3-Blue-Plus MCU?
if ((jdecpid & 0xF0) == 0xC0)
{
// Apollo3-Blue or Apollo3-Blue-Plus
Report("Ambiq Apollo3-Blue ResetTarget");
// Read MCUCTRL_BOOTLDR to determine if it is a secure or non-secure chip
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, MCUCTRL_BOOTLDR);
bootldr = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
Report1("Bootldr = ", bootldr);
if ((bootldr & 0x0C000000) == 0x04000000)
{
Report("Secure Part.");
nonsecure = 0;
}
}
if (nonsecure == 0)
{
// Set MCUCTRL Scratch0, indicating that the Bootloader needs to run, then halt when it is finished.
Report("Secure Chip. Bootloader needs to run which will then halt when finish.");
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, MCUCTRL_SCRATCH0);
scratch0 = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, MCUCTRL_SCRATCH0);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, scratch0 | 0x1);
} else
{
// Set VC_CORERESET in the DEMCR.
Report("Non-Secure Chip. Following normal Reset procedure.");
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
demcr = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, demcr | 0x00000001);
}
// Set the SYSRESETREQ bit in the AIRCR.
// This will request the MCU Core to reset.
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, AIRCR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, 0x05FA0004);
// Wait until CPU is halted
Tries = 0;
Done = 0;
do {
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DHCSR_ADDR);
v = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
// Check if CPU is halted. If so, we are done
if (Tries >= 25) // wait for up to 2.5 seconds.
{
Report("Apollo3 (connect): Timeout while waiting for CPU to halt after reset. Manually halting CPU.");
Done = 1;
timeout = 1;
}
else if ((v != 0xFFFFFFFF) && (v & 0x00020000)) // Bit 17: S_HALT in the DHCSR.
{
Report1("CPU halted after reset. Num Tries = ", Tries);
Done = 1;
}
Tries = Tries + 1;
SYS_Sleep(100); // Go to sleep for 100 msec.
} while(Done == 0);
// Let's try one more time using regular reset method
if ((timeout == 1) && (nonsecure == 0))
{
// Set VC_CORERESET
Report("Secure Part Reset timed out. Reverting to normal Reset procedure.");
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
demcr = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, demcr | 0x00000001);
// SYSRESETREQ
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, AIRCR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, 0x05FA0004);
//
// Wait until CPU is halted
//
Tries = 0;
Done = 0;
do {
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DHCSR_ADDR);
v = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
// Check if CPU is halted. If so, we are done
if (Tries >= 25) // wait for up to 2.5 seconds.
{
Report("Apollo3 (connect): Timeout while waiting for CPU to halt after reset. Manually halting CPU.");
Done = 1;
timeout = 1;
}
else if ((v != 0xFFFFFFFF) && (v & 0x00020000)) // Bit 17: S_HALT in the DHCSR.
{
Report1("CPU halted after reset. Num Tries = ", Tries);
Done = 1;
}
Tries = Tries + 1;
SYS_Sleep(100); // Go to sleep for 100 msec.
} while(Done == 0);
}
// If non-secure part,...
if (nonsecure == 1)
{
// Clear VC_CORERESET in the DEMCR.
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
demcr = JLINK_CORESIGHT_ReadAP(AHBAP_REG_DATA);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_ADDR, DEMCR_ADDR);
JLINK_CORESIGHT_WriteAP(AHBAP_REG_DATA, (demcr & 0xFFFFFFFE));
}
}
@@ -0,0 +1,357 @@
#!/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)
#******************************************************************************
#
# 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,178 @@
from am_defines import *
#******************************************************************************
#
# INFOSPACE related definitions
#
#******************************************************************************
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
@@ -0,0 +1,180 @@
#from am_defines import *
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_PROTECT0_O = 0x000001f0
INFO0_WRITE_PROTECT1_O = 0x000001f4
INFO0_WRITE_PROTECT2_O = 0x000001f8
INFO0_WRITE_PROTECT3_O = 0x000001fc
INFO0_COPY_PROTECT0_O = 0x00000200
INFO0_COPY_PROTECT1_O = 0x00000204
INFO0_COPY_PROTECT2_O = 0x00000208
INFO0_COPY_PROTECT3_O = 0x0000020C
INFO0_WRITE_PROTECT0_SBL_O = 0x000009f0
INFO0_WRITE_PROTECT1_SBL_O = 0x000009f4
INFO0_WRITE_PROTECT2_SBL_O = 0x000009f8
INFO0_WRITE_PROTECT3_SBL_O = 0x000009fc
INFO0_COPY_PROTECT0_SBL_O = 0x00000A00
INFO0_COPY_PROTECT1_SBL_O = 0x00000A04
INFO0_COPY_PROTECT2_SBL_O = 0x00000A08
INFO0_COPY_PROTECT3_SBL_O = 0x00000A0C
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
@@ -0,0 +1,299 @@
#!/usr/bin/env python3
# Utility to create image blobs for Corvette Secure Boot
import argparse
import sys
from Crypto.Cipher import AES
import array
import hashlib
import hmac
import os
import binascii
import importlib
from am_defines import *
#from keys_info import keyTblAes, keyTblHmac, minAesKeyIdx, maxAesKeyIdx, minHmacKeyIdx, maxHmacKeyIdx, INFO_KEY
#******************************************************************************
#
# Generate the image blob as per command line parameters
#
#******************************************************************************
def process(loadaddress, appFile, magicNum, crcI, crcB, authI, authB, protection, authKeyIdx, output, encKeyIdx, version, erasePrev, child0, child1, authalgo, encalgo, keyFile):
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()
filenames = keyFile.split('.')
keys = importlib.import_module(filenames[0])
encVal = 0
if (encalgo != 0):
encVal = 1
if ((encKeyIdx < keys.minAesKeyIdx) or (encKeyIdx > keys.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 < keys.minHmacKeyIdx) or (authKeyIdx > keys.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 (loadaddress & (FLASH_PAGE_SIZE - 1)):
am_print("WARNING!!! - load address is not page aligned", level=AM_PRINT_LEVEL_ERROR)
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 = keys.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 - keys.minHmacKeyIdx
if (authB != 0): # Authentication needed
am_print("Boot Authentication Enabled")
# am_print("Key used for HMAC")
# am_print([hex(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
# Initialize the clear image HMAC
sigClr = compute_hmac(keys.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 - keys.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(keys.keyTblAes[encKeyIdx*keySize + n]) for n in range (0, keySize)])
# Encrypted Key
enc_key = encrypt_app_aes(keyAes, keys.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(keys.keyTblHmac[authKeyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, AM_HMAC_SIG_SIZE)])
# Initialize the top level HMAC
sig = compute_hmac(keys.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 + '.bin'
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)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Generate Corvette Image Blob')
parser.add_argument('--bin', dest='appFile', type=argparse.FileType('rb'),
help='binary file (blah.bin)')
parser.add_argument('--load-address', dest='loadaddress', type=auto_int, default=hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN),
help='Load address of the binary.')
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 = '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='outimage',
help = 'Output filename (without the extension)')
parser.add_argument('--authkey', dest = 'authkey', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1),
help = 'Authentication Key Idx? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')')
parser.add_argument('--kek', dest = 'kek', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1),
help = 'KEK Index? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')')
parser.add_argument('--authalgo', dest = 'authalgo', type=auto_int, default=0, choices=range(0, AM_SECBOOT_AUTH_ALGO_MAX+1),
help = helpAuthAlgo)
parser.add_argument('--encalgo', dest = 'encalgo', type=auto_int, default=0, choices = range(0, AM_SECBOOT_ENC_ALGO_MAX+1),
help = helpEncAlgo)
parser.add_argument('--child0', dest = 'child0', type=auto_int, default=hex(0xFFFFFFFF),
help = 'child (blobPtr#0 for Main / feature key for AM3P)')
parser.add_argument('--child1', dest = 'child1', type=auto_int, default=hex(0xFFFFFFFF),
help = 'child (blobPtr#1 for Main)')
parser.add_argument('--version', dest = 'version', type=auto_int, default=0,
help = 'version (15 bit)')
parser.add_argument('--crcI', dest = 'crcI', type=auto_int, default=1, choices=[0,1],
help = 'Install CRC check enabled (Default = Y)?')
parser.add_argument('--crcB', dest = 'crcB', type=auto_int, default=0, choices=[0,1],
help = 'Boot CRC check enabled (Default = N)?')
parser.add_argument('--authI', dest = 'authI', type=auto_int, default=0, choices=[0,1],
help = 'Install Authentication check enabled (Default = N)?')
parser.add_argument('--authB', dest = 'authB', type=auto_int, default=0, choices=[0,1],
help = 'Boot Authentication check enabled (Default = N)?')
parser.add_argument('--erasePrev', dest = 'erasePrev', type=auto_int, default=0, choices=[0,1],
help = 'erasePrev (Valid only for main)')
parser.add_argument('-p', dest = 'protection', type=auto_int, default=0, choices = [0x0, 0x1, 0x2, 0x3],
help = 'protection info 2 bit C W')
parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py',
help='key file in specified format [default = keys_info.py]')
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=helpPrintLevel)
args = parser.parse_args()
args.magic_num = int(args.magic_num, 16)
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
am_set_print_level(args.loglevel)
process(args.loadaddress, 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, args.keyFile)
if __name__ == '__main__':
main()
@@ -0,0 +1,241 @@
#!/usr/bin/env python3
# Utility to generate image blobs for Corvette Bootloader assisted Wired updates
import argparse
import sys
from Crypto.Cipher import AES
import array
import hashlib
import hmac
import os
import binascii
import importlib
from am_defines import *
#from keys_info import keyTblAes, keyTblHmac, minAesKeyIdx, maxAesKeyIdx, minHmacKeyIdx, maxHmacKeyIdx , INFO_KEY, FLASH_KEY
#******************************************************************************
#
# Generate the image blob as per command line parameters
#
#******************************************************************************
def process(appFile, imagetype, loadaddress, authalgo, encalgo, authKeyIdx, encKeyIdx, optionsVal, maxSize, output, keyFile):
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()
# 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
filenames = keyFile.split('.')
keys = importlib.import_module(filenames[0])
if (encalgo != 0):
if ((encKeyIdx < keys.minAesKeyIdx) or (encKeyIdx > keys.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 < keys.minHmacKeyIdx) or (authKeyIdx > keys.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 + '.bin'
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 = keys.INFO_KEY
# word offset
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress>>2)
else:
key = keys.FLASH_KEY
# load address
fill_word(hdr_binarray, AM_WU_IMAGEHDR_OFFSET_ADDR, loadaddress)
if (loadaddress & (FLASH_PAGE_SIZE - 1)):
am_print("WARNING!!! - load address is not page aligned", level=AM_PRINT_LEVEL_ERROR)
# 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 - keys.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(keys.keyTblAes[keyIdx*AM_SECBOOT_KEYIDX_BYTES + n]) for n in range (0, keySize)])
# Encrypted Key
enc_key = encrypt_app_aes(keyAes, keys.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 - keys.minHmacKeyIdx
# am_print("Key used for HMAC")
# am_print([hex(keys.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(keys.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
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Generate Corvette Wired Update Blob')
parser.add_argument('--load-address', dest='loadaddress', type=auto_int, default=hex(0x60000),
help='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('--bin', dest='appFile', type=argparse.FileType('rb'),
help='binary file (blah.bin)')
parser.add_argument('-i', dest = 'imagetype', default=AM_SECBOOT_WIRED_IMAGETYPE_MAIN, type=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)
],
help = '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) '
'- default[Main]')
parser.add_argument('--options', dest = 'options', type=auto_int, default=0x1,
help = '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('-o', dest = 'output', default='wuimage',
help = 'Output filename (without the extension)')
parser.add_argument('--authkey', dest = 'authkey', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1),
help = 'Authentication Key Idx? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')')
parser.add_argument('--kek', dest = 'kek', type=auto_int, default=(AM_SECBOOT_MIN_KEYIDX_INFO0), choices = range(AM_SECBOOT_MIN_KEYIDX_INFO0, AM_SECBOOT_MAX_KEYIDX_INFO0 + 1),
help = 'KEK Index? (' + str(AM_SECBOOT_MIN_KEYIDX_INFO0) + ' to ' + str(AM_SECBOOT_MAX_KEYIDX_INFO0) + ')')
parser.add_argument('--authalgo', dest = 'authalgo', type=auto_int, default=0, choices=range(0, AM_SECBOOT_AUTH_ALGO_MAX+1),
help = helpAuthAlgo)
parser.add_argument('--encalgo', dest = 'encalgo', type=auto_int, default=0, choices = range(0, AM_SECBOOT_ENC_ALGO_MAX+1),
help = helpEncAlgo)
parser.add_argument('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE),
help='Specify the max block size if the image will be downloaded in pieces')
parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py',
help='key file in specified format [default = keys_info.py]')
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=helpPrintLevel)
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
am_set_print_level(args.loglevel)
process(args.appFile, args.imagetype, args.loadaddress, args.authalgo, args.encalgo, args.authkey, args.kek, args.options, args.split, args.output, args.keyFile)
if __name__ == '__main__':
main()
@@ -0,0 +1,409 @@
#!/usr/bin/env python3
# Utility to create info0 for Apollo3
import argparse
import sys
from Crypto.Cipher import AES
import array
import hashlib
import hmac
import os
import binascii
import importlib
from am_defines import *
#from keys_info import keyTblAes, keyTblHmac, custKey
def copy_keys(binarray, offset, key, size):
for i in range (0, size):
binarray[offset + i] = key[i]
#******************************************************************************
#
# Generate the info0 blob as per command line parameters
#
#******************************************************************************
def process(valid, version, output, mainPtr, secPol, keyWrap, secBoot, secBootOnRst, plOnExit, sDbg, bEnErase, infoProg, bNoSramWipe, bSwoCtrl, bDbgAllowed, custTrim, custTrim2, overrideGpio, overridePol, wiredIfMask, wiredSlvInt, wiredI2cAddr, wiredTimeout, u0, u1, u2, u3, u4, u5, krev, arev, chipId0, chipId1, sresv, wprot0, wprot1, rprot0, rprot1, swprot0, swprot1, srprot0, srprot1, wprot2, wprot3, rprot2, rprot3, swprot2, swprot3, srprot2, srprot3, chip, keyFile):
if (chip == 'apollo3'):
import apollo3_info0 as info0
am_print("Apollo3 INFO0")
elif (chip == 'apollo3p'):
import apollo3p_info0 as info0
am_print("Apollo3P INFO0")
filenames = keyFile.split('.')
keys = importlib.import_module(filenames[0])
#generate mutable byte array for the header
hdr_binarray = bytearray([0xFF]*INFO_SIZE_BYTES);
# initialize signature
if (valid == 1):
fill_word(hdr_binarray, info0.INFO0_SIGNATURE0_O, AM_SECBOOT_INFO0_SIGN_PROGRAMMED0)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE1_O, AM_SECBOOT_INFO0_SIGN_PROGRAMMED1)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE2_O, AM_SECBOOT_INFO0_SIGN_PROGRAMMED2)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE3_O, AM_SECBOOT_INFO0_SIGN_PROGRAMMED3)
elif (valid == 0):
fill_word(hdr_binarray, info0.INFO0_SIGNATURE0_O, AM_SECBOOT_INFO0_SIGN_UINIT0)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE1_O, AM_SECBOOT_INFO0_SIGN_UINIT1)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE2_O, AM_SECBOOT_INFO0_SIGN_UINIT2)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE3_O, AM_SECBOOT_INFO0_SIGN_UINIT3)
else:
fill_word(hdr_binarray, info0.INFO0_SIGNATURE0_O, FLASH_INVALID)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE1_O, FLASH_INVALID)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE2_O, FLASH_INVALID)
fill_word(hdr_binarray, info0.INFO0_SIGNATURE3_O, FLASH_INVALID)
am_print("info0 Signature...")
am_print([hex(hdr_binarray[n]) for n in range(0, 16)])
# Flash wipe is no longer supported
bNoFlashWipe = 1
if (valid == 1):
# Build Security word
blAtReset = 1
if (secBoot == 0):
secBoot = 0x5
else:
secBoot = 0x2
if (secBootOnRst == 0):
secBootOnRst = 0x5
else:
secBootOnRst = 0x2
security = (((secPol & 0x7) << 24) | ((keyWrap & 0xf) << 20) | (secBootOnRst << 16) | (secBoot << 12) | ((plOnExit & 0x1) << 11) | ((sDbg & 0x1) << 10) | ((blAtReset & 0x1) << 9) | ((bEnErase & 0x1) << 8) | ((infoProg & 0xf) << 4) | ((bNoFlashWipe & 0x1) << 3) | ((bNoSramWipe & 0x1) << 2) | ((bSwoCtrl & 0x1) << 1) | ((bDbgAllowed & 0x1)))
am_print("Security Word = ", hex(security))
fill_word(hdr_binarray, info0.INFO0_SECURITY_O, security)
# Customer trim
am_print("Customer Trim = ", hex(custTrim))
fill_word(hdr_binarray, info0.INFO0_CUSTOMER_TRIM_O, custTrim)
am_print("Customer Trim2 = ", hex(custTrim2))
fill_word(hdr_binarray, info0.INFO0_CUSTOMER_TRIM2_O, custTrim2)
# Override
override = ((overridePol & 0x1) << 7) | (overrideGpio & 0x7f)
am_print("Override = ", hex(override))
fill_word(hdr_binarray, info0.INFO0_SECURITY_OVR_O, override)
# Wired interface config
wired = ((wiredIfMask & 0x7) | ((wiredSlvInt & 0x3f) << 3) | ((wiredI2cAddr & 0x7f) << 9) | ((wiredTimeout & 0xffff) << 16))
am_print("WiredCfg = ", hex(wired))
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_CFG_O, wired)
# Wired UART cfg
if (wiredIfMask & 0x1): # UART
am_print("UART Config ", hex(u0), hex(u1), hex(u2), hex(u3), hex(u4), hex(u5))
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG0_O, u0)
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG1_O, u1)
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG2_O, u2)
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG3_O, u3)
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG4_O, u4)
fill_word(hdr_binarray, info0.INFO0_SECURITY_WIRED_IFC_CFG5_O, u5)
# version
am_print("Version = ", hex(version))
fill_word(hdr_binarray, info0.INFO0_SECURITY_VERSION_O, version)
# main ptr
am_print("Main Ptr = ", hex(mainPtr))
fill_word(hdr_binarray, info0.INFO0_MAIN_PTR1_O, mainPtr)
# SRAM Reservation
am_print("SRAM Reservation = ", hex(sresv))
fill_word(hdr_binarray, info0.INFO0_SECURITY_SRAM_RESV_O, sresv)
# Flash Protections
if (chip == 'apollo3'):
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT_L_O, wprot0)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT_H_O, wprot1)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT_L_O, rprot0)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT_H_O, rprot1)
am_print("Permanent Write Protections = ", hex(wprot0), ":", hex(wprot1))
am_print("Permanent Copy Protections = ", hex(rprot0), ":", hex(rprot1))
elif (chip == 'apollo3p'):
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT0_O, wprot0)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT1_O, wprot1)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT0_O, rprot0)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT1_O, rprot1)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT2_O, wprot2)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT3_O, wprot3)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT2_O, rprot2)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT3_O, rprot3)
am_print("Permanent Write Protections = ", hex(wprot0), ":", hex(wprot1), ":", hex(wprot2), ":", hex(wprot3))
am_print("Permanent Copy Protections = ", hex(rprot0), ":", hex(rprot1), ":", hex(rprot2), ":", hex(rprot3))
if (chip == 'apollo3'):
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT_SBL_L_O, swprot0)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT_SBL_H_O, swprot1)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT_SBL_L_O, srprot0)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT_SBL_H_O, srprot1)
am_print("SBL Overridable Write Protections = ", hex(swprot0), ":", hex(swprot1))
am_print("SBL Overridable Copy Protections = ", hex(srprot0), ":", hex(srprot1))
elif (chip == 'apollo3p'):
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT0_SBL_O, swprot0)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT1_SBL_O, swprot1)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT0_SBL_O, srprot0)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT1_SBL_O, srprot1)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT2_SBL_O, swprot2)
fill_word(hdr_binarray, info0.INFO0_WRITE_PROTECT3_SBL_O, swprot3)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT2_SBL_O, srprot2)
fill_word(hdr_binarray, info0.INFO0_COPY_PROTECT3_SBL_O, srprot3)
am_print("SBL Overridable Write Protections = ", hex(swprot0), ":", hex(swprot1), ":", hex(swprot2), ":", hex(swprot3))
am_print("SBL Overridable Copy Protections = ", hex(srprot0), ":", hex(srprot1), ":", hex(srprot2), ":", hex(srprot3))
# Customer Key
am_print("Customer Key")
am_print([hex(n) for n in keys.custKey])
copy_keys(hdr_binarray, info0.INFO0_CUSTOMER_KEY0_O, keys.custKey, 16)
# Keys
chipId = int_to_bytes(chipId0) + int_to_bytes(chipId1)
while (len(chipId) != AM_SECBOOT_KEYIDX_BYTES):
chipId = chipId + chipId
am_print("chipID 16")
am_print([hex(n) for n in chipId])
kekOffset = info0.INFO0_CUST_KEK_W0_O
authKeyOffset = info0.INFO0_CUST_AUTH_W0_O
if (keyWrap == 0): ## None
am_print("wrap mode 0")
am_print("KEK")
for i in range (0, INFO_MAX_ENC_KEYS):
am_print([hex(n) for n in keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]])
copy_keys(hdr_binarray, kekOffset + i*AM_SECBOOT_KEYIDX_BYTES, keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], AM_SECBOOT_KEYIDX_BYTES)
am_print("AuthKey")
for i in range (0, INFO_MAX_AUTH_KEYS):
am_print([hex(n) for n in keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]])
copy_keys(hdr_binarray, authKeyOffset + i*AM_SECBOOT_KEYIDX_BYTES, keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], AM_SECBOOT_KEYIDX_BYTES)
elif (keyWrap == 1): ## XOR
am_print("wrap mode 1")
am_print("KEK")
for i in range (0, INFO_MAX_ENC_KEYS):
scrambledKey = keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]
for j in range (0, AM_SECBOOT_KEYIDX_BYTES):
scrambledKey[j] = scrambledKey[j] ^ keys.custKey[j] ^ chipId[j]
am_print([hex(n) for n in scrambledKey])
copy_keys(hdr_binarray, kekOffset + i*AM_SECBOOT_KEYIDX_BYTES, scrambledKey, AM_SECBOOT_KEYIDX_BYTES)
am_print("AuthKey")
for i in range (0, INFO_MAX_AUTH_KEYS):
scrambledKey = keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]
for j in range (0, AM_SECBOOT_KEYIDX_BYTES):
scrambledKey[j] = scrambledKey[j] ^ keys.custKey[j] ^ chipId[j]
am_print([hex(n) for n in scrambledKey])
copy_keys(hdr_binarray, authKeyOffset + i*AM_SECBOOT_KEYIDX_BYTES, scrambledKey, AM_SECBOOT_KEYIDX_BYTES)
elif (keyWrap == 2): ## AES128
am_print("wrap mode 2")
am_print("KEK")
for i in range (0, INFO_MAX_ENC_KEYS):
scrambledKey = encrypt_app_aes(keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], keys.custKey, chipId)
am_print([hex(n) for n in scrambledKey])
copy_keys(hdr_binarray, kekOffset + i*AM_SECBOOT_KEYIDX_BYTES, scrambledKey, AM_SECBOOT_KEYIDX_BYTES)
am_print("AuthKey")
for i in range (0, INFO_MAX_AUTH_KEYS):
scrambledKey = encrypt_app_aes(keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], keys.custKey, chipId)
am_print([hex(n) for n in scrambledKey])
copy_keys(hdr_binarray, authKeyOffset + i*AM_SECBOOT_KEYIDX_BYTES, scrambledKey, AM_SECBOOT_KEYIDX_BYTES)
else:
am_print("Unknown wrap mode")
# Revocation Masks
am_print("KREV Mask = ", hex(krev))
fill_word(hdr_binarray, info0.INFO0_KREVTRACK_O, krev)
am_print("AREV Mask = ", hex(arev))
fill_word(hdr_binarray, info0.INFO0_AREVTRACK_O, arev)
# now output all three binary arrays in the proper order
with open(output + '.bin', mode = 'wb') as out:
am_print("Writing to file ", output + '.bin')
out.write(hdr_binarray)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Generate Corvette Info0 Blob')
parser.add_argument('output',
help = 'Output filename (without the extension)')
parser.add_argument('--valid', dest = 'valid', type=auto_int, default=1, choices = [0,1,2],
help = 'INFO0 Valid 0 = Uninitialized, 1 = Valid, 2 = Invalid (Default = 1)?')
parser.add_argument('--version', dest = 'version', type=auto_int, default=0,
help = 'version (Default = 0)?')
parser.add_argument('--main', dest = 'mainPtr', type=auto_int, default=hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN),
help = 'Main Firmware location (Default = ' + str(hex(AM_SECBOOT_DEFAULT_NONSECURE_MAIN)) + ')?')
parser.add_argument('--secpol', dest = 'secPol', type=auto_int, default=hex(0x0), choices = [0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7],
help = 'Security Policy Bitmask (Default = 0)? (bit 0 = Auth, bit 1 = Enc, bit 2 = Version Rollback)')
parser.add_argument('--wrap', dest = 'keyWrap', type=auto_int, default=0, choices = [0,1,2],
help = 'KeyWrap Algo (Default = 0)? (0 = none, 1 = XOR, 2 = AES128)')
parser.add_argument('--sRst', dest = 'secBootOnRst', type=auto_int, default=0, choices = [0,1],
help = 'Secure Boot on Soft Reset (Default = 0) ?')
parser.add_argument('-s', dest = 'secBoot', type=auto_int, default=0, choices = [0,1],
help = 'Secure Boot (Default = 0) ?')
parser.add_argument('--pl', dest = 'plOnExit', type=auto_int, default=0, choices = [0,1],
help = 'Protection Lock Enabled (Default = 0) ?')
parser.add_argument('--sDbgAllowed', dest = 'sDbg', type=auto_int, default=1, choices = [0,1],
help = 'Debugger allowed during (optional) Secondary Bootloader (Default = 1) ?')
parser.add_argument('--erase', dest = 'bEnErase', type=auto_int, default=1, choices = [0,1],
help = 'Info0 Erase Allowed (Default = 1) ?')
parser.add_argument('--prog', dest = 'infoProg', type=auto_int, default=hex(0xf),
help = 'INFO0 Program allowed (1 bit per quadrant) (Default = 0xf) ?')
parser.add_argument('--snowipe', dest = 'bNoSramWipe', type=auto_int, default=1, choices = [0,1],
help = ' Do not wipe SRAM on debugger connection (Default = 1) ?')
parser.add_argument('--swo', dest = 'bSwoCtrl', type=auto_int, default=1, choices = [0,1],
help = 'debugger connection allowed (Default = 1) ?')
parser.add_argument('--dbgprot', dest = 'bDbgAllowed', type=auto_int, default=1, choices = [0,1],
help = 'Do not lock debugger (Default = 1) ?')
parser.add_argument('--trim', dest = 'custTrim', type=auto_int, default=hex(0xFFFFFFFF),
help = 'customer trim ?')
parser.add_argument('--trim2', dest = 'custTrim2', type=auto_int, default=hex(0xFFFFFFFF),
help = 'customer trim 2?')
parser.add_argument('--gpio', dest = 'overrideGpio', type=auto_int, default=hex(0x7f),
help = 'Override GPIO (7 bit - in hex) - 0x7f for disabled (Default = 0x7f)')
parser.add_argument('--gpiolvl', dest = 'overridePol', type=auto_int, default=0, choices = [0,1],
help = 'Override GPIO Polarity (0 = low, 1 = hi) (Default = 0)')
parser.add_argument('--wmask', dest = 'wiredIfMask', type=auto_int, default=0x1,
help = 'Wired interface mask (bit 0 = UART, bit 1 = SPI, bit 2 = I2C) (default = UART)')
parser.add_argument('--wSlInt', dest = 'wiredSlvInt', type=auto_int, default=4,
help = 'Wired IOS interface handshake pin (default = 4)')
parser.add_argument('--wI2c', dest = 'wiredI2cAddr', type=auto_int, default=hex(0x20),
help = 'Wired IOS interface I2C Address (default = 0x20)')
parser.add_argument('--wTO', dest = 'wiredTimeout', type=auto_int, default=20000,
help = 'Wired interface timeout in millisec (default = 20000)')
parser.add_argument('--u0', dest = 'u0', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 0 (default = 0xFFFFFFFF)')
parser.add_argument('--u1', dest = 'u1', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 1 (default = 0xFFFFFFFF)')
parser.add_argument('--u2', dest = 'u2', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 2 (default = 0xFFFFFFFF)')
parser.add_argument('--u3', dest = 'u3', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 3 (default = 0xFFFFFFFF)')
parser.add_argument('--u4', dest = 'u4', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 4 (default = 0xFFFFFFFF)')
parser.add_argument('--u5', dest = 'u5', type=auto_int, default=hex(0xFFFFFFFF),
help = 'UART Config 5 (default = 0xFFFFFFFF)')
parser.add_argument('--krev', dest='krev', type=auto_int, default = hex(0xFFFFFFFF),
help='KEK Revocation Mask (Default 0xFFFFFFFF)')
parser.add_argument('--arev', dest='arev', type=auto_int, default = hex(0xFFFFFFFF),
help='AuthKey Revocation Mask (Default 0xFFFFFFFF)')
parser.add_argument('--sresv', dest='sresv', type=auto_int, default = hex(0x0),
help='SRAM Reservation (Default 0x0)')
parser.add_argument('--chipid0', dest='chipId0', type=auto_int, default = 0,
help='CHIPID0 for the device (Default 0)')
parser.add_argument('--chipid1', dest='chipId1', type=auto_int, default = 0,
help='CHIPID1 for the device (Default 0)')
parser.add_argument('--wprot0', dest='wprot0', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Write Protections Mask for flash#0 (Default 0xFFFFFFFF)')
parser.add_argument('--wprot1', dest='wprot1', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Write Protections Mask for flash#1 (Default 0xFFFFFFFF)')
parser.add_argument('--rprot0', dest='rprot0', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Copy Protections Mask for flash#0 (Default 0xFFFFFFFF)')
parser.add_argument('--rprot1', dest='rprot1', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Copy Protections Mask for flash#1 (Default 0xFFFFFFFF)')
parser.add_argument('--swprot0', dest='swprot0', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Write Protections Mask for flash#0 (Default 0xFFFFFFFF)')
parser.add_argument('--swprot1', dest='swprot1', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Write Protections Mask for flash#1 (Default 0xFFFFFFFF)')
parser.add_argument('--srprot0', dest='srprot0', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Copy Protections Mask for flash#0 (Default 0xFFFFFFFF)')
parser.add_argument('--srprot1', dest='srprot1', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Copy Protections Mask for flash#1 (Default 0xFFFFFFFF)')
parser.add_argument('--wprot2', dest='wprot2', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Write Protections Mask for flash#2 (Default 0xFFFFFFFF)')
parser.add_argument('--wprot3', dest='wprot3', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Write Protections Mask for flash#3 (Default 0xFFFFFFFF)')
parser.add_argument('--rprot2', dest='rprot2', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Copy Protections Mask for flash#2 (Default 0xFFFFFFFF)')
parser.add_argument('--rprot3', dest='rprot3', type=auto_int, default = hex(0xFFFFFFFF),
help='Permanent Copy Protections Mask for flash#3 (Default 0xFFFFFFFF)')
parser.add_argument('--swprot2', dest='swprot2', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Write Protections Mask for flash#2 (Default 0xFFFFFFFF)')
parser.add_argument('--swprot3', dest='swprot3', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Write Protections Mask for flash#3 (Default 0xFFFFFFFF)')
parser.add_argument('--srprot2', dest='srprot2', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Copy Protections Mask for flash#2 (Default 0xFFFFFFFF)')
parser.add_argument('--srprot3', dest='srprot3', type=auto_int, default = hex(0xFFFFFFFF),
help='SBL overridable Copy Protections Mask for flash#3 (Default 0xFFFFFFFF)')
parser.add_argument('--chipType', dest='chip', type=str, required=True,
choices = ['apollo3', 'apollo3p'],
help='Chip Type: apollo3, apollo3p (default = apollo3)')
parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py',
help='key file in specified format [default = keys_info.py]')
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=helpPrintLevel)
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
am_set_print_level(args.loglevel)
process(args.valid, args.version, args.output, args.mainPtr, args.secPol, args.keyWrap, args.secBoot, args.secBootOnRst, args.plOnExit, args.sDbg, args.bEnErase, args.infoProg, args.bNoSramWipe, args.bSwoCtrl, args.bDbgAllowed, args.custTrim, args.custTrim2, args.overrideGpio, args.overridePol, args.wiredIfMask, args.wiredSlvInt, args.wiredI2cAddr, args.wiredTimeout, args.u0, args.u1, args.u2, args.u3, args.u4, args.u5, args.krev, args.arev, args.chipId0, args.chipId1, args.sresv, args.wprot0, args.wprot1, args.rprot0, args.rprot1, args.swprot0, args.swprot1, args.srprot0, args.srprot1, args.wprot2, args.wprot3, args.rprot2, args.rprot3, args.swprot2, args.swprot3, args.srprot2, args.srprot3, args.chip, args.keyFile)
if __name__ == '__main__':
main()
@@ -0,0 +1,133 @@
#!/usr/bin/env python3
import argparse
import sys
from Crypto.Cipher import AES
import array
import os
import binascii
import importlib
from am_defines import *
#from keys_info import keyTblAes, keyTblHmac, wrapKey, minWrapMode
#******************************************************************************
#
# Generate the device keys for a given chipId based on well known keys and key Wrap mode
#
#******************************************************************************
def process(wrapMode, chipId0, chipId1, inFile, keyFile):
filenames = keyFile.split('.')
keys = importlib.import_module(filenames[0])
if (inFile is not None):
for line in inFile:
linecontent = line.rstrip()
if linecontent:
chipids = line.split(':')
process_chip(wrapMode, int(chipids[0], 16), int(chipids[1], 16), keys)
else:
process_chip(wrapMode, chipId0, chipId1, keys)
def process_chip(wrapMode, chipId0, chipId1, keys):
output = str(hex(chipId0)) + '_' + str(hex(chipId1))
chipId = int_to_bytes(chipId0) + int_to_bytes(chipId1)
while (len(chipId) != AM_SECBOOT_KEYIDX_BYTES):
chipId = chipId + chipId
am_print("ChipID: ", [hex(n) for n in chipId], level=AM_PRINT_LEVEL_VERBOSE)
if (wrapMode < keys.minWrapMode):
am_print("Invalid Wrap Mode", level=AM_PRINT_LEVEL_ERROR)
return
if (wrapMode == AM_SECBOOT_KEYWRAP_NONE): ## None
am_print("wrap mode None")
with open(output + '_info_kek.bin', mode = 'wb') as out_info_kek:
am_print("writing to " + output + '_info_kek.bin')
for i in range (0, INFO_MAX_ENC_KEYS):
am_print([hex(n) for n in keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]], level=AM_PRINT_LEVEL_DEBUG)
out_info_kek.write(bytearray(keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]))
with open(output + '_info_authkey.bin', mode = 'wb') as out_info_authkey:
am_print("writing to " + output + '_info_authkey.bin')
for i in range (0, INFO_MAX_AUTH_KEYS):
am_print([hex(n) for n in keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]], level=AM_PRINT_LEVEL_DEBUG)
out_info_authkey.write(bytearray(keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]))
elif (wrapMode == AM_SECBOOT_KEYWRAP_XOR): ## XOR
am_print("wrap mode XOR")
with open(output + '_info_kek.bin', mode = 'wb') as out_info_kek:
am_print("writing to " + output + '_info_kek.bin')
for i in range (0, INFO_MAX_ENC_KEYS):
scrambledKey = keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]
for j in range (0, AM_SECBOOT_KEYIDX_BYTES):
scrambledKey[j] = scrambledKey[j] ^ keys.wrapKey[j] ^ chipId[j]
am_print([hex(n) for n in scrambledKey], level=AM_PRINT_LEVEL_DEBUG)
out_info_kek.write(bytearray(scrambledKey))
with open(output + '_info_authkey.bin', mode = 'wb') as out_info_authkey:
am_print("writing to " + output + '_info_authkey.bin')
for i in range (0, INFO_MAX_AUTH_KEYS):
scrambledKey = keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)]
for j in range (0, AM_SECBOOT_KEYIDX_BYTES):
scrambledKey[j] = scrambledKey[j] ^ keys.wrapKey[j] ^ chipId[j]
am_print([hex(n) for n in scrambledKey], level=AM_PRINT_LEVEL_DEBUG)
out_info_authkey.write(bytearray(scrambledKey))
elif (wrapMode == AM_SECBOOT_KEYWRAP_AES128): ## AES128
am_print("wrap mode AES128")
with open(output + '_info_kek.bin', mode = 'wb') as out_info_kek:
am_print("writing to " + output + '_info_kek.bin')
for i in range (0, INFO_MAX_ENC_KEYS):
scrambledKey = encrypt_app_aes(keys.keyTblAes[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], keys.wrapKey, chipId)
am_print([hex(n) for n in scrambledKey], level=AM_PRINT_LEVEL_DEBUG)
out_info_kek.write(bytearray(scrambledKey))
with open(output + '_info_authkey.bin', mode = 'wb') as out_info_authkey:
am_print("writing to " + output + '_info_authkey.bin')
for i in range (0, INFO_MAX_AUTH_KEYS):
scrambledKey = encrypt_app_aes(keys.keyTblHmac[i*AM_SECBOOT_KEYIDX_BYTES:(i*AM_SECBOOT_KEYIDX_BYTES + AM_SECBOOT_KEYIDX_BYTES)], keys.wrapKey, chipId)
am_print([hex(n) for n in scrambledKey], level=AM_PRINT_LEVEL_DEBUG)
out_info_authkey.write(bytearray(scrambledKey))
else:
am_print("Unknown wrap mode", level=AM_PRINT_LEVEL_ERROR)
return
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Generate Corvette Device keys')
parser.add_argument('--wrap', dest='wrap', type=auto_int, default=AM_SECBOOT_KEYWRAP_NONE, choices = range(AM_SECBOOT_KEYWRAP_NONE, AM_SECBOOT_KEYWRAP_MAX+1),
help='wrap mode (0 = None, 1 = XOR, 2 = AES128) [default ' + str(AM_SECBOOT_KEYWRAP_NONE) +'] ')
parser.add_argument('--chipid0', dest='chipId0', type=auto_int, default=0,
help='CHIPID0 for the device')
parser.add_argument('--chipid1', dest='chipId1', type=auto_int, default=0,
help='CHIPID1 for the device')
parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py',
help='key file in specified format [default = keys_info.py]')
parser.add_argument('-f', type=argparse.FileType('r'), dest='inFile', nargs='?',
help='Input text file with chip ID Tuples (chipID0:chipID1 in each line)')
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=helpPrintLevel)
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
am_set_print_level(args.loglevel)
process(args.wrap, args.chipId0, args.chipId1, args.inFile, args.keyFile)
if __name__ == '__main__':
main()
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
# Utility to generate Recover Message for Corvette Bootloader assisted Wired updates
import argparse
import sys
from Crypto.Cipher import AES
import array
import hashlib
import hmac
import os
import binascii
import importlib
from am_defines import *
#from keys_info import recoveryKey
#******************************************************************************
#
# Generate the image blob as per command line parameters
#
#******************************************************************************
def process(binfile, n0, n1, n2, n3, custId, output, keyFile):
if (binfile != ''):
# Read the binary file from the command line.
with open(binfile, mode='rb') as binfile:
blob = binfile.read()
# Gather the important binary metadata.
blobLen = len(blob)
else:
blob = bytearray()
blobLen = 0
filenames = keyFile.split('.')
keys = importlib.import_module(filenames[0])
# Send Recover command
print('Building Recover Command.')
hdr_binarray = bytearray([0x00]*(AM_WU_RECOVERY_HDR_SIZE))
fill_word(hdr_binarray, 4, (((blobLen + AM_WU_RECOVERY_HDR_SIZE) << 16) | AM_SECBOOT_WIRED_MSGTYPE_RECOVER))
# CustId
fill_word(hdr_binarray, AM_WU_RECOVERY_HDR_OFFSET_CUSTID, custId)
# Recovery key
for x in range(0, AM_SECBOOT_AESCBC_BLOCK_SIZE_BYTES):
hdr_binarray[AM_WU_RECOVERY_HDR_OFFSET_RECKEY + x] = keys.recoveryKey[x]
# Nonce
fill_word(hdr_binarray, AM_WU_RECOVERY_HDR_OFFSET_NONCE + 0, n0)
fill_word(hdr_binarray, AM_WU_RECOVERY_HDR_OFFSET_NONCE + 4, n1)
fill_word(hdr_binarray, AM_WU_RECOVERY_HDR_OFFSET_NONCE + 8, n2)
fill_word(hdr_binarray, AM_WU_RECOVERY_HDR_OFFSET_NONCE + 12, n3)
# Compute crc
crc = crc32(hdr_binarray[4:AM_WU_RECOVERY_HDR_SIZE] + blob)
fill_word(hdr_binarray, 0, crc)
# Write the message as a raw message
with open(output + '.msg', mode = 'wb') as out:
print("Writing to file ", output + '.msg')
out.write(hdr_binarray + blob)
def parse_arguments():
parser = argparse.ArgumentParser(description =
'Generate Corvette Recovery Message')
parser.add_argument('-f', dest='binfile', default='',
help = 'Binary file representing the raw Recovery Blob provided by Ambiq')
parser.add_argument('-o', dest = 'output',
help = 'Output filename (without the extension)')
parser.add_argument('--n0', dest = 'n0', type=auto_int, default=0xFFFFFFFF,
help = 'Nonce 0 - should correspond to the value provided to Ambiq (default = 0xFFFFFFFF)')
parser.add_argument('--n1', dest = 'n1', type=auto_int, default=0xFFFFFFFF,
help = 'Nonce 1 - should correspond to the value provided to Ambiq (default = 0xFFFFFFFF)')
parser.add_argument('--n2', dest = 'n2', type=auto_int, default=0xFFFFFFFF,
help = 'Nonce 2 - should correspond to the value provided to Ambiq (default = 0xFFFFFFFF)')
parser.add_argument('--n3', dest = 'n3', type=auto_int, default=0xFFFFFFFF,
help = 'Nonce 3 - should correspond to the value provided to Ambiq (default = 0xFFFFFFFF)')
parser.add_argument('--custId', dest='custId', type=auto_int, default=0xFFFFFFFF,
help='Customer ID - should correspond to the value provided to Ambiq (default = 0xFFFFFFFF)')
parser.add_argument('-k', type=str, dest='keyFile', nargs='?', default='keys_info.py',
help='key file in specified format [default = keys_info.py]')
args = parser.parse_args()
return args
#******************************************************************************
#
# Main function.
#
#******************************************************************************
def main():
# Read the arguments.
args = parse_arguments()
process(args.binfile, args.n0, args.n1, args.n2, args.n3, args.custId, args.output, args.keyFile)
if __name__ == '__main__':
main()
@@ -0,0 +1,36 @@
// connect to device
// NEED to specify -device AMA3B2KK-KBR (for Apollo3P) or -device AMA3B1KK-KBR/KCR (for Apollo3) from command line
si SWD
speed 1000
r
sleep 10
//set C runtime environment
wreg MSP, 0x10000100
// erase info0
w4 0x10000000 0 // flash info instance
w4 0x10000004 0xd894e09e // info 0 key
w4 0x10000008 0xFFFFFFFF // clear return value
setPC 0x08000085 // call the ROM helper function flash_info_erase_from_sram
g
sleep 50
mem32 0x10000008 1 // dump return value for check
// program info0
w4 0x10000000 0 // flash info instance
w4 0x10000004 0 // offset
w4 0x10000008 0x800 // length in words
w4 0x1000000C 0xd894e09e // info 0 key
w4 0x10000010 0xFFFFFFFF // clear return value
loadbin info0.bin 0x10001000 //load the info0 binary into sram
setPC 0x08000061 // call the ROM helper function flash_program_info_area_from_sram
g
sleep 50
mem32 0x10000010 1 // dump return value for check
// perform software POI
w4 0x40000004 0x1B
// quit
qc
@@ -0,0 +1,20 @@
//connect to device
// NEED to specify -device AMA3B2KK-KBR (for Apollo3P) or -device AMA3B1KK-KBR/KCR (for Apollo3) from command line
si SWD
speed 1000
r
sleep 10
// download image to 0x80000 - **** Change this based on device flashmap ****
loadbin sbl.bin 0x80000 //load the SBL binary into temp location in flash
// Set up OTA Descriptor at 0xF0000 // **** Change this based on device flashmap ****
w4 0xF0000 0x80003
w4 0xF0004 0xFFFFFFFF
// Set up OTAPointer
w4 0x40020264 0xF0003
// Reset POI
w4 0x40000004 0x1B
// quit
qc
@@ -0,0 +1,25 @@
//connect to device
// NEED to specify -device AMA3B2KK-KBR (for Apollo3P) or -device AMA3B1KK-KBR/KCR (for Apollo3) from command line
si SWD
speed 1000
r
sleep 10
//set C runtime environment
wreg MSP, 0x10000100
// unlock info0 access
w4 0x40030078 1 // unlock the info0 secured portion
w4 0x40030080 DEADBEEF DEADBEEF DEADBEEF DEADBEEF // write the unlock key
// this should be replaced with actual customer key!!!
mem32 0x4003007C 1 // dump the lock status for check
// read back info0 for verify
verifybin info0.bin 0x50020000 // verify the info 0 content with the original source file
savebin info0_dump.bin 0x50020000 0x2000 // read out the info 0 content for saving
w4 0x40030080 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF // lock the secured portion
// reset and quit
r
qc
@@ -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,11 @@
@echo off
if [%1]==[] goto usage
jlink -device %1 -CommanderScript jlink-prog-info0.txt
pause
goto :eof
:usage
@echo Usage: %0 ^<Chip Part#^>
exit /B 1
@@ -0,0 +1,9 @@
sbl_v2_3_93e8 SBL v2 created on 7/26/2018. Change to disable Encap OTA Support.
SBL_v2 Release candidate #2. Header version 3
sbl_v3_4_994c SBL v3 created on 2/19/2019. Adds IOS SPI and I2C support.
Header version 4.
sbl_v3_5_99a0 SBL v4 created on 3/8/2019. Eliminates setting of HFADJ in
am_secboot_config_wired_uart() for B0. Header version 5.
@@ -0,0 +1,5 @@
sbl3p_v2_2_a0a5 (OTA: 0xA3009400:0x53D5B051) - SBL v2
enhancement for SRAM Reservation feature
with fix to GPIO macro to allow GPIO>63 to be used
Header Version 2
@@ -0,0 +1,345 @@
#!/usr/bin/env python3
# UART wired update host for Corvette Bootloader
import argparse
import serial
import sys
import array
import os
import binascii
from am_defines import *
#******************************************************************************
#
# Main function
#
#******************************************************************************
def main():
# Open a serial port, and communicate with Device
#
# We will use a UART timeout value of 12 second. This should be long
# enough that the slave will respond to us, but short enough that a human
# operator should see errors quickly..
# 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.
print('Connecting with Corvette over serial port {}...'.format(args.port), flush=True)
with serial.Serial(args.port, args.baud, timeout=12) as ser:
connect_device(ser)
print('Done.')
#******************************************************************************
#
# Communicate with Device
#
# Given a serial port, connects to the target device using the
# UART.
#
#******************************************************************************
def connect_device(ser):
# Send Hello
#generate mutable byte array for the header
hello = bytearray([0x00]*4);
fill_word(hello, 0, ((8 << 16) | AM_SECBOOT_WIRED_MSGTYPE_HELLO))
print('Sending Hello.')
response = send_command(hello, 88, ser)
print("Received response for Hello")
word = word_from_bytes(response, 4)
if ((word & 0xFFFF) == AM_SECBOOT_WIRED_MSGTYPE_STATUS):
# Received Status
print("Received Status")
print("length = ", hex((word >> 16)))
print("version = ", hex(word_from_bytes(response, 8)))
print("Max Storage = ", hex(word_from_bytes(response, 12)))
print("Status = ", hex(word_from_bytes(response, 16)))
print("State = ", hex(word_from_bytes(response, 20)))
print("AMInfo = ")
for x in range(24, 88, 4):
print(hex(word_from_bytes(response, x)))
abort = args.abort
if (abort != -1):
# Send OTA Desc
print('Sending Abort command.')
abortMsg = bytearray([0x00]*8);
fill_word(abortMsg, 0, ((12 << 16) | AM_SECBOOT_WIRED_MSGTYPE_ABORT))
fill_word(abortMsg, 4, abort)
send_ackd_command(abortMsg, ser)
otadescaddr = args.otadesc
if (otadescaddr != 0xFFFFFFFF):
# Send OTA Desc
print('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)
send_ackd_command(otaDesc, ser)
imageType = args.imagetype
if (args.binfile != ''):
# Read the binary file from the command line.
with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
# Gather the important binary metadata.
totalLen = len(application)
# Send Update command
print('Sending Update Command.')
# It is assumed that maxSize is 256b multiple
maxImageSize = args.split
if ((maxImageSize & (FLASH_PAGE_SIZE - 1)) != 0):
print ("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
print("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
print("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)
send_ackd_command(update, ser)
# 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)
print("Sending Data Packet of length ", chunklen)
send_ackd_command(dataMsg + chunk, ser)
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
print('Sending Raw Command.')
ser.write(blob)
if (args.reset != 0):
# Send reset
print('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)
send_ackd_command(resetmsg, ser)
else:
# Received Wrong message
print("Received Unknown Message")
word = word_from_bytes(response, 4)
print("msgType = ", hex(word & 0xFFFF))
print("Length = ", hex(word >> 16))
print([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):
for numTries in range(1, 5 , 1):
response = send_command(command, 20, ser)
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):
print("Received NACK")
print("msgType = ", hex(word_from_bytes(response, 8)))
print("error = ", hex(word_from_bytes(response, 12)))
print("seqNo = ", hex(word_from_bytes(response, 16)))
if (numTries < 4):
print("Retry # ", numTries)
else:
print("Exceed number of retries")
else:
break
else:
print("!!!Wired Upgrade Unsuccessful!!!....unexpected respose - Terminating the script")
exit()
if (numTries == 4):
print("!!!Wired Upgrade Unsuccessful!!!....numTries exceeded - Terminating the script")
exit()
return response
#******************************************************************************
#
# Send command
#
# Sends a command, and waits for the response.
#
#******************************************************************************
def send_command(params, response_len, ser):
# 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:
print('No response for command 0x{:08X}'.format(word_from_bytes(params, 0) & 0xFFFF))
n = len(response)
if (n != 0):
print("received bytes ", len(response))
print([hex(n) for n in response])
raise NoResponseError
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('No response for command 0x{:08X}'.format(command))
raise NoResponseError
return response
#******************************************************************************
#
# Errors
#
#******************************************************************************
class BootError(Exception):
pass
class NoAckError(BootError):
pass
class NoResponseError(BootError):
pass
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(description =
'UART Wired Update Host for Apollo3')
parser.add_argument('port', help = 'Serial COMx Port')
parser.add_argument('-b', dest='baud', default=115200, type=int,
help = 'Baud Rate (default is 115200)')
parser.add_argument('--raw', dest='raw', default='',
help = 'Binary file for raw message')
parser.add_argument('-f', dest='binfile', default='',
help = '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 = '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('-o', dest = 'otadesc', type=auto_int, default=0xFE000,
help = '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('-r', dest = 'reset', default=1, type=auto_int, choices = [0,1,2],
help = 'Should it send reset command after image download? (0 = no reset, 1 = POI, 2 = POR) (default is 1)')
parser.add_argument('-a', dest = 'abort', default=-1, type=int, choices = [0,1,-1],
help = 'Should it send abort command? (0 = abort, 1 = abort and quit, -1 = no abort) (default is -1)')
parser.add_argument('--split', dest='split', type=auto_int, default=hex(MAX_DOWNLOAD_SIZE),
help='Specify the max block size if the image will be downloaded in pieces')
args = parser.parse_args()
main()
@@ -0,0 +1,17 @@
@echo off
if [%1]==[] goto usage
jlink -device %1 -CommanderScript jlink-read-info0.txt
diff info0.bin info0_dump.bin
@ECHO OFF
if ERRORLEVEL 1 (
echo "INFO0 Invalid"
) else (
echo "INFO0 Valid"
)
pause
goto :eof
:usage
@echo Usage: %0 ^<Chip Part#^>
exit /B 1
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
#******************************************************************************
#
# This scripts creates a single Blob, which can be programmed at OTA_POINTER
# The first 4 bytes contain address to OTA Descriptor, which immediately follows
#
#******************************************************************************
import argparse
import sys
magicnum = 0xDEADCAFE
#******************************************************************************
#
# Main function
#
#******************************************************************************
def main():
# Read the binary file from the command line.
with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
if len(args.trailerfile) > 0:
with open(args.trailerfile, mode='rb') as binfile:
trailer= binfile.read()
else:
trailer = []
applen = len(application)
crc = crc32(application)
ota_desc = []
# OTA Descriptor immediately follows the OTA_POINTER
ota_desc.extend(int_to_bytes(magicnum))
ota_desc.extend(int_to_bytes(int(args.linkaddr, 0)))
ota_desc.extend(int_to_bytes(applen))
ota_desc.extend(int_to_bytes(crc))
trailerlen = len(trailer)
ota_desc.extend(int_to_bytes(trailerlen))
ota_desc.extend(int_to_bytes(int(args.options, 0)))
ota_desc.extend(int_to_bytes(int(args.flashaddr , 0)+ 40)) # pui32SecInfoPtr
if (args.otaimageaddr != 0):
# Image has been flashed seprately. Just code the address in descriptor
ota_desc.extend(int_to_bytes(int(args.otaimageaddr, 0))) # pui32ImageAddr
else:
# Image is written immediately following the OTA descriptor
# Need to determine if there is a padding needed in between to ensure proper alignment
image_addr = int(args.flashaddr , 0) + 40 + trailerlen
if (image_addr % int(args.alignment) != 0):
pad_size = int(args.alignment) - (image_addr % int(args.alignment))
else:
pad_size = 0
image_addr = image_addr + pad_size
ota_desc.extend(int_to_bytes(image_addr)) # pui32ImageAddr
ota_desc_crc = crc32(ota_desc)
# OTA_POINTER contents
new_image = []
new_image.extend(int_to_bytes(int(args.flashaddr , 0)+ 4)) # OTA Descriptor address
new_image.extend(ota_desc) # OTA Descriptor
new_image.extend(int_to_bytes(ota_desc_crc))
if trailerlen > 0:
print('Adding Security Trailer')
new_image.extend(trailer)
if (pad_size != 0):
print('Adding padding of {} bytes...'.format(pad_size), flush=True)
pad_binarray = bytearray([0]*pad_size);
new_image.extend(pad_binarray)
if (args.otaimageaddr == 0):
# Image
new_image.extend(application)
print('Saving OTA descriptor image {} bytes to {}...'.format(len(new_image), args.outfile), flush=True)
with open(args.outfile, mode='wb') as imagefile:
imagebytearray = bytearray(new_image)
imagefile.write(imagebytearray)
print('Done.')
#******************************************************************************
#
# 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))
#******************************************************************************
#
# CRC function that matches the CRC used by the Apollo bootloader.
#
#******************************************************************************
poly32 = 0x1EDC6F41
def crc32(L):
rem = 0
for b in L:
rem = rem ^ (b << 24)
for i in range(8):
if rem & 0x80000000:
rem = ((rem << 1) ^ poly32)
else:
rem = (rem << 1)
rem = rem & 0xFFFFFFFF
return rem
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(description =
'Utility to generate OTA blob for multi boot test app')
parser.add_argument('binfile',
help = 'Binary file to program into the target device')
parser.add_argument('flashaddr', help = 'Flash address (hex) where blob will be flashed to')
parser.add_argument('linkaddr', help = 'Link address (hex) of the program file')
parser.add_argument('-a', dest = 'alignment', default=4,
help = 'Desired alignment for application in image')
parser.add_argument('options',
help = 'OTA options parameter')
parser.add_argument('-s', dest = 'trailerfile', default='',
help = 'Binary file for (optional) security trailer')
parser.add_argument('-i', dest='otaimageaddr', default=0,
help = '(optional) Flash address (hex) where the OTA image is flashed to - if not specified, the image is included in outfile immediately following the descriptor')
parser.add_argument('-o', dest = 'outfile',
help = 'output filename for the ota descriptor')
args = parser.parse_args()
main()
@@ -0,0 +1,263 @@
#!/usr/bin/env python3
import argparse
import serial
import sys
#******************************************************************************
#
# Main function
#
#******************************************************************************
def main():
# Open a serial port, and load the image at the specified address.
#
# We will use a UART timeout value of 12 second. This should be long
# enough that the slave will respond to us, but short enough that a human
# operator should see errors quickly..
# 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.
# Read the binary file from the command line.
with open(args.binfile, mode='rb') as binfile:
application = binfile.read()
if len(args.trailerfile) > 0:
with open(args.trailerfile, mode='rb') as binfile:
trailer= binfile.read()
else:
trailer = []
print('Loading {} bytes over serial port {}...'.format(len(application), args.port), flush=True)
with serial.Serial(args.port, args.baud, timeout=12) as ser:
load_image(application, trailer, int(args.address, 0), ser)
print('Done.')
#******************************************************************************
#
# Load image
#
# Given a serial port and a binary file, loads the target device using the
# UART.
#
#******************************************************************************
def load_image(application, trailer, address, ser):
# Gather the important binary metadata.
applen = len(application)
crc = crc32(application)
new_image = []
new_image.extend(int_to_bytes(address))
new_image.extend(int_to_bytes(applen))
new_image.extend(int_to_bytes(crc))
trailerlen = len(trailer)
if trailerlen > 0:
print('Adding Security Trailer')
new_image.extend(int_to_bytes(trailerlen))
new_image.extend(trailer)
#print([hex(n) for n in new_image])
if (args.bauddetect):
# Making sure autobaud gets set.
ser.write([0x55])
response = ser.read(1)
# Make sure we got the number of bytes we asked for.
if len(response) == 0:
raise NoResponseError
# Send a New Image command.
response = send_bytewise_command(0x2, new_image, 4, ser)
if response[0] != 0x2:
raise NoAckError
# Set the override pin.
send_ackd_command(0x5, [args.ovr, args.level], ser)
# Loop over the bytes in the image, and send them to the target.
resp = 0
for x in range(0, applen, 512):
# Split the application into chunks of 512 bytes.
# This is the max chunk size supported by the UART bootloader
chunk = application[x:x+512]
# Build a data packet with a "data command" a "length" and the actual
# payload bytes, and send it to the target.
resp = send_bytewise_command(0x3, int_to_bytes(len(chunk)) + list(chunk), 4, ser)
# Check the CRC.
if word_from_bytes(resp, 0) == 0x3:
# If the CRC was good, optionally reset the target and let it run.
if (args.reset):
send_command(0x4, [], 0, ser)
else:
print('CRC was bad')
#******************************************************************************
#
# Send ACK'd command
#
# Sends a command, and waits for an ACK.
#
#******************************************************************************
def send_ackd_command(command, params, ser):
response = send_command(command, params, 4, ser)
if response[0] != 0x2:
raise NoAckError
#******************************************************************************
#
# Send command
#
# Sends a command, and waits for the response.
#
#******************************************************************************
def send_command(command, params, response_len, ser):
# Send the command first.
ser.write(int_to_bytes(command))
# Next, send the parameters.
for param in params:
ser.write(int_to_bytes(param))
response = ''
response = ser.read(response_len)
# Make sure we got the number of bytes we asked for.
if len(response) != response_len:
print('No response for command 0x{:08X}'.format(command))
raise NoResponseError
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('No response for command 0x{:08X}'.format(command))
raise NoResponseError
return response
#******************************************************************************
#
# 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))
#******************************************************************************
#
# CRC function that matches the CRC used by the Apollo bootloader.
#
#******************************************************************************
poly32 = 0x1EDC6F41
def crc32(L):
rem = 0
for b in L:
rem = rem ^ (b << 24)
for i in range(8):
if rem & 0x80000000:
rem = ((rem << 1) ^ poly32)
else:
rem = (rem << 1)
rem = rem & 0xFFFFFFFF
return rem
#******************************************************************************
#
# Errors
#
#******************************************************************************
class BootError(Exception):
pass
class NoAckError(BootError):
pass
class NoResponseError(BootError):
pass
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(description =
'UART Boot Host for Apollo or Apollo2')
parser.add_argument('binfile',
help = 'Binary file to program into the target device')
parser.add_argument('address', help = 'Link address (hex)')
parser.add_argument('port', help = 'Serial COMx Port')
parser.add_argument('-b', dest='baud', default=115200, type=int,
help = 'Baud Rate (default is 115200)')
parser.add_argument('-o', dest ='ovr', default = 18, type=int,
help = 'Override pin (default is 18)')
parser.add_argument('-l', dest='level', default=0, type=int,
help = 'Override pin polarity (0 or 1) Default is active low')
parser.add_argument('-s', dest = 'trailerfile', default='',
help = 'Binary file for (optional) security trailer')
parser.add_argument('-r', dest = 'reset', default=1, type=int,
help = 'Should it send reset command after image download? (0/1) (default is 1)')
parser.add_argument('-a', dest = 'bauddetect', default=1, type=int,
help = 'Should it send a preamble for slave to detect the baudrate? (0/1) (default is 1)')
args = parser.parse_args()
main()
@@ -0,0 +1,670 @@
#!/usr/bin/env python3
# *****************************************************************************
#
# pinconfig.py
#
# @brief Script for generating a BSP pin file.
# *****************************************************************************
# *****************************************************************************
#
# Copyright (c) 2020, Ambiq Micro
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
#
# Third party software included in this distribution is subject to the
# additional license terms as defined in the /docs/licenses directory.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# This is part of revision 2.4.2 of the AmbiqSuite Development Package.
#
# *****************************************************************************
# *****************************************************************************
# Imported modules
# *****************************************************************************
import argparse
import textwrap
import os.path
import rsonlite
# *****************************************************************************
# Templates
# *****************************************************************************
filetemplateC = '''
//*****************************************************************************
//
// {filename}
//! @file
//!
//! @brief BSP pin configuration definitions.
//!
//! @addtogroup BSP Board Support Package (BSP)
//! @ingroup BSP
//! @{{
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#include "am_bsp.h"
{pin_defs}
//*****************************************************************************
//
// End Doxygen group.
//! @}}
//
//*****************************************************************************
'''.strip()
filetemplateH = '''
//*****************************************************************************
//
// {filename}
//! @file
//!
//! @brief BSP pin configuration definitions.
//!
//! @addtogroup BSP Board Support Package (BSP)
//! @addtogroup apollo3_bsp BSP for the Apollo3 EVB.
//! @ingroup BSP
//! @{{
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) 2020, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision 2.4.2 of the AmbiqSuite Development Package.
//
//*****************************************************************************
#ifndef {headerdef}
#define {headerdef}
#include <stdint.h>
#include <stdbool.h>
#include "am_mcu_apollo.h"
#ifdef __cplusplus
extern "C"
{{
#endif
{pin_defs}
#ifdef __cplusplus
}}
#endif
#endif // {headerdef}
//*****************************************************************************
//
// End Doxygen group.
//! @}}
//
//*****************************************************************************
'''.strip()
sectiontemplate = '''
//*****************************************************************************
//
// {pin_descr}
//
//*****************************************************************************
'''.strip()
# *****************************************************************************
#
# Globals
#
# *****************************************************************************
# *****************************************************************************
#
# Command line support.
#
# *****************************************************************************
def read_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('input', help='input src file name')
parser.add_argument('CorH', help='C to create C file, H to create H file',
choices=['C','H','c','h'])
return parser.parse_args()
# *****************************************************************************
# parse_input()
# *****************************************************************************
def parse_input(filename):
'''
Simple wrapper to pull an rsonlite list from given file.
'''
with open(filename) as f:
data = f.read()
return rsonlite.loads(data)
# *****************************************************************************
# list_to_dict()
# *****************************************************************************
def list_to_dict(L):
'''
rsonlite loads data from the rson files as a very-nested list. This
function converts that list to a set of nested dictionaries. At each level,
the dictionary keys correspond to the "headings", and the dictionary values
are a list of the items under that "heading". Values assigned by
equals-sign statements are (unfortunately) also converted to (key, list)
pairs, the same way that subheadings are.
'''
if len(L) == 1:
return L[0]
else:
D = dict()
for x in L:
if x[0] in D:
D[x[0]] = D[x[0]] + [list_to_dict(x[1])]
else:
D[x[0]] = [list_to_dict(x[1])]
return D
# *****************************************************************************
# get_val()
# *****************************************************************************
def get_val(name, D):
'''
get_val is just a helper to make it more obvious when you are trying to get
at an equals-sign-assigned value from the rson file dictionary.
'''
if name not in D:
return "ERROR: {} VALUE MISSING".format(name)
else:
return D[name][0]
# *****************************************************************************
# get_version()
# *****************************************************************************
def get_version(filename):
'''
Given the filename of an 'src' file, this function will return a 'pin'
object corresponding to the fields described in the src file.
'''
# Read in the contents of the src file, and use the rsonlite library to
# parse them into a list.
rson_data = parse_input(filename)
# Convert the list to a dictionary.
rson_dict = list_to_dict(rson_data)
if 'pinsrc_ver' in rson_dict:
# Convert rson list object to int
ssrcver = rson_dict["pinsrc_ver"][0]
if ssrcver[0:2].lower() == "0x":
source_version = int(ssrcver, 16)
else:
source_version = int(ssrcver, 10)
else:
# If no src file version given, assume version for Apollo3
source_version = 0x0003
return source_version
# *****************************************************************************
# get_pinobj()
# *****************************************************************************
def get_pinobj(filename):
'''
Given the filename of an 'src' file, this function will return a 'pin'
object corresponding to the fields described in the src file.
'''
# Set the location for the supplementary files (infoblock, base addresses, etc.)
srcdir = os.path.dirname(filename)
# Read in the contents of the src file, and use the rsonlite library to
# parse them into a list.
rson_data = parse_input(filename)
# Convert the list to a dictionary.
rson_dict = list_to_dict(rson_data)
# Convert the dictionary to a block object, which will create field
# objects as necessary. Return this object to the caller.
return pinobj(rson_dict)
# *****************************************************************************
# pinfields
# *****************************************************************************
class pinfields:
def __init__(self, pindict):
self.name = ''
self.desc = ''
self.pinnum = 0
self.powersw = ''
self.pullup = ''
self.func_sel = ''
self.drvstrength = 0
self.intdir = 0
self.GPOutcfg = ''
self.GPinput = ''
self.GPRdZero = ''
self.IOMnum = ''
self.CEnum = ''
self.CEpol = ''
self.bIomMSPIn = ''
intnotgiven = 65535
strnotgiven = 'none_given'
if 'name' in pindict:
self.name = get_val('name', pindict)
else:
self.name = strnotgiven
if 'desc' in pindict:
self.desc = get_val('desc', pindict)
else:
self.desc = strnotgiven
if 'pinnum' in pindict:
self.pinnum = int(get_val('pinnum', pindict))
else:
self.pinnum = intnotgiven
if 'powersw' in pindict:
self.powersw = get_val('powersw', pindict)
else:
self.powersw = strnotgiven
if 'pullup' in pindict:
self.pullup = get_val('pullup', pindict)
else:
self.pullup = strnotgiven
if 'func_sel' in pindict:
self.func_sel = get_val('func_sel', pindict)
else:
self.func_sel = strnotgiven
if 'drvstrength' in pindict:
self.drvstrength = int(get_val('drvstrength', pindict))
else:
self.drvstrength = intnotgiven
if 'intdir' in pindict:
self.intdir = get_val('intdir', pindict)
else:
self.intdir = strnotgiven
if 'GPOutcfg' in pindict:
self.GPOutcfg = get_val('GPOutcfg', pindict)
else:
self.GPOutcfg = strnotgiven
if 'GPinput' in pindict:
self.GPinput = get_val('GPinput', pindict)
else:
self.GPinput = strnotgiven
if 'GPRdZero' in pindict:
self.GPRdZero = get_val('GPRdZero', pindict)
else:
self.GPRdZero = strnotgiven
if 'IOMnum' in pindict:
self.IOMnum = get_val('IOMnum', pindict)
elif 'MSPInum' in pindict:
self.IOMnum = get_val('MSPInum', pindict)
else:
self.IOMnum = strnotgiven
if 'CEnum' in pindict:
self.CEnum = get_val('CEnum', pindict)
else:
self.CEnum = strnotgiven
if 'CEpol' in pindict:
self.CEpol = get_val('CEpol', pindict)
else:
self.CEpol = strnotgiven
if 'bIomMSPIn' in pindict:
self.bIomMSPIn = get_val('bIomMSPIn', pindict)
else:
self.bIomMSPIn = strnotgiven
#
# Check for an invalid field in the src file. If so, report it.
#
for fld in pindict:
if fld != 'name' and \
fld != 'desc' and \
fld != 'pinnum' and \
fld != 'powersw' and \
fld != 'pullup' and \
fld != 'func_sel' and \
fld != 'drvstrength' and \
fld != 'intdir' and \
fld != 'GPOutcfg' and \
fld != 'GPinput' and \
fld != 'GPRdZero' and \
fld != 'IOMnum' and \
fld != 'MSPInum' and \
fld != 'CEnum' and \
fld != 'CEpol' and \
fld != 'bIomMSPIn':
print("Invalid field: '%s.%s'" % (self.name, fld))
# *****************************************************************************
# pinobj
# *****************************************************************************
class pinobj:
def __init__(self, pindict):
self.name = 'name'
self.srcver=0x0000
self.pins = []
if 'pinsrc_ver' in pindict:
# Convert rson list object to int
ssrcver = pindict["pinsrc_ver"][0]
if ssrcver[0:2].lower() == "0x":
self.srcver = int(ssrcver,16)
else:
self.srcver = int(ssrcver,10)
else:
# If no src file version given, assume version for Apollo3
self.srcver = 0x0003
# Run through all the pins given in the src file
if 'pin' in pindict:
#
# Run through the 'pin' list.
#
for pin in pindict['pin']:
self.pins.append(pinfields(pin))
# *****************************************************************************
# write_Cfiles()
# *****************************************************************************
def write_Cfiles(pinobj, bCreateC):
#
# Initializations
#
intnotgiven = 65535
strnotgiven = 'none_given'
strCfile = ''
strHfile = ''
for pin in pinobj.pins:
sectiondesc = pin.name.strip() + ' pin'
if pin.desc == strnotgiven:
sectiondesc += '.'
else:
sectiondesc += ': ' + pin.desc.strip()
if sectiondesc[-1] != '.':
sectiondesc += '.'
strCfile += '\n'
strCfile += sectiontemplate.format(pin_descr=sectiondesc)
strCfile += '\n'
strHfile += '\n'
strHfile += sectiontemplate.format(pin_descr=sectiondesc)
strHfile += '\n'
if pin.pinnum != intnotgiven:
strHfile += '#define AM_BSP_GPIO_%-20s' % pin.name + '%d\n' % pin.pinnum
strHfile += 'extern const am_hal_gpio_pincfg_t g_AM_BSP_GPIO_%s;\n' % pin.name
#strHfile += '\n'
strCfile += 'const am_hal_gpio_pincfg_t g_AM_BSP_GPIO_%s =\n' % pin.name
strCfile += '{\n'
strCfile += '%-25s' % ' .uFuncSel' + '= %s,\n' % pin.func_sel
if pin.powersw != strnotgiven:
if (pin.powersw.lower() == "vdd") or \
(pin.powersw.lower() == "vss") or \
(pin.powersw.lower() == "none"):
strCfile += '%-25s' % ' .ePowerSw' + '= AM_HAL_GPIO_PIN_POWERSW_%s,\n' % pin.powersw.upper()
else:
strCfile += '%-25s' % ' .ePowerSw' + '= %s,\n' % pin.powersw
if pin.pullup != strnotgiven:
if (pin.pullup.lower() == "none") or \
(pin.pullup.lower() == "weak") or \
(pin.pullup.lower() == "pulldown") or \
(pin.pullup.lower() == "1_5k") or \
(pin.pullup.lower() == "6k") or \
(pin.pullup.lower() == "12k") or \
(pin.pullup.lower() == "24k"):
strCfile += '%-25s' % ' .ePullup' + '= AM_HAL_GPIO_PIN_PULLUP_%s,\n' % pin.pullup.upper()
elif (pin.pullup == "1_5") or \
(pin.pullup == "6") or \
(pin.pullup == "12") or \
(pin.pullup == "24"):
strCfile += '%-25s' % ' .ePullup' + '= AM_HAL_GPIO_PIN_PULLUP_%sK,\n' % pin.pullup
else:
strCfile += '%-25s' % ' .ePullup' + '= %s,\n' % pin.pullup
if pin.drvstrength != intnotgiven:
if (pin.drvstrength != 2) and (pin.drvstrength != 4) and \
(pin.drvstrength != 8) and (pin.drvstrength != 12):
pin.drvstrength = 2
strCfile += '%-25s' % ' .eDriveStrength' + '= AM_HAL_GPIO_PIN_DRIVESTRENGTH_%dMA,\n' % pin.drvstrength
if pin.GPOutcfg != strnotgiven:
if (pin.GPOutcfg.lower() == "disable") or \
(pin.GPOutcfg.lower() == "pushpull") or \
(pin.GPOutcfg.lower() == "opendrain") or \
(pin.GPOutcfg.lower() == "tristate"):
strCfile += '%-25s' % ' .eGPOutcfg' + '= AM_HAL_GPIO_PIN_OUTCFG_%s,\n' % pin.GPOutcfg.upper()
else:
strCfile += '%-25s' % ' .eGPOutcfg' + '= %s,\n' % pin.GPOutcfg
if pin.GPinput != strnotgiven:
if (pin.GPinput.lower() == "true"):
strCfile += '%-25s' % ' .eGPInput' + '= AM_HAL_GPIO_PIN_INPUT_ENABLE,\n'
elif (pin.GPinput.lower() == "false"):
strCfile += '%-25s' % ' .eGPInput' + '= AM_HAL_GPIO_PIN_INPUT_NONE,\n'
else:
strCfile += '%-25s' % ' .eGPInput' + '= %s,\n' % pin.GPinput
if pin.GPRdZero != strnotgiven:
if (pin.GPRdZero.lower() == "true") or \
(pin.GPRdZero.lower() == "zero"):
strCfile += '%-25s' % ' .eGPRdZero' + '= AM_HAL_GPIO_PIN_RDZERO_ZERO,\n'
elif (pin.GPRdZero.lower() == "false") or \
(pin.GPRdZero.lower() == "readpin"):
strCfile += '%-25s' % ' .eGPRdZero' + '= AM_HAL_GPIO_PIN_RDZERO_READPIN,\n'
else:
strCfile += '%-25s' % ' .eGPRdZero' + '= %s,\n' % pin.GPRdZero
if pin.intdir != strnotgiven:
if (pin.intdir.lower() == "none") or \
(pin.intdir.lower() == "lo2hi") or \
(pin.intdir.lower() == "hi2lo") or \
(pin.intdir.lower() == "either"):
strCfile += '%-25s' % ' .eIntDir' + '= AM_HAL_GPIO_PIN_INTDIR_%s,\n' % pin.intdir.upper()
else:
strCfile += '%-25s' % ' .eIntDir' + '= %s,\n' % pin.intdir
if (pin.bIomMSPIn != strnotgiven):
if pinobj.srcver >= 0x0003:
if ((pin.bIomMSPIn[0:1].lower() != "m") and (pin.bIomMSPIn != "0")):
bIom = 1
else:
bIom = 0
strCfile += '%-25s' % ' .bIomMSPIn' + '= %d,\n' % bIom
if pin.IOMnum != strnotgiven:
strCfile += '%-25s' % ' .uIOMnum' + '= %s,\n' % str(pin.IOMnum)
if pin.CEnum != strnotgiven:
strCfile += '%-25s' % ' .uNCE' + '= %s,\n' % str(pin.CEnum)
# Create the define
strtmp = '#define AM_BSP_%s_CHNL' % (pin.name)
strHfile += '%-40s' % strtmp + '%s\n' % str((pin.CEnum))
if pin.CEpol != strnotgiven:
if (pin.CEpol.lower() == "low") or \
(pin.CEpol.lower() == "high"):
strCfile += '%-25s' % ' .eCEpol' + '= AM_HAL_GPIO_PIN_CEPOL_ACTIVE%s,\n' % pin.CEpol.upper()
elif (pin.CEpol.lower() == "activelow") or \
(pin.CEpol.lower() == "activehigh"):
strCfile += '%-25s' % ' .eCEpol' + '= AM_HAL_GPIO_PIN_CEPOL_%s,\n' % pin.CEpol.upper()
else:
strCfile += '%-25s' % ' .eCEpol' + '= %s,\n' % pin.CEpol
# Eliminate the last comma from the last structure member
strCfile = strCfile[:-2]
# Terminate the structure
strCfile += '\n};\n'
if bCreateC:
#
# Develop and print the C file
#
S = filetemplateC.format(filename='am_bsp_pins.c', pin_defs=strCfile);
print(S)
else:
#
# Develop and print the H file
#
S = filetemplateH.format(filename='am_bsp_pins.h',
pin_defs=strHfile,
headerdef='AM_BSP_PINS_H')
print(S)
# *****************************************************************************
#
# main
#
# *****************************************************************************
if __name__ == '__main__':
#
# Get arguments.
# First arg is a string for the input .src file.
# Second arg must be a C or H.
#
args = read_arguments()
if args.CorH.upper() == 'C':
bCreateC = True
else:
bCreateC = False
version = get_version(args.input)
# Redirect the script based on the version number.
if version & 0xFF == 0x03:
pinobj = get_pinobj(args.input)
#
# pinobj.pins is a list of the pins
#
write_Cfiles(pinobj, bCreateC)
@@ -0,0 +1,294 @@
'''
rsonlite -- an extremely lightweight version of rson.
Copyright (c) 2012, Patrick Maupin
License :: MIT
http://pypi.python.org/pypi/rsonlite
http://code.google.com/p/rson/
rsonlite makes it easy to build a file parser for
declarative hierarchical data structures using indentation.
(Spaces only, tabs not considered indentation.)
The only special characters are '#', '=', and indentation:
- Indentation denotes a key/value relationship. The
value is indented from the key.
- = Denotes the start of a free-format string. These
strings can contain '=' and '#' characters, and
even be multi-line, but every line in the string
must be indented past the initial equal sign.
Note that, for multi-line strings, indentation is
preserved but normalized such that at least one
line starts in the left column. This allows for
restructuredText or Python code to exist inside
multi-line strings.
- # Denotes the start of a line comment, when not
inside a free-format string.
The only Python objects resulting from parsing a file
with rsonlite are:
- strings:
free-format strings (described above) can
contain any character, but the whitespace
before/after the string may be stripped.
Regular strings must fit on a single line and
cannot contain '=' or '#' characters.
Regular strings may be used as keys in key/value
pairs, but free-format strings may not.
- tuple:
A key/value pair is a two-element tuple. The key is always
a string. The value is always a list.
- list:
The top level is a list, and the value element of every
key/value pair tuple is also a list. Lists can contain
strings and key/value pair tuples.
'''
import re
version = __version__ = '0.1.0'
# Our attempt at rationalizing differences between Python 2 and Python 3.
try:
basestring
except NameError:
basestring = str
class unicode: pass
# Use OrderedDict if it's available
try:
from collections import OrderedDict as stddict
except ImportError:
stddict = dict
# Splits the entire file into probable tokens.
splitter = re.compile('(\n *|=[^\n]*|#[^\n]*|[^\n#=]+)').findall
class RsonToken(str):
''' A string that may be annotated with location information
'''
def __new__(cls, s, line, col):
self = str.__new__(cls, s)
self.line = line
self.col = col
return self
def __add__(self, other):
return RsonToken(str(self) + other, self.line, self.col)
def gettoks(source):
''' Convert string into (probable) tokens
(some tokens may be recombined later, e.g. if they
contain # or = but were already inside a string)
'''
# Use "regular" strings, whatever that means for the given Python
if isinstance(source, unicode):
source = source.encode('utf-8', 'replace')
elif not isinstance(source, basestring):
source = source.decode('utf-8', 'replace')
# Convert MS-DOS or Mac line endings to the one true way, and
# prefix the source with a linefeed to simplify the tokenization.
source = '\n' + source.replace('\r\n', '\n').replace('\r', '\n')
line = 0
for tok in splitter(source):
if tok.startswith('\n'):
line += 1
col = len(tok)
else:
yield RsonToken(tok, line, col)
col += len(tok)
def multiline(lineinfo, dedent):
''' Returns one string for each line,
properly dedented.
'''
linenum = lineinfo[0].line
for tok in lineinfo:
while linenum < tok.line:
yield ''
linenum += 1
yield (tok.col - dedent) * ' ' + tok.rstrip()
linenum += 1
def getfreeformat(toklist, firsttok, firstcol):
''' Returns a free-formatted string.
'''
curline = firsttok.line
firstpart = firsttok[1:].strip() # Get past = sign
lineinfo = []
while toklist and toklist[-1].col > firstcol:
tok = toklist.pop()
if tok.line == curline:
lineinfo[-1] += tok
else:
lineinfo.append(tok)
curline = tok.line
if lineinfo:
dedent = min(tok.col for tok in lineinfo)
if firstpart:
lineinfo.insert(0, RsonToken(firstpart, firsttok.line, dedent))
firstpart = '\n'.join(multiline(lineinfo, dedent))
return RsonToken(firstpart, firsttok.line, firsttok.col)
def loads(source):
''' load a string into an rsonlite datastructure.
If the source is not a string instance, then
loads will attempt to convert it into a string
instance, by encoding to UTF-8 on Python 2,
or decoding from UTF-8 on Python 3.
'''
toklist = list(gettoks(source))
toklist.reverse()
result = [None]
stack = []
curcol = -1
curlist = result
while toklist:
tok = toklist.pop()
if tok.startswith('#'):
continue
col = tok.col
if col > curcol:
stack.append((curcol, curlist))
oldlist = curlist
curcol, curlist = col, []
oldlist[-1] = oldlist[-1], curlist
while col < curcol:
curcol, curlist = stack.pop()
if col != curcol:
err = IndentationError('unindent does not match any outer indentation level')
err.filename = '<rsonlite>'
err.lineno = tok.line
raise err
if tok.startswith('='):
curlist.append(getfreeformat(toklist, tok, col))
else:
curlist.append(RsonToken(tok.rstrip(), tok.line, tok.col))
if toklist and toklist[-1].line == tok.line:
tok = toklist.pop()
if tok.startswith('='):
curlist[-1] = curlist[-1], [getfreeformat(toklist, tok, col)]
else:
assert tok.startswith('#') # else problem in regex...
result, = result
return [] if result is None else result[1]
def dumps(data, indent=' ', initial_indent=''):
''' Dump a string loaded with loads back out.
'''
def getstring(data, indent2):
if '\n' in data:
data = ('\n'+indent2).join([''] + data.split('\n'))
return data
def recurse(data, indent2):
assert isinstance(data, list), repr(data)
for data in data:
if isinstance(data, tuple):
key, value = data
if len(value) == 1 and isinstance(value[0], basestring):
append('%s%s = %s' % (indent2, key, getstring(value[0], indent2+indent)))
else:
append('%s%s' % (indent2, key))
recurse(value, indent2 + indent)
else:
assert isinstance(data, basestring)
if '\n' in data or '=' in data or '#' in data:
append(indent2 + '=')
append(getstring(data, indent2 + ' '))
else:
append('%s%s' % (indent2, data))
result = []
append = result.append
recurse(data, initial_indent)
append('')
return '\n'.join(result)
def pretty(data, indent=' '):
''' Pretty-print a string loaded by loads into
something that makes it easy to see the actual
structure of the data. The return value of
this should be parseable by eval()
'''
def recurse(data, indent2):
assert isinstance(data, list)
for data in data:
assert isinstance(data, (tuple, basestring))
if isinstance(data, tuple) and (
len(data[1]) != 1 or not isinstance(data[1][0], basestring)):
append('%s(%s, [' % (indent2, repr(data[0])))
recurse(data[1], indent2 + indent)
append('%s])' % (indent2))
else:
append('%s%s,' % (indent2, repr(data)))
result = []
append = result.append
append('[')
recurse(data, indent)
append(']')
append('')
return '\n'.join(result)
##########################################################################
# These higher-level functions might suffice for simple data, and also
# provide a template for designing similar functions.
def stringparse(s, special=dict(true=True, false=False, null=None)):
''' This gives an example of handling the JSON special identifiers
true, false and null, and also of handling simple arrays.
'''
if s in special:
return special[s]
if s.startswith('[') and s.endswith(']'):
t = s[1:-1]
for ch in '"\'[]{}\n':
if ch in t:
return s
return [x.strip() for x in t.split(',')]
return s
def simpleparse(source, stringparse=stringparse, stddict=stddict):
''' Return the simplest structure that uses dicts instead
of tuples, and doesn't lose any source information.
Use ordered dicts if they are available.
'''
def recurse(mylist):
if len(mylist) == 1 and isinstance(mylist[0], basestring):
return stringparse(mylist[0])
keys = [x[0] for x in mylist if isinstance(x, tuple)]
if not keys:
return mylist # simple list
if len(set(keys)) == len(mylist):
return stddict((x, recurse(y)) for (x, y) in mylist)
# Complicated. Make a list that might have multiple dicts
result = []
curdict = None
for item in mylist:
if not isinstance(item, tuple):
result.append(stringparse(item))
curdict = None
continue
key, value = item
if curdict is None or key in curdict:
curdict = stddict()
result.append(curdict)
curdict[key] = recurse(value)
return result
return recurse(source if isinstance(source, list) else loads(source))
@@ -0,0 +1,35 @@
0000000_META_hci_patches_v8.emp (can be programmed into IRAM or OTP)
Container ID 77
Build number 3089
User Build 0
eight_connections.emp
Format Version 1
Container ID 20
Build number 3089
User Build 2
data_length_extension.emp
Format Version 1
Container ID 21
Build number 3089
User Build 2
sleep_clk_src_apollo.emp
Format Version 1
Container ID 22
Build number 3089
User Build 2
disable_pull-up_em_gpio10.emp
Format Version 1
Container ID 23
Build number 3089
User Build 2
disable_pull-up_em_gpio11.emp
Format Version 1
Container ID 24
Build number 3089
User Build 2
@@ -0,0 +1,34 @@
This directory holds the *.emp files generated by the EM Micro tools (e.g. EM9304 Configuration Editor) or EM Micro developers (code patches).
All *.emp files contained in this directory will result in the generation of a patch or patches to be
applied to the EM9304.
The script emp2include.py has the following usage:
$ ./emp2include.py --help
usage: emp2include.py [-h] [-o MEMORY]
Convert multiple EM9304 *.emp files to the em9304_patches.* files.
optional arguments:
-h, --help show this help message and exit
-o MEMORY Destination memory (IRAM or OTP)
Patches can be applied temporarily to IRAM and then permanently applied to OTP (one-time programable) memory
as needed. The default is IRAM.
The files em9304_patches.h and em9304_patches.c will be generated in the following directory:
/third_party/exactle/sw/hci/ambiq/em9304
Patches are uniquely identified by the following meta-data embedded in *.emp files:
* Format Version - Container format version (increments as container is updated)
* Patch ID - Unique identifier for the container/patch (assigned by patch creator)
* Build number - EM9304 ROM minor revision supported by patch
* User Build - User defined number (assigned by patch creator)
=> 0, 1: Reserved by EM
=> 2, 3: Reserved by Ambiq Micro
=> 4+ : Available for customer
Note: 0001642_PROD_boot_overhead.emp is built into 0000000_META_hci_patches_v7.emp, there's only a single emp from EM in the future.
@@ -0,0 +1,455 @@
#!/usr/bin/env python3
import argparse
import configparser
import os
import sys
import platform
#******************************************************************************
#
# Template for the output files
#
#******************************************************************************
hfile1 = '''
//*****************************************************************************
//
//! @file {name}.h
//!
//! @brief This is a generated file.
//
//*****************************************************************************
#include <stdint.h>
#include <stdbool.h>
#ifndef {macroname}_H
#define {macroname}_H
//*****************************************************************************
//
// Length of the binary array in bytes.
//
//*****************************************************************************
#define {lengthmacro:35} {length}
// EM patch destination memory:
// 1 means EM patch will be programmed into OTP if emp file
// was not programmed before.
// 0 means EM patch will be programmed into IRAM each time when
// EM9304 is cold boot.
#define DEST_MEMORY_IRAM 0
#define DEST_MEMORY_OTP 1
#define {patch_dest_memory_macro:35} {dest_memory}
//*****************************************************************************
//
// EM9304 Container Info type
//
//*****************************************************************************
typedef struct
{{
uint16_t buildNumber; // Firmware Build Number
uint16_t userBuildNumber; // User defined Build Number (determines patch precedence)
uint8_t containerVersion; // Container Version
uint8_t containerType; // Container Type
uint8_t containerID; // Container ID
bool applyPatch; // Flag to apply this patch.
uint8_t startingPatch; // Starting patch index.
uint8_t endingPatch; // Ending patch index + 1.
}} em9304_container_info_t;
//*****************************************************************************
//
// Extracted binary array.
//
//*****************************************************************************
'''.strip()
hfile2 = '''
extern em9304_container_info_t g_p{varname}[{length}];
extern const uint8_t g_p{varname}HCICmd[{numpatches}][68];
#endif // {macroname}_H
'''.strip()
hfile3 = '''
//
// Note: A NULL patch was generated. This means that there were no *.emp files
// processed during the generation of this file.
//
extern em9304_container_info_t g_p{varname}[1];
extern const uint8_t g_p{varname}HCICmd[1][68];
#endif // {macroname}_H
'''.strip()
cfile1 = '''
//*****************************************************************************
//
//! @file {name}.c
//!
//! @brief This is a generated file.
//
//*****************************************************************************
#include <stdint.h>
#include "{name}.h"
//*****************************************************************************
//
// Extracted binary array
//
//*****************************************************************************
'''.strip()
cfile2 = '''
em9304_container_info_t g_p{varname}[{length}] =
{{
// Build UserBuild Version Type ID bApply Start End
'''.strip()
cfile2_null = '''
//
// This is a NULL patch. It indicates that there were no *.emp files processed
// during the generation of this file.
//
em9304_container_info_t g_p{varname}[1] =
{{
{{ 0xFFFF, 0xFFFF, 0xFF, 0xFF, 0xFF, false, 0x00, 0x00 }}
'''.strip()
cfile3 = '''
}};
const uint8_t g_p{varname}HCICmd[{numpatches}][68] =
{{
'''.strip()
cfile3_null = '''
}};
const uint8_t g_pEm9304PatchesHCICmd[1][68] =
{{
{{ 0x00
}}
'''.strip()
#******************************************************************************
#
# CRC32 lookup table.
#
#******************************************************************************
crc32Table = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d ]
def crc32( previousCrc, L ):
crc = previousCrc
for b in L:
crc = crc32Table[( crc ^ b) & 0xFF ] ^ ( crc >> 8 )
return (~crc & 0xFFFFFFFF)
#******************************************************************************
#
# Read in the binary file and output a C array.
#
#******************************************************************************
def process(memory):
print('Find *.emp files')
# Change to the target directory with *.emp files.
asps = []
for subdir, dirs, files in os.walk('./'):
for file in files:
if file.endswith('.emp'):
if subdir.endswith('./'):
print(file)
asps.append(file)
output = 'em9304_patches'
print(output)
# S will be the string to accumulate the patch records in a C-friendly format
S = ''
# C will be the string to accumulate the patch HCI commands in a C-friendly format
C = ''
patchIndex = 0
patchCount = 0
# For each file in the list
for file in asps:
with open(file, mode = 'rb') as f:
binarray = f.read()
# Set the starting index
cmdIndex = 0
#print(len(binarray))
while ((cmdIndex < len(binarray)) & ((len(binarray) - cmdIndex) > 20)):
#print(cmdIndex)
# Check for the start of a new container.
if(binarray[cmdIndex:cmdIndex+4] == b'39me'):
#print("Found container header")
# Increment the patch count.
patchCount += 1
# Add a record start
S += ' { '
# Add the decimal representation of the patch Build Number Version
S += '{0:4d}'.format(binarray[cmdIndex+12] + (binarray[cmdIndex+13] << 8))
S += ', '
# Add the decimal representation of the patch User Build Number
S += ' {0:4d}'.format(binarray[cmdIndex+14] + (binarray[cmdIndex+15] << 8))
S += ', '
# Add the decimal representation of the patch Format Version
S += ' {0:2d}'.format(binarray[cmdIndex+8])
S += ', '
# Add the decimal representation of the patch Container Type
S += ' {0:2d}'.format(binarray[cmdIndex+9])
S += ', '
# Determine whether the patch should override user and must go into OTP.
if ( (binarray[cmdIndex+9] == 0x09) or (binarray[cmdIndex+9] == 0x0A) ):
bOTPOverride = True
else:
bOTPOverride = False
# Add the decimal representation of the patch Container ID
S += ' {0:2d}'.format(binarray[cmdIndex+10])
S += ', '
# Initialize the path application flag to false
S += 'false, '
# Set the starting patch index
S += ' {0:3d}, '.format(patchIndex)
cmdSize = binarray[cmdIndex+4] + (binarray[cmdIndex+5] << 8) + (binarray[cmdIndex+6] << 16) + (binarray[cmdIndex+7] << 24)
#print(cmdSize)
firstCmd = True
seqNum = 1
while (cmdSize > 0):
# Add a record start
C += ' { '
# First record is EM_WritePatchStart HCI command
if (firstCmd):
# Determine the binary patch data length
if (cmdSize <= 59):
cmdLength = cmdSize
else:
cmdLength = 59
# Add the HCI Command Header (Note: Set destination to IRAM1 until testing is complete)
C += '0x27, 0xFC,\t\t// HCI Vendor Specific Command EM_WritePatchStart\n '
C += '0x{:02X},\t\t\t// HCI Parameter Length\n '.format(cmdLength + 5)
if ( (memory == 'IRAM') and (not bOTPOverride) ):
C += '0x00,\t\t\t// Destination Memory (0 = IRAM1; 1 = OTP)\n '
elif (memory == 'OTP'):
C += '0x01,\t\t\t// Destination Memory (0 = IRAM1; 1 = OTP)\n '
else:
print('ERROR in destination memory indication')
return
# Create the binary data to run the CRC across
crcarray = binarray[cmdIndex:(cmdIndex+cmdLength)]
# Clear the firstCmd flag
firstCmd = False
#Remaining records are EM_WritePatchContinue commands
else:
# Determine the binary patch data length
if (cmdSize <= 58):
cmdLength = cmdSize
else:
cmdLength = 58
# Add the HCI Command Header (Note: Set destination to IRAM1 until testing is complete
C += '0x28, 0xFC,\t\t// HCI Vendor Specific Command EM_WritePatchContinue\n '
C += '0x{:02X},\t\t\t// HCI Parameter Length\n '.format(cmdLength + 6)
# Add the Sequence Number
C += '0x{:02X}, '.format(seqNum & 0xFF)
C += '0x{:02X},\t\t\t// Sequence Number\n '.format((seqNum >> 8) & 0xFF)
# Create the binary data to run the CRC across
crcarray = binarray[cmdIndex:(cmdIndex+cmdLength)]
# Increment the Sequence Number
seqNum = seqNum + 1
# Add the CRC32 to the HCI command
packetCRC = crc32(0xFFFFFFFF,crcarray)
for i in range(0,4):
C += '0x{:02X}, '.format(packetCRC & 0xFF)
packetCRC = packetCRC >> 8
C += '\t// CRC32'
# Start a newline
C += '\n '
# index
i = 0
# Loop over binarray
for n in range(cmdIndex,(cmdIndex+cmdLength)):
# Add the hex representation of each byte to S
C += '0x{:02X}'.format(binarray[n])
# Print in comma-separated lines, 8 bytes long each.
if i % 8 == 7:
C += ',\n '
else:
C += ', '
i += 1
# The end of the previous loop will leave an extra comma and some amount of
# whitespace after the last byte value. This line will remove that.
C = C.rsplit(',', 1)[0]
C += '\n },\n'
# Increment the patch index
patchIndex += 1
#print("End of While Loop")
#print(patchIndex)
# Adjust the command size
cmdSize -= cmdLength
#print(cmdSize)
# Adjust the command index
cmdIndex += cmdLength
#print(cmdIndex)
cmdIndex = (cmdIndex + 3) & 0xFFFFFFFC
#print(cmdIndex)
# Set the ending patch index
S += ' {0:3d}'.format(patchIndex) + ' },\n'
else:
# skipping with extra bytes inserted by emp patch
# tool
cmdIndex += 1
# The end of the previous loop will leave an extra comma and some amount of
# whitespace after the last byte value. This line will remove that.
C = C.rsplit(',', 1)[0]
C += '\n};\n\n'
# The end of the previous loop will leave an extra comma and some amount of
# whitespace after the last byte value. This line will remove that.
S = S.rsplit(',', 1)[0]
if (memory == 'OTP'):
memory_var = 1;
else:
memory_var = 0;
if (patchIndex > 0):
# Define a set of names to be used in the output files.
formatmap = {'length' : patchCount,
'name' : output,
'numpatches' : patchIndex,
'macroname' : output.upper(),
'lengthmacro' : output.upper() + '_NUM_PATCHES',
'varname' : output.title().replace('_', ''),
'patch_dest_memory_macro' : output.upper() + '_DEST_MEMORY',
'dest_memory': memory_var,
}
else:
# Define a set of names to be used in the output files.
formatmap = {'length' : 1,
'name' : output,
'numpatches' : patchIndex,
'macroname' : output.upper(),
'lengthmacro' : output.upper() + '_NUM_PATCHES',
'varname' : output.title().replace('_', ''),
'patch_dest_memory_macro' : output.upper() + '_DEST_MEMORY',
'dest_memory': memory_var,
}
dir = os.path.join(os.getcwd(),'../../ambiq_ble/em9304')
os.chdir(dir)
with open(output + '.c', mode = 'w') as out:
# Print the global fileheader.
print(cfile1.format(**formatmap), file=out)
if (patchIndex > 0):
print(cfile2.format(**formatmap), file=out)
print(S, file=out)
print(cfile3.format(**formatmap), file=out)
print(C, file=out)
else:
print(cfile2_null.format(**formatmap), file=out)
print(S, file=out)
print(cfile3_null.format(**formatmap), file=out)
print(C, file=out)
with open(output + '.h', mode = 'w') as out:
print(hfile1.format(**formatmap), file=out)
if (patchIndex > 0):
print(hfile2.format(**formatmap), file=out)
else:
print(hfile3.format(**formatmap), file=out)
return
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
print('Parse Args')
parser = argparse.ArgumentParser(description =
'Convert multiple EM9304 *.emp files to the em9304_patches.* files.')
parser.add_argument('-o', dest = 'memory', default = 'IRAM',
help = 'Destination memory (IRAM or OTP)')
args = parser.parse_args()
print('Call function')
process(args.memory)
@@ -0,0 +1,98 @@
#!/usr/bin/env python3
import argparse
import configparser
import os
import sys
import platform
import binascii
import struct
#******************************************************************************
#
# CRC32 lookup table.
#
#******************************************************************************
crc32Table = [
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d ]
def crc32( previousCrc, L ):
crc = previousCrc
for b in L:
crc = crc32Table[( crc ^ b) & 0xFF ] ^ ( crc >> 8 )
return (~crc & 0xFFFFFFFF)
#******************************************************************************
#
# Read in the source *.emp file, update the CRC and write it to destination
# *.emp file. This only works for *.emp files with single containers.
#
#******************************************************************************
def process(source, destination):
sourceData = open(source, "rb").read()
destData = bytearray(sourceData)
print(binascii.b2a_hex(sourceData))
size = struct.unpack("<I", sourceData[4:4+4])[0]
destData[16:16+4] = struct.pack("<I", 0)
packetCRC = crc32(0xFFFFFFFF,destData[0:size])
destData[16:16+4] = struct.pack("<I", packetCRC)
print(binascii.b2a_hex(destData))
open(destination, "wb").write(destData)
return
#******************************************************************************
#
# Main program flow
#
#******************************************************************************
if __name__ == '__main__':
parser = argparse.ArgumentParser(description =
'Updates CRC32 for EM9304 *.emp files.')
parser.add_argument('-i', dest = 'sourcefile', help = 'Source *.emp file')
parser.add_argument('-o', dest = 'destfile', help = 'Destination *.emp file')
args = parser.parse_args()
process(args.sourcefile,args.destfile)
@@ -0,0 +1 @@
from .linker_config import generate_files
@@ -0,0 +1,181 @@
from string import Template
import re
def fix_startup_file(config, filename):
# Get the stack size from the config file. Note that the startup file
# measures in words, not bytes.
stack_size = config['STACK']['size'] // 4
# Read in every line of the given file
input_lines = None
with open(filename) as f:
input_lines = [line for line in f]
# Perform a search-and-replace with the new stack size.
def fix_line(line):
return re.sub(r'(g_pui32Stack\[)([x0-9a-fA-F]*)',
r'\g<1>' + str(stack_size),
line)
output_lines = map(fix_line, input_lines)
output_text = ''.join(output_lines)
# Write the new file back to the input
with open(filename, 'w') as f:
f.write(output_text)
def generate_link_script(config):
D = dict()
D['ro_base'] = format_hex(config['ROMEM']['start'])
D['ro_size'] = config['ROMEM']['size']
D['rw_base'] = format_hex(config['RWMEM']['start'])
D['rw_size'] = config['RWMEM']['size']
D['stack_base'] = format_hex(config['STACK']['start'])
D['stack_size'] = config['STACK']['size']
D['tcm_base'] = format_hex(config['TCM']['start'])
D['tcm_size'] = config['TCM']['size']
D['section_definitions'] = format_section_definitions(config)
D['additional_sections'] = format_sections(config)
return link_script_template.substitute(**D)
def format_section_definitions(config):
def format_section_definition(section):
mapping = {
'name': section,
'permissions': config[section]['perm'],
'base': format_hex(config[section]['start']),
'length': format_hex(config[section]['size']),
}
return section_definition_template.substitute(**mapping)
extra_sections = get_extra_sections(config)
return ''.join(map(format_section_definition, extra_sections))
def format_sections(config):
def format_section(section):
mapping = {
'name': section,
}
return section_template.substitute(**mapping)
extra_sections = get_extra_sections(config)
return ''.join(map(format_section, extra_sections))
def get_extra_sections(config):
def section_not_required(x):
return x not in ['ROMEM', 'RWMEM', 'STACK', 'TCM']
return list(filter(section_not_required, config))
def format_hex(n):
return '0x{:08X}'.format(n)
link_script_template = Template('''\
/******************************************************************************
*
* linker_script.ld - Linker script for applications using startup_gnu.c
*
*****************************************************************************/
ENTRY(Reset_Handler)
MEMORY
{
ROMEM (rx) : ORIGIN = ${ro_base}, LENGTH = ${ro_size}
RWMEM (rwx) : ORIGIN = ${rw_base}, LENGTH = ${rw_size}
TCM (rwx) : ORIGIN = ${tcm_base}, LENGTH = ${tcm_size}
STACKMEM (rwx) : ORIGIN = ${stack_base}, LENGTH = ${stack_size}
}
SECTIONS
{
.text :
{
. = ALIGN(4);
KEEP(*(.isr_vector))
KEEP(*(.patch))
*(.text)
*(.text*)
*(.rodata)
*(.rodata*)
. = ALIGN(4);
_etext = .;
} > ROMEM
.data :
{
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data*)
. = ALIGN(4);
_edata = .;
} > RWMEM AT>ROMEM
/* used by startup to initialize data */
_init_data = LOADADDR(.data);
.tcm :
{
. = ALIGN(4);
_stcm = .;
*(.tcm)
*(.tcm*)
. = ALIGN(4);
_etcm = .;
} > TCM AT>ROMEM
/* used by startup to initialize tcm */
_init_tcm = LOADADDR(.tcm);
/* User stack section initialized by startup code. */
.stack (NOLOAD):
{
. = ALIGN(8);
*(.stack)
*(.stack*)
. = ALIGN(8);
} > STACKMEM
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > RWMEM
.ARM.attributes 0 : { *(.ARM.attributes) }
}\
''')
section_definition_template = Template('''\
${name} (${permissions}) : ORIGIN = ${base}, LENGTH = ${length}
''')
section_template = Template('''\
${name} :
{
*(${name})
} > ${name}
''')
@@ -0,0 +1,168 @@
from string import Template
import re
required_sections = ['ROMEM', 'RWMEM', 'STACK']
def fix_startup_file(config, filename):
# Get the stack size from the config file. Note that the startup file
# measures in words, not bytes.
stack_size = config['STACK']['size'] // 4
# Read in every line of the given file
input_lines = None
with open(filename) as f:
input_lines = [line for line in f]
# Perform a search-and-replace with the new stack size.
def fix_line(line):
return re.sub(r'(pui32Stack\[)([x0-9a-fA-F]*)',
r'\g<1>' + str(stack_size),
line)
output_lines = map(fix_line, input_lines)
output_text = ''.join(output_lines)
# Write the new file back to the input
with open(filename, 'w') as f:
f.write(output_text)
def generate_link_script(config):
robase = config['ROMEM']['start']
roend = robase + config['ROMEM']['size']
rwbase = config['RWMEM']['start']
rwend = rwbase + config['RWMEM']['size']
tcmbase = config['TCM']['start']
tcmend = tcmbase + config['TCM']['size']
stackbase = config['STACK']['start']
stackend = stackbase + config['STACK']['size']
D = dict()
D['robase'] = format_hex(robase)
D['roend'] = format_hex(roend)
D['rwbase'] = format_hex(rwbase)
D['rwend'] = format_hex(rwend)
D['tcmbase'] = format_hex(tcmbase)
D['tcmend'] = format_hex(tcmend)
D['stackbase'] = format_hex(stackbase)
D['stackend'] = format_hex(stackend)
D['stack_size'] = config['STACK']['size']
#D['section_defs'] = format_sections(section_definition, config)
#D['section_blocks'] = format_sections(section_block, config)
#D['section_placements'] = format_sections(section_placement, config)
return link_script_template.substitute(**D)
def format_sections(template, config):
def fill_section_template(section):
mapping = {
'section_name': section,
'block_name': section + '_BLOCK',
'mem_name': section + '_MEM',
'align': 4,
'start': format_hex(config[section]['start']),
'size': format_hex(config[section]['size']),
'end': format_hex(config[section]['start'] +
config[section]['size']),
'permissions': convert_permissions(config[section]['perm']),
}
return template.substitute(**mapping)
def is_extra_section(section):
return section not in required_sections
sections = filter(is_extra_section, config)
return ''.join(map(fill_section_template, sections))
def convert_permissions(perm_string):
if 'r' in perm_string and 'w' in perm_string and 'x' in perm_string:
return 'readwrite'
else:
return 'readonly'
def format_hex(n):
return '0x{:08X}'.format(n)
link_script_template = Template('''\
//*****************************************************************************
//
// linker_script.icf
//
// IAR linker Configuration File
//
//*****************************************************************************
//
// Define a memory section that covers the entire 4 GB addressable space of the
// processor. (32-bit can address up to 4GB)
//
define memory mem with size = 4G;
//
// Define regions for the various types of internal memory.
//
define region ROMEM = mem:[from ${robase} to ${roend}];
define region RWMEM = mem:[from ${rwbase} to ${rwend}];
define region TCM = mem:[from ${tcmbase} to ${tcmend}];
define region STACKMEM = mem:[from ${stackbase} to ${stackend}];
//
// Define blocks for logical groups of data.
//
define block HEAP with alignment = 0x8, size = 0x00000000 { };
define block CSTACK with alignment = 0x8, size = ${stack_size}
{
section .stack
};
define block ROSTART with fixed order
{
readonly section .intvec,
readonly section .patch
};
//
// Set section properties.
//
initialize by copy { readwrite };
initialize by copy { section RWMEM };
do not initialize { section .noinit };
do not initialize { section .stack };
//
// Place code sections in memory regions.
//
place at start of ROMEM { block ROSTART };
place in ROMEM { readonly };
place at start of STACKMEM { block CSTACK};
place in RWMEM { block HEAP, readwrite, section .noinit };
place in TCM { section .tcm };
''')
section_definition = Template('''\
define region ${mem_name} = mem:[from ${start} to ${end}];
''')
section_block = Template('''\
define block ${block_name} with alignment = ${align}, size = ${size}
{
${permissions} section ${section_name}
};
''')
section_placement = Template('''\
place in ${mem_name} { block ${block_name} };
''')
@@ -0,0 +1,171 @@
from string import Template
import textwrap
import re
def fix_startup_file(config, filename):
# Get the stack size from the config file. Note that the startup file
# measures in words, not bytes.
stack_size = config['STACK']['size']
# Read in every line of the given file
input_lines = None
with open(filename) as f:
input_lines = [line for line in f]
# Perform a search-and-replace with the new stack size.
def fix_line(line):
return re.sub(r'(Stack EQU )0x[0-9a-fA-F]*',
r'\g<1>' + '0x{:08X}'.format(stack_size),
line)
output_lines = map(fix_line, input_lines)
output_text = ''.join(output_lines)
# Write the new file back to the input
with open(filename, 'w') as f:
f.write(output_text)
def generate_link_script(config):
mapping = dict()
mapping['ro_base'] = format_number(config['ROMEM']['start'])
mapping['ro_size'] = format_number(config['ROMEM']['size'])
mapping['rw_base'] = format_number(config['RWMEM']['start'])
mapping['rw_size'] = format_number(config['RWMEM']['size'])
mapping['stack_base'] = format_number(config['STACK']['start'])
mapping['stack_size'] = format_number(config['STACK']['size'])
mapping['tcm_base'] = format_number(config['TCM']['start'])
mapping['tcm_size'] = format_number(config['TCM']['size'])
mapping['additional_sections'] = generate_sections(config)
link_script = link_script_template.substitute(**mapping)
debug_file = debug_file_template.substitute(**mapping)
return (link_script, debug_file)
def generate_sections(config):
# If there aren't any custom sections in the config file, we don't need to
# add anything to the linker scripts.
if 'custom_sections' not in config:
return ''
elif not config['custom_sections']:
return ''
L = []
for mem_section in config['custom_sections']:
D = dict()
D['name'] = mem_section['blockname']
D['start'] = format_number(mem_section['start'])
D['length'] = format_number(mem_section['length'])
D['sections'] = '\n'.join(' * ({})'.format(x) for x in mem_section['sections'])
S = extra_section_template.substitute(**D)
L.append(textwrap.indent(S, 4 * ' '))
return '\n' + '\n'.join(L)
def format_number(n):
return '0x{:08X}'.format(n)
link_script_template = Template('''\
;******************************************************************************
;
; Scatter file for Keil linker configuration.
;
;******************************************************************************
LR_1 ${ro_base}
{
ROMEM ${ro_base} ${ro_size}
{
*.o (RESET, +First)
* (+RO)
}
RWMEM ${rw_base} ${rw_size}
{
* (+RW, +ZI)
}
TCM ${tcm_base} ${tcm_size}
{
* (.tcm)
}
STACKMEM ${stack_base} ${stack_size}
{
startup_keil.o (STACK)
}
}
''')
extra_section_template = Template('''\
${name} ${start} ${length}
{
${sections}
}
''')
debug_file_template = Template('''\
/*----------------------------------------------------------------------------
* Name: Dbg_RAM.ini
* Purpose: RAM Debug Initialization File
* Note(s):
*----------------------------------------------------------------------------
* This file is part of the uVision/ARM development tools.
* This software may only be used under the terms of a valid, current,
* end user licence from KEIL for a compatible version of KEIL software
* development tools. Nothing else gives you the right to use this software.
*
* This software is supplied "AS IS" without warranties of any kind.
*
* Copyright (c) 2008-2013 Keil - An ARM Company. All rights reserved.
*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------
TraceSetup() Turn on ITM clocks, etc.
*----------------------------------------------------------------------------*/
FUNC void TraceSetup (void)
{
// turn on the ITM/TPIU clock
//_WDWORD(0x40020250, 0x00000201); // TPIU clock enabled at 3MHz
}
/*----------------------------------------------------------------------------
Setup() configure PC & SP for RAM Debug
*----------------------------------------------------------------------------*/
FUNC void Setup (void) {
SP = _RDWORD(${ro_base}+0x0); // Setup Stack Pointer
PC = _RDWORD(${ro_base}+0x4); // Setup Program Counter
_WDWORD(0xE000ED08, ${ro_base}+0x0); // Setup Vector Table Offset Register (done in system file)
}
/*----------------------------------------------------------------------------
OnResetExec() executed after reset via uVision's 'Reset'-button
*----------------------------------------------------------------------------*/
FUNC void OnResetExec (void)
{
}
LOAD %L INCREMENTAL // load the application
Setup(); // Setup for Running
BS main
g
/*----------------------------------------------------------------------------
*----------------------------------------------------------------------------*/
''')
extra_section_template = Template('''\
${name} ${start} ${length}
{
${sections}
}
''')
@@ -0,0 +1,224 @@
# <TextAddressRange></TextAddressRange>
# <DataAddressRange></DataAddressRange>
import argparse
import yaml
from . import iar_link
from . import gcc_link
from . import keil_link
# Linker file locations.
IAR_LD_SCRIPT = "iar/linker_script.icf"
KEIL_LD_SCRIPT = "keil/linker_script.sct"
KEIL_DEBUG_FILE = "keil/Dbg_RAM.ini"
GCC_LD_SCRIPT = "gcc/linker_script.ld"
KEIL_STARTUP_FILE = "keil/startup_keil.s"
IAR_STARTUP_FILE = "iar/startup_iar.c"
GCC_STARTUP_FILE = "gcc/startup_gcc.c"
def main():
parser = argparse.ArgumentParser()
parser.add_argument('config_file', default='default_config_apollo3.yaml')
parser.add_argument('-i', dest='iar', action='store_true')
parser.add_argument('-k', dest='keil', action='store_true')
parser.add_argument('-g', dest='gcc', action='store_true')
args = parser.parse_args()
# Read the configuration file.
config = read_configuration(args.config_file)
# Figure out what we want to build. If no specific toolchains were
# specified, just build everything. Otherwise, only build what was
# specified.
build_all = True
if args.keil:
write_keil_linker_scripts(config)
build_all = False
if args.iar:
write_iar_linker_scripts(config)
build_all = False
if args.gcc:
write_gcc_linker_scripts(config)
build_all = False
if build_all:
write_keil_linker_scripts(config)
write_iar_linker_scripts(config)
write_gcc_linker_scripts(config)
# Print the memory map
print_memory_map(config)
def generate_files(config_file, toolchains):
config = read_configuration(config_file)
print_memory_map(config)
if 'keil' in toolchains:
write_keil_linker_scripts(config)
if 'iar' in toolchains:
write_iar_linker_scripts(config)
if 'gcc' in toolchains:
write_gcc_linker_scripts(config)
def read_configuration(config_file):
"""Read a configuration YAML files and return a dictionary of memory sections"""
# Read the YAML configuration file as is.
with open(config_file) as file_object:
config_string = file_object.read()
config = yaml.load(config_string, Loader=yaml.FullLoader)
memory_sections = config['MemorySections']
# Search through the memory sections...
for name in memory_sections.keys():
# Find the start value, and convert it if necessary.
start = memory_sections[name]['start']
memory_sections[name]['start'] = convert_number(start)
# If we find a size value, use it.
if 'size' in memory_sections[name]:
size = memory_sections[name]['size']
memory_sections[name]['size'] = convert_number(size)
# It not, try to use an "end" value.
elif 'end' in memory_sections[name]:
end = memory_sections[name]['end']
memory_sections[name]['size'] = convert_number(end) - convert_number(start)
stack_size = config['StackOptions']['size']
config['StackOptions']['size'] = convert_number(stack_size)
# Create a memory section for the stack.
memory_sections['STACK'] = dict()
# Create a section for the stack. To do this, we'll either need to
# carve space out of TCM or RWMEM.
if config['StackOptions']['place_in_tcm']:
if config['StackOptions']['size'] > memory_sections['TCM']['size']:
raise LinkerConfigError("Stack ({} B) doesn't fit in TCM ({} B)".format(
config['StackOptions']['size'],
memory_sections['TCM']['size']))
memory_sections['STACK']['start'] = memory_sections['TCM']['start']
memory_sections['STACK']['size'] = config['StackOptions']['size']
memory_sections['TCM']['start'] = (memory_sections['STACK']['start'] +
config['StackOptions']['size'])
memory_sections['TCM']['size'] = (memory_sections['TCM']['size'] -
config['StackOptions']['size'])
else:
if config['StackOptions']['size'] > memory_sections['RWMEM']['size']:
raise LinkerConfigError("Stack ({} B) doesn't fit in RWMEM ({} B)".format(
config['StackOptions']['size'],
memory_sections['RWMEM']['size']))
memory_sections['STACK']['start'] = memory_sections['RWMEM']['start']
memory_sections['STACK']['size'] = config['StackOptions']['size']
memory_sections['RWMEM']['start'] = (memory_sections['STACK']['start'] +
config['StackOptions']['size'])
memory_sections['RWMEM']['size'] = (memory_sections['RWMEM']['size'] -
config['StackOptions']['size'])
return memory_sections
def write_keil_linker_scripts(config):
try:
linker_file_data, debug_file_data = keil_link.generate_link_script(config)
with open(KEIL_LD_SCRIPT, 'w') as linker_file:
linker_file.write(linker_file_data)
with open(KEIL_DEBUG_FILE, 'w') as debug_file:
debug_file.write(debug_file_data)
keil_link.fix_startup_file(config, KEIL_STARTUP_FILE)
except FileNotFoundError:
pass
def write_iar_linker_scripts(config):
try:
with open(IAR_LD_SCRIPT, 'w') as linker_file:
linker_file.write(iar_link.generate_link_script(config))
iar_link.fix_startup_file(config, IAR_STARTUP_FILE)
except FileNotFoundError:
pass
def write_gcc_linker_scripts(config):
try:
with open(GCC_LD_SCRIPT, 'w') as linker_file:
linker_file.write(gcc_link.generate_link_script(config))
gcc_link.fix_startup_file(config, GCC_STARTUP_FILE)
except FileNotFoundError:
pass
def convert_number(N):
"""Take in an integer or a numerical string ending in 'K', and convert it to an int"""
if isinstance(N, int):
return N
elif isinstance(N, str):
if N.endswith('K'):
return int(N[:-1]) * 1024
else:
raise LinkerConfigError('"{}" not recognized as a number'.format(N))
else:
raise LinkerConfigError('"{}" not recognized as a number'.format(N))
def print_memory_map(memory_sections):
"""Show the memory map in a human readable format"""
# Sort the section names by their starting address.
section_names = sorted(memory_sections.keys(), key=lambda x: memory_sections[x]['start'])
# Search for the longest section name, and record its length.
max_name_length = max(len(x) for x in section_names)
# This is a roundabout way to copy the maximum name length into a format
# string, so we can make the output string look pretty.
name_format = '{{:{}}}'.format(max_name_length + 1)
for name in section_names:
section = memory_sections[name]
mapping = {
'name': name_format.format(name + ':'),
'start': '0x{:08X}'.format(section['start']),
'end': '0x{:08X}'.format(section['start'] + section['size']),
'size': section['size'],
}
print('{name} {start:10} - {end:10} ({size} bytes)'.format(**mapping))
# Custom error for linker configuration problems.
class LinkerConfigError(Exception):
pass
if __name__ == '__main__':
main()
@@ -0,0 +1,127 @@
import string
import datetime
memory_header_string = '''\
//*****************************************************************************
//
// file am_memory_map.h
//
// brief Memory map include file.
//
// This file is generated by "memory_map.py".
//
//*****************************************************************************
//*****************************************************************************
//
// Copyright (c) {copyrightyear}, Ambiq Micro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of the AmbiqSuite Development Package.
//
//*****************************************************************************
#ifndef AM_MEMORY_MAP_H
#define AM_MEMORY_MAP_H
#ifdef __cplusplus
extern "C"
{
#endif
//*****************************************************************************
//
// Memory block locations.
//
//*****************************************************************************
${memory_locations}
//*****************************************************************************
//
// Memory block sizes (in bytes)
//
//*****************************************************************************
${memory_sizes}
#ifdef __cplusplus
}
#endif
#endif // AM_MEMORY_MAP_H
'''
currdatetime = datetime.datetime.now()
memory_header_template = string.Template(memory_header_string.format(copyrightyear=currdatetime.year))
memory_location_define = '#define AM_MEM_{name:36} ((void *) 0x{start:08X})'
memory_size_define = '#define AM_MEM_{name:36} {size}'
def generate(memory_sections):
mapping = {
'memory_locations': '\n'.join(write_memory_locations(memory_sections)),
'memory_sizes': '\n'.join(write_memory_sizes(memory_sections)),
}
return memory_header_template.substitute(**mapping)
def write_memory_locations(memory_sections):
section_names = sorted_sections(memory_sections)
for n in section_names:
mapping = {
'name': n,
'start': memory_sections[n]['start'],
}
yield memory_location_define.format_map(mapping)
def write_memory_sizes(memory_sections):
section_names = sorted_sections(memory_sections)
for n in section_names:
mapping = {
'name': n + '_SIZE',
'size': memory_sections[n]['size'],
}
yield memory_size_define.format_map(mapping)
def sorted_sections(memory_sections):
def start_address(section):
return memory_sections[section]['start']
return sorted(memory_sections.keys(), key=start_address)
@@ -0,0 +1,51 @@
import argparse
import apollo3p
import os
DEFAULT_TOOLCHAINS = ['iar', 'keil', 'gcc']
linker_generators = {
'apollo3p': apollo3p.generate_files,
}
def main():
parser = argparse.ArgumentParser()
parser.add_argument('config_file')
parser.add_argument('-i', dest='iar', action='store_true')
parser.add_argument('-k', dest='keil', action='store_true')
parser.add_argument('-g', dest='gcc', action='store_true')
parser.add_argument('-p', dest='part')
args = parser.parse_args()
toolchains = []
if args.iar:
toolchains.append('iar')
if args.keil:
toolchains.append('keil')
if args.gcc:
toolchains.append('gcc')
if toolchains == []:
#
# No command line arguments were specified.
# Determine which toolchains need a linker control file based on whether
# a directory already exists for that toolchain.
#
for toolnm in DEFAULT_TOOLCHAINS:
if os.path.isdir(toolnm):
toolchains.append(toolnm)
if toolchains == []:
toolchains = DEFAULT_TOOLCHAINS
if args.part in linker_generators:
print('Building linker scripts for {} and toolchains {}.'.format(args.part, toolchains))
linker_generators[args.part](args.config_file, toolchains)
else:
print("Can't generate linker scripts for {}. (No generator functions found)".format(args.part))
if __name__ == '__main__':
main()