mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Add Checksums to Firmware & External App images (#1809)
* Pad image to 1MB and add simple checksum * Test code to verify firmware checksum * Comment out unneeded zlib * Add files via upload * Print space remaining in ROM * Append checksum to external apps too * Check external app checksums when loading * Is it 2024 already?! * Validate firmware checksum before flashing * Add files via upload * Added flash error warning to nav screen * Clang * Replaced some hard-coded values with #defines * Check FW checksum before USB serial flash too * Add files via upload
This commit is contained in:
parent
2d98c5d311
commit
6a6c6d6502
@ -30,6 +30,30 @@ static const char16_t* firmware_folder = u"/FIRMWARE";
|
||||
Thread* FlashUtilityView::thread{nullptr};
|
||||
static constexpr size_t max_filename_length = 26;
|
||||
|
||||
bool valid_firmware_file(std::filesystem::path::string_type path) {
|
||||
File firmware_file;
|
||||
uint32_t read_buffer[128];
|
||||
uint32_t checksum{1};
|
||||
|
||||
// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
|
||||
auto result = firmware_file.open(path.c_str());
|
||||
if (!result.is_valid()) {
|
||||
checksum = 0;
|
||||
for (uint32_t i = FLASH_STARTING_ADDRESS; i < FLASH_ROM_SIZE / sizeof(read_buffer); i++) {
|
||||
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
|
||||
|
||||
// if file is smaller than 1MB, assume it's a downgrade to an old FW version and ignore the checksum
|
||||
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
|
||||
checksum = FLASH_EXPECTED_CHECKSUM;
|
||||
break;
|
||||
}
|
||||
|
||||
checksum += simple_checksum((uint32_t)read_buffer, sizeof(read_buffer));
|
||||
}
|
||||
}
|
||||
return (checksum == FLASH_EXPECTED_CHECKSUM);
|
||||
}
|
||||
|
||||
FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
add_children({&labels,
|
||||
@ -111,8 +135,11 @@ void FlashUtilityView::flash_firmware(std::filesystem::path::string_type path) {
|
||||
if (endsWith(path, u".tar")) {
|
||||
// extract, then update
|
||||
path = extract_tar(u'/' + path).native();
|
||||
if (path.empty()) return;
|
||||
}
|
||||
|
||||
if (path.empty() || !valid_firmware_file(path.c_str()))
|
||||
return; // bad firmware image - just returning back to the file list
|
||||
|
||||
ui::Painter painter;
|
||||
painter.fill_rectangle(
|
||||
{0, 0, portapack::display.width(), portapack::display.height()},
|
||||
|
@ -32,8 +32,14 @@
|
||||
#include "untar.hpp"
|
||||
#include <cstdint>
|
||||
|
||||
#define FLASH_ROM_SIZE 1048576
|
||||
#define FLASH_STARTING_ADDRESS 0x00000000
|
||||
#define FLASH_EXPECTED_CHECKSUM 0x00000000
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool valid_firmware_file(std::filesystem::path::string_type path);
|
||||
|
||||
class FlashUtilityView : public View {
|
||||
public:
|
||||
FlashUtilityView(NavigationView& nav);
|
||||
|
@ -107,6 +107,7 @@ namespace ui {
|
||||
|
||||
/* static */ bool ExternalItemsMenuLoader::run_external_app(ui::NavigationView& nav, std::filesystem::path filePath) {
|
||||
File app;
|
||||
uint32_t checksum{0};
|
||||
|
||||
auto openError = app.open(filePath);
|
||||
if (openError)
|
||||
@ -115,7 +116,6 @@ namespace ui {
|
||||
application_information_t application_information = {};
|
||||
|
||||
auto readResult = app.read(&application_information, sizeof(application_information_t));
|
||||
|
||||
if (!readResult)
|
||||
return false;
|
||||
|
||||
@ -135,6 +135,8 @@ namespace ui {
|
||||
if (!readResult)
|
||||
return false;
|
||||
|
||||
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
|
||||
|
||||
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||
break;
|
||||
}
|
||||
@ -156,6 +158,8 @@ namespace ui {
|
||||
if (!readResult)
|
||||
return false;
|
||||
|
||||
checksum += simple_checksum((uint32_t)target_memory, readResult.value());
|
||||
|
||||
if (readResult.value() != bytes_to_read)
|
||||
break;
|
||||
}
|
||||
@ -168,11 +172,16 @@ namespace ui {
|
||||
if (!readResult)
|
||||
return false;
|
||||
|
||||
checksum += simple_checksum((uint32_t)&application_information.memory_location[file_read_index], readResult.value());
|
||||
|
||||
if (readResult.value() < std::filesystem::max_file_block_size)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (checksum != EXT_APP_EXPECTED_CHECKSUM)
|
||||
return false;
|
||||
|
||||
application_information.externalAppEntry(nav);
|
||||
return true;
|
||||
}
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
#define EXT_APP_EXPECTED_CHECKSUM 0x00000000
|
||||
|
||||
namespace ui {
|
||||
|
||||
template <size_t Width, size_t Height>
|
||||
|
@ -503,8 +503,8 @@ void SystemStatusView::rtc_battery_workaround() {
|
||||
month = (timestamp.FAT_date >> 5) & 0xF;
|
||||
day = timestamp.FAT_date & 0x1F;
|
||||
|
||||
// bump to next month at 28 days for simplicity
|
||||
if (++day > 28) {
|
||||
// bump to next month
|
||||
if (++day > rtc_time::days_per_month(year, month)) {
|
||||
day = 1;
|
||||
if (++month > 12) {
|
||||
month = 1;
|
||||
@ -547,16 +547,16 @@ InformationView::InformationView(
|
||||
<ime});
|
||||
|
||||
#if GCC_VERSION_MISMATCH
|
||||
static constexpr Style style_gcc_warning{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {33, 33, 33},
|
||||
.foreground = Color::yellow(),
|
||||
};
|
||||
version.set_style(&style_gcc_warning);
|
||||
version.set_style(&Styles::yellow);
|
||||
#else
|
||||
version.set_style(&style_infobar);
|
||||
#endif
|
||||
|
||||
if (firmware_checksum_error()) {
|
||||
version.set("FLASH ERROR");
|
||||
version.set_style(&Styles::red);
|
||||
}
|
||||
|
||||
ltime.set_style(&style_infobar);
|
||||
refresh();
|
||||
set_dirty();
|
||||
@ -568,6 +568,17 @@ void InformationView::refresh() {
|
||||
ltime.set_date_enabled(pmem::clock_with_date());
|
||||
}
|
||||
|
||||
bool InformationView::firmware_checksum_error() {
|
||||
static bool fw_checksum_checked{false};
|
||||
static bool fw_checksum_error{false};
|
||||
|
||||
// only checking firmware checksum once per boot
|
||||
if (!fw_checksum_checked) {
|
||||
fw_checksum_error = (simple_checksum(FLASH_STARTING_ADDRESS, FLASH_ROM_SIZE) != FLASH_EXPECTED_CHECKSUM);
|
||||
}
|
||||
return fw_checksum_error;
|
||||
}
|
||||
|
||||
/* Navigation ************************************************************/
|
||||
|
||||
bool NavigationView::is_top() const {
|
||||
|
@ -286,6 +286,7 @@ class InformationView : public View {
|
||||
public:
|
||||
InformationView(NavigationView& nav);
|
||||
void refresh();
|
||||
bool firmware_checksum_error();
|
||||
|
||||
private:
|
||||
// static constexpr auto version_string = "v1.4.4"; // This is commented out as we are now setting the version via ENV (VERSION_STRING=v1.0.0)
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "chprintf.h"
|
||||
#include "chqueues.h"
|
||||
#include "ui_external_items_menu_loader.hpp"
|
||||
#include "ui_flash_utility.hpp"
|
||||
#include "untar.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
@ -170,7 +171,13 @@ static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
} else if (strEndsWith(path.native(), u".bin")) {
|
||||
// nothing to do for this case yet.
|
||||
} else {
|
||||
chprintf(chp, "error only .bin or .ppfw.tar files canbe flashed.\r\n");
|
||||
chprintf(chp, "error only .bin or .ppfw.tar files can be flashed.\r\n");
|
||||
nav->pop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ui::valid_firmware_file(path.native().c_str())) {
|
||||
chprintf(chp, "error corrupt firmware file.\r\n");
|
||||
nav->pop();
|
||||
return;
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_styles.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_flash_utility.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "rtc_time.hpp"
|
||||
|
||||
@ -1051,6 +1052,9 @@ bool debug_dump() {
|
||||
pmem_dump_file.write_line("Ext APPS version req'd: 0x" + to_string_hex(VERSION_MD5));
|
||||
pmem_dump_file.write_line("GCC version: " + to_string_dec_int(__GNUC__) + "." + to_string_dec_int(__GNUC_MINOR__) + "." + to_string_dec_int(__GNUC_PATCHLEVEL__));
|
||||
|
||||
// firmware checksum
|
||||
pmem_dump_file.write_line("Firmware calculated checksum: 0x" + to_string_hex(simple_checksum(FLASH_STARTING_ADDRESS, FLASH_ROM_SIZE), 8));
|
||||
|
||||
// write persistent memory
|
||||
pmem_dump_file.write_line("\n[Persistent Memory]");
|
||||
|
||||
|
@ -235,3 +235,10 @@ std::string join(char c, std::initializer_list<std::string_view> strings) {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t simple_checksum(uint32_t buffer_address, uint32_t length) {
|
||||
uint32_t checksum = 0;
|
||||
for (uint32_t i = 0; i < length; i += 4)
|
||||
checksum += *(uint32_t*)(buffer_address + i);
|
||||
return checksum;
|
||||
}
|
@ -217,4 +217,6 @@ struct range_t {
|
||||
|
||||
std::string join(char c, std::initializer_list<std::string_view> strings);
|
||||
|
||||
uint32_t simple_checksum(uint32_t buffer_address, uint32_t length);
|
||||
|
||||
#endif /*__UTILITY_H__*/
|
||||
|
@ -104,6 +104,14 @@ for external_image_prefix in sys.argv[4:]:
|
||||
external_application_image = patch_image(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
|
||||
for i in range(0, len(external_application_image), 4):
|
||||
checksum += external_application_image[i] + (external_application_image[i + 1] << 8) + (external_application_image[i + 2] << 16) + (external_application_image[i + 3] << 24)
|
||||
|
||||
final_checksum = 0
|
||||
checksum = (final_checksum - checksum) & 0xFFFFFFFF
|
||||
external_application_image += checksum.to_bytes(4, 'little')
|
||||
|
||||
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
|
||||
continue
|
||||
|
||||
@ -127,5 +135,13 @@ for external_image_prefix in sys.argv[4:]:
|
||||
print("application {} can not exceed 32kb: {} bytes used".format(external_image_prefix, len(external_application_image)))
|
||||
sys.exit(-1)
|
||||
|
||||
checksum = 0
|
||||
for i in range(0, len(external_application_image), 4):
|
||||
checksum += external_application_image[i] + (external_application_image[i + 1] << 8) + (external_application_image[i + 2] << 16) + (external_application_image[i + 3] << 24)
|
||||
|
||||
final_checksum = 0
|
||||
checksum = (final_checksum - checksum) & 0xFFFFFFFF
|
||||
external_application_image += checksum.to_bytes(4, 'little')
|
||||
|
||||
# write .ppma (portapack mayhem application)
|
||||
write_image(external_application_image, "{}/{}.ppma".format(binary_dir, external_image_prefix))
|
||||
|
@ -74,7 +74,24 @@ for image in images:
|
||||
padded_data = image['data'] + (spi_image_default_byte * pad_size)
|
||||
spi_image += padded_data
|
||||
|
||||
if len(spi_image) > spi_size:
|
||||
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image), spi_size))
|
||||
if len(spi_image) > spi_size - 4:
|
||||
raise RuntimeError('SPI flash image size of %d exceeds device size of %d bytes' % (len(spi_image) + 4, spi_size))
|
||||
|
||||
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:
|
||||
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))
|
||||
|
||||
final_checksum = 0
|
||||
checksum = (final_checksum - checksum) & 0xFFFFFFFF
|
||||
|
||||
spi_image += checksum.to_bytes(4, 'little')
|
||||
|
||||
write_image(spi_image, output_path)
|
||||
|
||||
percent_remaining = round(1000 * pad_size / spi_size) / 10;
|
||||
print ("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
|
57
firmware/tools/simple_checksum.py
Normal file
57
firmware/tools/simple_checksum.py
Normal file
@ -0,0 +1,57 @@
|
||||
#!/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
|
||||
import struct
|
||||
|
||||
usage_message = """
|
||||
PortaPack ROM image checksum checker
|
||||
|
||||
Usage: <command> <input-file>
|
||||
"""
|
||||
|
||||
def read_image(path):
|
||||
f = open(path, 'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
|
||||
def write_image(data, path):
|
||||
f = open(path, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print(usage_message)
|
||||
sys.exit(-1)
|
||||
|
||||
image = read_image(sys.argv[1])
|
||||
image = bytearray(image)
|
||||
|
||||
# simple "add up the words" checksum:
|
||||
checksum = 0
|
||||
for i in range(0, len(image), 4):
|
||||
checksum += (image[i] + (image[i + 1] << 8) + (image[i + 2] << 16) + (image[i + 3] << 24))
|
||||
|
||||
checksum &= 0xFFFFFFFF
|
||||
print ("Simple checksum =", checksum)
|
Loading…
Reference in New Issue
Block a user