diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index c4fc0c4c..498db8be 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -1,12 +1,12 @@ set(EXTCPPSRC #pacman - external/pacman/main.cpp - external/pacman/ui_pacman.cpp +# external/pacman/main.cpp +# external/pacman/ui_pacman.cpp #tetris - external/tetris/main.cpp - external/tetris/ui_tetris.cpp +# external/tetris/main.cpp +# external/tetris/ui_tetris.cpp #afsk_rx external/afsk_rx/main.cpp @@ -65,7 +65,7 @@ set(EXTCPPSRC ) set(EXTAPPLIST - pacman +# pacman afsk_rx calculator font_viewer @@ -79,5 +79,5 @@ set(EXTAPPLIST gpssim spainter keyfob - tetris +# tetris ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index c2449335..26fda937 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -1,5 +1,6 @@ /* Copyright (C) 2023 Bernd Herzog + Copyright (C) 2024 Mark Thompson Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,22 +17,27 @@ MEMORY { - /* external apps: regions can't overlap so addresses are corrected after build */ - ram_external_app_pacman (rwx) : org = 0xEEE90000, len = 32k - ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 32k - ram_external_app_calculator (rwx) : org = 0xEEEB0000, len = 32k - ram_external_app_font_viewer(rwx) : org = 0xEEEC0000, len = 32k - ram_external_app_blespam(rwx) : org = 0xEEED0000, len = 32k - ram_external_app_analogtv(rwx) : org = 0xEEEE0000, len = 32k - ram_external_app_nrf_rx(rwx) : org = 0xEEEF0000, len = 32k - ram_external_app_coasterp(rwx) : org = 0xEEF00000, len = 32k - ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k - ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k - ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k - ram_external_app_gpssim(rwx) : org = 0xEEF40000, len = 32k - ram_external_app_spainter(rwx) : org = 0xEEF50000, len = 32k - ram_external_app_keyfob(rwx) : org = 0xEEF60000, len = 32k - ram_external_app_tetris(rwx) : org = 0xEEF70000, len = 32k + /* + * External apps: regions can't overlap so addresses are corrected after build. + * Picking uncommon address values for search & replace in binaries (no false positives) - 0xADB00000-0xADEF0000 seems to be good. + * Also need to consider processor memory map - reading 0xADxxxxxx generates a fault which may be better than unexpected behavior. + * External app address ranges below must match those in python file "external_app_info.py". + */ + ram_external_app_pacman (rwx) : org = 0xADB00000, len = 32k + ram_external_app_afsk_rx (rwx) : org = 0xADB10000, len = 32k + ram_external_app_calculator (rwx) : org = 0xADB20000, len = 32k + ram_external_app_font_viewer(rwx) : org = 0xADB30000, len = 32k + ram_external_app_blespam(rwx) : org = 0xADB40000, len = 32k + ram_external_app_analogtv(rwx) : org = 0xADB50000, len = 32k + ram_external_app_nrf_rx(rwx) : org = 0xADB60000, len = 32k + ram_external_app_coasterp(rwx) : org = 0xADB70000, len = 32k + ram_external_app_lge(rwx) : org = 0xADB80000, len = 32k + ram_external_app_lcr(rwx) : org = 0xADB90000, len = 32k + ram_external_app_jammer(rwx) : org = 0xADBA0000, len = 32k + ram_external_app_gpssim(rwx) : org = 0xADBB0000, len = 32k + ram_external_app_spainter(rwx) : org = 0xADBC0000, len = 32k + ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k + ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k } SECTIONS @@ -60,7 +66,6 @@ SECTIONS *(*ui*external_app*font_viewer*); } > ram_external_app_font_viewer - .external_app_blespam : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_blespam.application_information)); @@ -91,14 +96,12 @@ SECTIONS *(*ui*external_app*lge*); } > ram_external_app_lge - .external_app_lcr : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_lcr.application_information)); *(*ui*external_app*lcr*); } > ram_external_app_lcr - .external_app_jammer : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_jammer.application_information)); @@ -111,7 +114,6 @@ SECTIONS *(*ui*external_app*gpssim*); } > ram_external_app_gpssim - .external_app_spainter : ALIGN(4) SUBALIGN(4) { KEEP(*(.external_app.app_spainter.application_information)); diff --git a/firmware/tools/check_for_shared_external_code.py b/firmware/tools/check_for_shared_external_code.py new file mode 100644 index 00000000..7d7ad676 --- /dev/null +++ b/firmware/tools/check_for_shared_external_code.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2024 Mark Thompson +# +# This file is part of PortaPack. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sys +from external_app_info import maximum_application_size +from external_app_info import external_apps_address_start +from external_app_info import external_apps_address_end + +usage_message = """ +PortaPack ROM image checker for possible shared external code addresses + +Usage: +""" + +def read_image(path): + f = open(path, 'rb') + data = f.read() + f.close() + return data + +if len(sys.argv) != 2: + print(usage_message) + sys.exit(-1) + +image = read_image(sys.argv[1]) +image = bytearray(image) + +for i in range(0, len(image), 4): + snippet = image[i:i+4] + val = int.from_bytes(snippet, byteorder='little') + offset = val & 0xFFFF + if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0): + print ("External code address", hex(val),"at offset", hex(i),"in", sys.argv[1]) diff --git a/firmware/tools/export_external_apps.py b/firmware/tools/export_external_apps.py index 8249ab9e..37f04e40 100755 --- a/firmware/tools/export_external_apps.py +++ b/firmware/tools/export_external_apps.py @@ -2,6 +2,7 @@ # # Copyright (C) 2023 Bernd Herzog +# Copyright (C) 2024 Mark Thompson # # This file is part of PortaPack. # @@ -25,6 +26,9 @@ import os import sys import struct import subprocess +from external_app_info import maximum_application_size +from external_app_info import external_apps_address_start +from external_app_info import external_apps_address_end usage_message = """ PortaPack external app image creator @@ -34,8 +38,6 @@ See firmware/application/CMakeLists.txt > COMMAND ${EXPORT_EXTERNAL_APP_IMAGES} Usage: """ -maximum_application_size = 32*1024 - if len(sys.argv) < 4: print(usage_message) sys.exit(-1) @@ -51,7 +53,7 @@ def write_image(data, path): f.write(data) f.close() -def patch_image(image_data, search_address, replace_address): +def patch_image(path, image_data, search_address, replace_address): if (len(image_data) % 4) != 0: print("file size not divideable by 4") sys.exit(-1) @@ -62,8 +64,8 @@ def patch_image(image_data, search_address, replace_address): snippet = image_data[x*4:4*(x+1)] val = int.from_bytes(snippet, byteorder='little') - # in firmware/application/external/external.ld the origin is set to something like search_address=0xEEE90000 - # if the value is above the search_address and inside a 40kb window (maximum size of an app) we replace it + # in firmware/application/external/external.ld the origin is set to something like search_address=0xADB00000 + # if the value is above the search_address and inside a 32kb window (maximum size of an app) we replace it # with replace_address=(0x1008000 + m4 size) where the app will actually be located. The reason we do this instead just # using the right address in external.ld is gcc does not permit to use the same memory range multiple times. if val > search_address and (val - search_address) < maximum_application_size: @@ -74,7 +76,9 @@ def patch_image(image_data, search_address, replace_address): external_application_image += new_snippet else: external_application_image += snippet - + if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0): + print ("WARNING: External code address", hex(val), "at offset", hex(x*4), "in", path) + return external_application_image project_source_dir = sys.argv[1] #/portapack-mayhem/firmware/application @@ -90,7 +94,7 @@ for external_image_prefix in sys.argv[4:]: # COMMAND ${CMAKE_OBJCOPY} -v -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}_ext_pacman.bin --only-section=.external_app_pacman himg = "{}/external_app_{}.himg".format(binary_dir, external_image_prefix) - subprocess.run([cmake_objcopy, "-v", "-O", "binary", "{}/application.elf".format(binary_dir), himg, "--only-section=.external_app_{}".format(external_image_prefix)]) + subprocess.run([cmake_objcopy, "-v", "-O", "binary", "{}/application.elf".format(binary_dir), himg, "--only-section=.external_app_{}".format(external_image_prefix)]) external_application_image = read_image(himg) @@ -101,7 +105,7 @@ for external_image_prefix in sys.argv[4:]: if (chunk_data[0] == 0 and chunk_data[1] == 0 and chunk_data[2] == 0 and chunk_data[3] == 0): replace_address = 0x10080000 search_address = int.from_bytes(external_application_image[externalAppEntry_header_position:externalAppEntry_header_position+4], byteorder='little') & 0xFFFF0000 - external_application_image = patch_image(external_application_image, search_address, replace_address) + external_application_image = patch_image(himg, external_application_image, search_address, replace_address) external_application_image[memory_location_header_position:memory_location_header_position+4] = replace_address.to_bytes(4, byteorder='little') checksum = 0 @@ -126,7 +130,7 @@ for external_image_prefix in sys.argv[4:]: replace_address = 0x10080000 + len(m4_image) search_address = int.from_bytes(external_application_image[externalAppEntry_header_position:externalAppEntry_header_position+4], byteorder='little') & 0xFFFF0000 - external_application_image = patch_image(external_application_image, search_address, replace_address) + external_application_image = patch_image(himg, external_application_image, search_address, replace_address) external_application_image[memory_location_header_position:memory_location_header_position+4] = replace_address.to_bytes(4, byteorder='little') external_application_image[m4_app_offset_header_position:m4_app_offset_header_position+4] = app_image_len.to_bytes(4, byteorder='little') diff --git a/firmware/tools/external_app_info.py b/firmware/tools/external_app_info.py new file mode 100644 index 00000000..51fe7823 --- /dev/null +++ b/firmware/tools/external_app_info.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2024 Mark Thompson +# +# This file is part of PortaPack. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# external app address ranges below must match those in linker file "external.ld" +maximum_application_size = 32*1024 +external_apps_address_start = 0xADB00000 +external_apps_address_end = 0xADF00000 \ No newline at end of file diff --git a/firmware/tools/make_spi_image.py b/firmware/tools/make_spi_image.py index 812028b3..2ce3273a 100755 --- a/firmware/tools/make_spi_image.py +++ b/firmware/tools/make_spi_image.py @@ -2,6 +2,7 @@ # # Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. +# Copyright (C) 2024 Mark Thompson # # This file is part of PortaPack. # @@ -22,6 +23,9 @@ # import sys +from external_app_info import maximum_application_size +from external_app_info import external_apps_address_start +from external_app_info import external_apps_address_end usage_message = """ PortaPack SPI flash image generator @@ -81,10 +85,14 @@ pad_size = spi_size - 4 - len(spi_image) for i in range(pad_size): spi_image += spi_image_default_byte -# quick "add up the words" checksum: +# quick "add up the words" checksum, and check for possible references to code in external apps checksum = 0 for i in range(0, len(spi_image), 4): - checksum += (spi_image[i] + (spi_image[i + 1] << 8) + (spi_image[i + 2] << 16) + (spi_image[i + 3] << 24)) + snippet = spi_image[i:i+4] + val = int.from_bytes(snippet, byteorder='little') + checksum += val + if (val >= external_apps_address_start) and (val < external_apps_address_end) and ((val & 0xFFFF) < maximum_application_size) and ((val & 0x3)==0): + print ("WARNING: External code address", hex(val), "at offset", hex(i), "in", sys.argv[3]) final_checksum = 0 checksum = (final_checksum - checksum) & 0xFFFFFFFF