From 275144019372c9d6dc5874b75a3c1056b0fbea48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Thu, 21 Oct 2021 15:24:34 +0300 Subject: [PATCH] [FL-1970, FL-1965, FL-1872, FL-1689] Python framework, Scripts and fixes (#779) * Scripts: add flipper lib, migrate ob to flipper lib, update ob.data * Makefile: speedup build with phony target for .d files * FuriHal,U8G2: full MGG display support and ERC contrast tuning. * Desktop: fix dolphin rename artifact. * Scripts: port otp.py to flipper scripting lib. * Scripts: add wipe and core1 flashing to flash.py, remove obsolete shell scripts * Scripts: replace core1 flashing script with global makefile. * Scripts: final touches and migration to python. Root Makefile for everything. --- Makefile | 71 +++++ ReadMe.md | 12 + .../debug_tools/display_test/display_test.c | 2 +- .../desktop/views/desktop_first_start.c | 2 +- applications/gui/canvas.c | 2 +- .../targets/f6/furi-hal/furi-hal-version.c | 290 ++++++++++++++++++ bootloader/targets/f6/furi-hal/furi-hal.c | 1 + bootloader/targets/f6/target.c | 2 +- .../targets/f7/furi-hal/furi-hal-version.c | 290 ++++++++++++++++++ bootloader/targets/f7/furi-hal/furi-hal.c | 1 + bootloader/targets/f7/furi-hal/furi-hal.h | 14 - bootloader/targets/f7/target.c | 2 +- .../furi-hal-include/furi-hal-version.h | 173 +++++++++++ .../furi-hal => furi-hal-include}/furi-hal.h | 1 + lib/u8g2/u8g2_glue.c | 33 +- lib/u8g2/u8g2_glue.h | 4 +- make/rules.mk | 4 +- scripts/ReadMe.md | 21 +- scripts/flash.py | 152 +++++++++ scripts/flash_core1_main_swd.sh | 12 - scripts/flash_core2_ble_swd.sh | 20 -- scripts/flash_otp_version_dfu.sh | 17 - scripts/flash_otp_version_swd.sh | 17 - scripts/flash_wipe_swd.sh | 11 - scripts/flipper/app.py | 47 +++ scripts/flipper/cube.py | 91 ++++++ scripts/ob.data | 2 +- scripts/ob.py | 116 +------ scripts/otp.py | 131 ++++---- 29 files changed, 1246 insertions(+), 295 deletions(-) create mode 100644 Makefile create mode 100644 bootloader/targets/f6/furi-hal/furi-hal-version.c create mode 100644 bootloader/targets/f7/furi-hal/furi-hal-version.c delete mode 100644 bootloader/targets/f7/furi-hal/furi-hal.h create mode 100644 bootloader/targets/furi-hal-include/furi-hal-version.h rename bootloader/targets/{f6/furi-hal => furi-hal-include}/furi-hal.h (89%) create mode 100755 scripts/flash.py delete mode 100755 scripts/flash_core1_main_swd.sh delete mode 100755 scripts/flash_core2_ble_swd.sh delete mode 100755 scripts/flash_otp_version_dfu.sh delete mode 100755 scripts/flash_otp_version_swd.sh delete mode 100755 scripts/flash_wipe_swd.sh create mode 100644 scripts/flipper/app.py create mode 100644 scripts/flipper/cube.py diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..bc9c5be7e --- /dev/null +++ b/Makefile @@ -0,0 +1,71 @@ +PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))) +COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x + +.PHONY: all +all: bootloader_all firmware_all + +.PHONY: whole +whole: flash_radio bootloader_flash firmware_flash + +.PHONY: clean +clean: bootloader_clean firmware_clean + +.PHONY: flash +flash: bootloader_flash firmware_flash + +.PHONY: debug +debug: + $(MAKE) -C firmware -j9 debug + +.PHONY: wipe +wipe: + $(PROJECT_ROOT)/scripts/flash.py wipe + $(PROJECT_ROOT)/scripts/ob.py set + +.PHONY: bootloader_all +bootloader_all: + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 all + +.PHONY: firmware_all +firmware_all: + $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 all + +.PHONY: bootloader_clean +bootloader_clean: + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 clean + +.PHONY: firmware_clean +firmware_clean: + $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 clean + +.PHONY: bootloader_flash +bootloader_flash: + rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true + $(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 flash + +.PHONY: firmware_flash +firmware_flash: + rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true + $(MAKE) -C $(PROJECT_ROOT)/firmware -j9 flash + +.PHONY: flash_radio +flash_radio: + $(PROJECT_ROOT)/scripts/flash.py core2radio 0x080CA000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_full_fw.bin + $(PROJECT_ROOT)/scripts/ob.py set + +.PHONY: flash_radio_fus +flash_radio_fus: + @echo + @echo "================ DON'T DO IT ================" + @echo "= Flashing FUS is going to erase secure enclave =" + @echo "= You will loose ability to use encrypted assets =" + @echo "= type 'find / -exec rm -rf {} \;' =" + @echo "= In case if you still want to continue =" + @echo "================ JUST DON'T ================" + @echo + +.PHONY: +flash_radio_fus_please_i_m_not_going_to_complain: + $(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin + $(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin + $(PROJECT_ROOT)/scripts/ob.py set diff --git a/ReadMe.md b/ReadMe.md index 0ffae0a20..738a3108a 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -70,6 +70,18 @@ One liner: `./flash_core1_main.sh` docker-compose up -d ``` +## Compile everything + +```sh +docker-compose exec dev make -j$(nproc) +``` + +## Flash everything + +```sh +docker-compose exec dev make -j$(nproc) whole +``` + ## Compile bootloader ```sh diff --git a/applications/debug_tools/display_test/display_test.c b/applications/debug_tools/display_test/display_test.c index 1e393a8c1..f2685a820 100644 --- a/applications/debug_tools/display_test/display_test.c +++ b/applications/debug_tools/display_test/display_test.c @@ -82,7 +82,7 @@ static void display_test_reload_config(DisplayTest* instance) { instance->config_contrast, instance->config_regulation_ratio, instance->config_bias); - u8x8_d_st756x_erc_init( + u8x8_d_st756x_init( &instance->gui->canvas->fb.u8x8, instance->config_contrast, instance->config_regulation_ratio, diff --git a/applications/desktop/views/desktop_first_start.c b/applications/desktop/views/desktop_first_start.c index 4c037f0b2..c63c77520 100644 --- a/applications/desktop/views/desktop_first_start.c +++ b/applications/desktop/views/desktop_first_start.c @@ -44,7 +44,7 @@ void desktop_first_start_render(Canvas* canvas, void* model) { "%s %s%s", "I am", my_name ? my_name : "Unknown", - ",\ncyberdesktop\nliving in your\npocket >"); + ",\ncyberdolphin\nliving in your\npocket >"); canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49); elements_multiline_text_framed(canvas, 60, 17, buf); } else if(m->page == 6) { diff --git a/applications/gui/canvas.c b/applications/gui/canvas.c index f1b351d87..a83cc2ba4 100644 --- a/applications/gui/canvas.c +++ b/applications/gui/canvas.c @@ -12,7 +12,7 @@ Canvas* canvas_init() { furi_hal_power_insomnia_enter(); canvas->orientation = CanvasOrientationHorizontal; - u8g2_Setup_st756x_erc(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); // send init sequence to the display, display is in sleep mode after this u8g2_InitDisplay(&canvas->fb); diff --git a/bootloader/targets/f6/furi-hal/furi-hal-version.c b/bootloader/targets/f6/furi-hal/furi-hal-version.c new file mode 100644 index 000000000..ff3b26991 --- /dev/null +++ b/bootloader/targets/f6/furi-hal/furi-hal-version.c @@ -0,0 +1,290 @@ +#include + +#include +#include +#include + +#include + +#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE +#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE + +/** OTP V0 Structure: prototypes and early EVT */ +typedef struct { + uint8_t board_version; + uint8_t board_target; + uint8_t board_body; + uint8_t board_connect; + uint32_t header_timestamp; + char name[FURI_HAL_VERSION_NAME_LENGTH]; +} FuriHalVersionOTPv0; + +/** OTP V1 Structure: late EVT, DVT */ +typedef struct { + /* First 64 bits: header */ + uint16_t header_magic; + uint8_t header_version; + uint8_t header_reserved; + uint32_t header_timestamp; + + /* Second 64 bits: board info */ + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint16_t board_reserved; /** Reserved for future use, 0x0000 */ + + /* Third 64 bits: Unique Device Name */ + char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */ +} FuriHalVersionOTPv1; + +/** OTP V2 Structure: DVT2, PVT, Production */ +typedef struct { + /* Early First 64 bits: header */ + uint16_t header_magic; + uint8_t header_version; + uint8_t header_reserved; + uint32_t header_timestamp; + + /* Early Second 64 bits: board info */ + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_display; /** Board display */ + uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */ + uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */ + + /* Late Third 64 bits: device info */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */ + uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */ + + /* Late Fourth 64 bits: Unique Device Name */ + char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */ +} FuriHalVersionOTPv2; + +/** Represenation Model: */ +typedef struct { + uint32_t timestamp; + + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint8_t board_display; /** Board display */ + + char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */ + char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */ + uint8_t ble_mac[6]; +} FuriHalVersion; + +static FuriHalVersion furi_hal_version = {0}; + +static void furi_hal_version_set_name(const char* name) { + if(name != NULL) { + strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH); + snprintf( + furi_hal_version.device_name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "xFlipper %s", + furi_hal_version.name); + } else { + snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper"); + } + + furi_hal_version.device_name[0] = 0; + + // BLE Mac address + uint32_t udn = LL_FLASH_GetUDN(); + uint32_t company_id = LL_FLASH_GetSTCompanyID(); + uint32_t device_id = LL_FLASH_GetDeviceID(); + furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF); + furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8); + furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16); + furi_hal_version.ble_mac[3] = (uint8_t)device_id; + furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF); + furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8); +} + +static void furi_hal_version_load_otp_default() { + furi_hal_version_set_name(NULL); +} + +static void furi_hal_version_load_otp_v0() { + const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS; + + furi_hal_version.timestamp = otp->header_timestamp; + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + + furi_hal_version_set_name(otp->name); +} + +static void furi_hal_version_load_otp_v1() { + const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS; + + furi_hal_version.timestamp = otp->header_timestamp; + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + furi_hal_version.board_color = otp->board_color; + furi_hal_version.board_region = otp->board_region; + + furi_hal_version_set_name(otp->name); +} + +static void furi_hal_version_load_otp_v2() { + const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS; + + // 1st block, programmed afer baking + furi_hal_version.timestamp = otp->header_timestamp; + + // 2nd block, programmed afer baking + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + furi_hal_version.board_display = otp->board_display; + + // 3rd and 4th blocks, programmed on FATP stage + if(otp->board_color != 0xFF) { + furi_hal_version.board_color = otp->board_color; + furi_hal_version.board_region = otp->board_region; + furi_hal_version_set_name(otp->name); + } else { + furi_hal_version.board_color = 0; + furi_hal_version.board_region = 0; + furi_hal_version_set_name(NULL); + } +} + +void furi_hal_version_init() { + switch(furi_hal_version_get_otp_version()) { + case FuriHalVersionOtpVersionUnknown: + furi_hal_version_load_otp_default(); + break; + case FuriHalVersionOtpVersionEmpty: + furi_hal_version_load_otp_default(); + break; + case FuriHalVersionOtpVersion0: + furi_hal_version_load_otp_v0(); + break; + case FuriHalVersionOtpVersion1: + furi_hal_version_load_otp_v1(); + break; + case FuriHalVersionOtpVersion2: + furi_hal_version_load_otp_v2(); + break; + default: + furi_hal_version_load_otp_default(); + } +} + +bool furi_hal_version_do_i_belong_here() { + return furi_hal_version_get_hw_target() == 7; +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Zero"; +} + +const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() { + if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) { + return FuriHalVersionOtpVersionEmpty; + } else { + if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == + FURI_HAL_VERSION_OTP_HEADER_MAGIC) { + // Version 1+ + uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version; + if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) { + return version; + } else { + return FuriHalVersionOtpVersionUnknown; + } + } else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) { + // Version 0 + return FuriHalVersionOtpVersion0; + } else { + // Version Unknown + return FuriHalVersionOtpVersionUnknown; + } + } +} + +const uint8_t furi_hal_version_get_hw_version() { + return furi_hal_version.board_version; +} + +const uint8_t furi_hal_version_get_hw_target() { + return furi_hal_version.board_target; +} + +const uint8_t furi_hal_version_get_hw_body() { + return furi_hal_version.board_body; +} + +const FuriHalVersionColor furi_hal_version_get_hw_color() { + return furi_hal_version.board_color; +} + +const uint8_t furi_hal_version_get_hw_connect() { + return furi_hal_version.board_connect; +} + +const FuriHalVersionRegion furi_hal_version_get_hw_region() { + return furi_hal_version.board_region; +} + +const FuriHalVersionDisplay furi_hal_version_get_hw_display() { + return furi_hal_version.board_display; +} + +const uint32_t furi_hal_version_get_hw_timestamp() { + return furi_hal_version.timestamp; +} + +const char* furi_hal_version_get_name_ptr() { + return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name; +} + +const char* furi_hal_version_get_device_name_ptr() { + return furi_hal_version.device_name + 1; +} + +const char* furi_hal_version_get_ble_local_device_name_ptr() { + return furi_hal_version.device_name; +} + +const uint8_t* furi_hal_version_get_ble_mac() { + return furi_hal_version.ble_mac; +} + +const struct Version* furi_hal_version_get_firmware_version(void) { + return version_get(); +} + +const struct Version* furi_hal_version_get_boot_version(void) { +#ifdef NO_BOOTLOADER + return 0; +#else + /* Backup register which points to structure in flash memory */ + return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1); +#endif +} + +size_t furi_hal_version_uid_size() { + return 64 / 8; +} + +const uint8_t* furi_hal_version_uid() { + return (const uint8_t*)UID64_BASE; +} diff --git a/bootloader/targets/f6/furi-hal/furi-hal.c b/bootloader/targets/f6/furi-hal/furi-hal.c index 36671807f..b77892db2 100644 --- a/bootloader/targets/f6/furi-hal/furi-hal.c +++ b/bootloader/targets/f6/furi-hal/furi-hal.c @@ -5,6 +5,7 @@ void furi_hal_init() { furi_hal_i2c_init(); furi_hal_light_init(); furi_hal_spi_init(); + furi_hal_version_init(); } void delay(float milliseconds) { diff --git a/bootloader/targets/f6/target.c b/bootloader/targets/f6/target.c index 6d94e5d1d..df694edd5 100644 --- a/bootloader/targets/f6/target.c +++ b/bootloader/targets/f6/target.c @@ -189,7 +189,7 @@ void target_display_init() { hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); // Initialize u8g2_t fb; - u8g2_Setup_st756x_erc(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); u8g2_InitDisplay(&fb); // Create payload u8g2_ClearBuffer(&fb); diff --git a/bootloader/targets/f7/furi-hal/furi-hal-version.c b/bootloader/targets/f7/furi-hal/furi-hal-version.c new file mode 100644 index 000000000..ff3b26991 --- /dev/null +++ b/bootloader/targets/f7/furi-hal/furi-hal-version.c @@ -0,0 +1,290 @@ +#include + +#include +#include +#include + +#include + +#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE +#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE + +/** OTP V0 Structure: prototypes and early EVT */ +typedef struct { + uint8_t board_version; + uint8_t board_target; + uint8_t board_body; + uint8_t board_connect; + uint32_t header_timestamp; + char name[FURI_HAL_VERSION_NAME_LENGTH]; +} FuriHalVersionOTPv0; + +/** OTP V1 Structure: late EVT, DVT */ +typedef struct { + /* First 64 bits: header */ + uint16_t header_magic; + uint8_t header_version; + uint8_t header_reserved; + uint32_t header_timestamp; + + /* Second 64 bits: board info */ + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint16_t board_reserved; /** Reserved for future use, 0x0000 */ + + /* Third 64 bits: Unique Device Name */ + char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */ +} FuriHalVersionOTPv1; + +/** OTP V2 Structure: DVT2, PVT, Production */ +typedef struct { + /* Early First 64 bits: header */ + uint16_t header_magic; + uint8_t header_version; + uint8_t header_reserved; + uint32_t header_timestamp; + + /* Early Second 64 bits: board info */ + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_display; /** Board display */ + uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */ + uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */ + + /* Late Third 64 bits: device info */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */ + uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */ + + /* Late Fourth 64 bits: Unique Device Name */ + char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */ +} FuriHalVersionOTPv2; + +/** Represenation Model: */ +typedef struct { + uint32_t timestamp; + + uint8_t board_version; /** Board version */ + uint8_t board_target; /** Board target firmware */ + uint8_t board_body; /** Board body */ + uint8_t board_connect; /** Board interconnect */ + uint8_t board_color; /** Board color */ + uint8_t board_region; /** Board region */ + uint8_t board_display; /** Board display */ + + char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */ + char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */ + uint8_t ble_mac[6]; +} FuriHalVersion; + +static FuriHalVersion furi_hal_version = {0}; + +static void furi_hal_version_set_name(const char* name) { + if(name != NULL) { + strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH); + snprintf( + furi_hal_version.device_name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH, + "xFlipper %s", + furi_hal_version.name); + } else { + snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper"); + } + + furi_hal_version.device_name[0] = 0; + + // BLE Mac address + uint32_t udn = LL_FLASH_GetUDN(); + uint32_t company_id = LL_FLASH_GetSTCompanyID(); + uint32_t device_id = LL_FLASH_GetDeviceID(); + furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF); + furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8); + furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16); + furi_hal_version.ble_mac[3] = (uint8_t)device_id; + furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF); + furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8); +} + +static void furi_hal_version_load_otp_default() { + furi_hal_version_set_name(NULL); +} + +static void furi_hal_version_load_otp_v0() { + const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS; + + furi_hal_version.timestamp = otp->header_timestamp; + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + + furi_hal_version_set_name(otp->name); +} + +static void furi_hal_version_load_otp_v1() { + const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS; + + furi_hal_version.timestamp = otp->header_timestamp; + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + furi_hal_version.board_color = otp->board_color; + furi_hal_version.board_region = otp->board_region; + + furi_hal_version_set_name(otp->name); +} + +static void furi_hal_version_load_otp_v2() { + const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS; + + // 1st block, programmed afer baking + furi_hal_version.timestamp = otp->header_timestamp; + + // 2nd block, programmed afer baking + furi_hal_version.board_version = otp->board_version; + furi_hal_version.board_target = otp->board_target; + furi_hal_version.board_body = otp->board_body; + furi_hal_version.board_connect = otp->board_connect; + furi_hal_version.board_display = otp->board_display; + + // 3rd and 4th blocks, programmed on FATP stage + if(otp->board_color != 0xFF) { + furi_hal_version.board_color = otp->board_color; + furi_hal_version.board_region = otp->board_region; + furi_hal_version_set_name(otp->name); + } else { + furi_hal_version.board_color = 0; + furi_hal_version.board_region = 0; + furi_hal_version_set_name(NULL); + } +} + +void furi_hal_version_init() { + switch(furi_hal_version_get_otp_version()) { + case FuriHalVersionOtpVersionUnknown: + furi_hal_version_load_otp_default(); + break; + case FuriHalVersionOtpVersionEmpty: + furi_hal_version_load_otp_default(); + break; + case FuriHalVersionOtpVersion0: + furi_hal_version_load_otp_v0(); + break; + case FuriHalVersionOtpVersion1: + furi_hal_version_load_otp_v1(); + break; + case FuriHalVersionOtpVersion2: + furi_hal_version_load_otp_v2(); + break; + default: + furi_hal_version_load_otp_default(); + } +} + +bool furi_hal_version_do_i_belong_here() { + return furi_hal_version_get_hw_target() == 7; +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Zero"; +} + +const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() { + if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) { + return FuriHalVersionOtpVersionEmpty; + } else { + if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == + FURI_HAL_VERSION_OTP_HEADER_MAGIC) { + // Version 1+ + uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version; + if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) { + return version; + } else { + return FuriHalVersionOtpVersionUnknown; + } + } else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) { + // Version 0 + return FuriHalVersionOtpVersion0; + } else { + // Version Unknown + return FuriHalVersionOtpVersionUnknown; + } + } +} + +const uint8_t furi_hal_version_get_hw_version() { + return furi_hal_version.board_version; +} + +const uint8_t furi_hal_version_get_hw_target() { + return furi_hal_version.board_target; +} + +const uint8_t furi_hal_version_get_hw_body() { + return furi_hal_version.board_body; +} + +const FuriHalVersionColor furi_hal_version_get_hw_color() { + return furi_hal_version.board_color; +} + +const uint8_t furi_hal_version_get_hw_connect() { + return furi_hal_version.board_connect; +} + +const FuriHalVersionRegion furi_hal_version_get_hw_region() { + return furi_hal_version.board_region; +} + +const FuriHalVersionDisplay furi_hal_version_get_hw_display() { + return furi_hal_version.board_display; +} + +const uint32_t furi_hal_version_get_hw_timestamp() { + return furi_hal_version.timestamp; +} + +const char* furi_hal_version_get_name_ptr() { + return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name; +} + +const char* furi_hal_version_get_device_name_ptr() { + return furi_hal_version.device_name + 1; +} + +const char* furi_hal_version_get_ble_local_device_name_ptr() { + return furi_hal_version.device_name; +} + +const uint8_t* furi_hal_version_get_ble_mac() { + return furi_hal_version.ble_mac; +} + +const struct Version* furi_hal_version_get_firmware_version(void) { + return version_get(); +} + +const struct Version* furi_hal_version_get_boot_version(void) { +#ifdef NO_BOOTLOADER + return 0; +#else + /* Backup register which points to structure in flash memory */ + return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1); +#endif +} + +size_t furi_hal_version_uid_size() { + return 64 / 8; +} + +const uint8_t* furi_hal_version_uid() { + return (const uint8_t*)UID64_BASE; +} diff --git a/bootloader/targets/f7/furi-hal/furi-hal.c b/bootloader/targets/f7/furi-hal/furi-hal.c index 36671807f..b77892db2 100644 --- a/bootloader/targets/f7/furi-hal/furi-hal.c +++ b/bootloader/targets/f7/furi-hal/furi-hal.c @@ -5,6 +5,7 @@ void furi_hal_init() { furi_hal_i2c_init(); furi_hal_light_init(); furi_hal_spi_init(); + furi_hal_version_init(); } void delay(float milliseconds) { diff --git a/bootloader/targets/f7/furi-hal/furi-hal.h b/bootloader/targets/f7/furi-hal/furi-hal.h deleted file mode 100644 index 41a79f11a..000000000 --- a/bootloader/targets/f7/furi-hal/furi-hal.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#define furi_assert(value) (void)(value) - -void furi_hal_init(); - -void delay(float milliseconds); - -void delay_us(float microseconds); diff --git a/bootloader/targets/f7/target.c b/bootloader/targets/f7/target.c index 6d94e5d1d..df694edd5 100644 --- a/bootloader/targets/f7/target.c +++ b/bootloader/targets/f7/target.c @@ -189,7 +189,7 @@ void target_display_init() { hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull); // Initialize u8g2_t fb; - u8g2_Setup_st756x_erc(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); u8g2_InitDisplay(&fb); // Create payload u8g2_ClearBuffer(&fb); diff --git a/bootloader/targets/furi-hal-include/furi-hal-version.h b/bootloader/targets/furi-hal-include/furi-hal-version.h new file mode 100644 index 000000000..99a005338 --- /dev/null +++ b/bootloader/targets/furi-hal-include/furi-hal-version.h @@ -0,0 +1,173 @@ +/** + * @file furi-hal-version.h + * Version HAL API + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define FURI_HAL_VERSION_NAME_LENGTH 8 +#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) +/** BLE symbol + "Flipper " + name */ +#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) + +/** OTP Versions enum */ +typedef enum { + FuriHalVersionOtpVersion0 = 0x00, + FuriHalVersionOtpVersion1 = 0x01, + FuriHalVersionOtpVersion2 = 0x02, + FuriHalVersionOtpVersionEmpty = 0xFFFFFFFE, + FuriHalVersionOtpVersionUnknown = 0xFFFFFFFF, +} FuriHalVersionOtpVersion; + +/** Device Colors */ +typedef enum { + FuriHalVersionColorUnknown = 0x00, + FuriHalVersionColorBlack = 0x01, + FuriHalVersionColorWhite = 0x02, +} FuriHalVersionColor; + +/** Device Regions */ +typedef enum { + FuriHalVersionRegionUnknown = 0x00, + FuriHalVersionRegionEuRu = 0x01, + FuriHalVersionRegionUsCaAu = 0x02, + FuriHalVersionRegionJp = 0x03, +} FuriHalVersionRegion; + +/** Device Display */ +typedef enum { + FuriHalVersionDisplayUnknown = 0x00, + FuriHalVersionDisplayErc = 0x01, + FuriHalVersionDisplayMgg = 0x02, +} FuriHalVersionDisplay; + +/** Init flipper version + */ +void furi_hal_version_init(); + +/** Check target firmware version + * + * @return true if target and real matches + */ +bool furi_hal_version_do_i_belong_here(); + +/** Get model name + * + * @return model name C-string + */ +const char* furi_hal_version_get_model_name(); + +/** Get OTP version + * + * @return OTP Version + */ +const FuriHalVersionOtpVersion furi_hal_version_get_otp_version(); + +/** Get hardware version + * + * @return Hardware Version + */ +const uint8_t furi_hal_version_get_hw_version(); + +/** Get hardware target + * + * @return Hardware Target + */ +const uint8_t furi_hal_version_get_hw_target(); + +/** Get hardware body + * + * @return Hardware Body + */ +const uint8_t furi_hal_version_get_hw_body(); + +/** Get hardware body color + * + * @return Hardware Color + */ +const FuriHalVersionColor furi_hal_version_get_hw_color(); + +/** Get hardware connect + * + * @return Hardware Interconnect + */ +const uint8_t furi_hal_version_get_hw_connect(); + +/** Get hardware region + * + * @return Hardware Region + */ +const FuriHalVersionRegion furi_hal_version_get_hw_region(); + +/** Get hardware display id + * + * @return Display id + */ +const FuriHalVersionDisplay furi_hal_version_get_hw_display(); + +/** Get hardware timestamp + * + * @return Hardware Manufacture timestamp + */ +const uint32_t furi_hal_version_get_hw_timestamp(); + +/** Get pointer to target name + * + * @return Hardware Name C-string + */ +const char* furi_hal_version_get_name_ptr(); + +/** Get pointer to target device name + * + * @return Hardware Device Name C-string + */ +const char* furi_hal_version_get_device_name_ptr(); + +/** Get pointer to target ble local device name + * + * @return Ble Device Name C-string + */ +const char* furi_hal_version_get_ble_local_device_name_ptr(); + +/** Get BLE MAC address + * + * @return pointer to BLE MAC address + */ +const uint8_t* furi_hal_version_get_ble_mac(); + +/** Get address of version structure of bootloader, stored in chip flash. + * + * @return Address of boot version structure. + */ +const struct Version* furi_hal_version_get_boot_version(); + +/** Get address of version structure of firmware. + * + * @return Address of firmware version structure. + */ +const struct Version* furi_hal_version_get_firmware_version(); + +/** Get platform UID size in bytes + * + * @return UID size in bytes + */ +size_t furi_hal_version_uid_size(); + +/** Get const pointer to UID + * + * @return pointer to UID + */ +const uint8_t* furi_hal_version_uid(); + +#ifdef __cplusplus +} +#endif diff --git a/bootloader/targets/f6/furi-hal/furi-hal.h b/bootloader/targets/furi-hal-include/furi-hal.h similarity index 89% rename from bootloader/targets/f6/furi-hal/furi-hal.h rename to bootloader/targets/furi-hal-include/furi-hal.h index 41a79f11a..83cf739e0 100644 --- a/bootloader/targets/f6/furi-hal/furi-hal.h +++ b/bootloader/targets/furi-hal-include/furi-hal.h @@ -4,6 +4,7 @@ #include #include #include +#include #define furi_assert(value) (void)(value) diff --git a/lib/u8g2/u8g2_glue.c b/lib/u8g2/u8g2_glue.c index b2549eeb6..e2ca8a9df 100644 --- a/lib/u8g2/u8g2_glue.c +++ b/lib/u8g2/u8g2_glue.c @@ -185,7 +185,7 @@ uint8_t u8x8_d_st756x_common(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *a return 1; } -void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias) { +void u8x8_d_st756x_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias) { contrast = contrast & 0b00111111; regulation_ratio = regulation_ratio & 0b111; @@ -209,7 +209,7 @@ void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_r u8x8_cad_EndTransfer(u8x8); } -uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { +uint8_t u8x8_d_st756x_flipper(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { /* call common procedure first and handle messages there */ if (u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) { /* msg not handled, then try here */ @@ -219,13 +219,24 @@ uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ break; case U8X8_MSG_DISPLAY_INIT: u8x8_d_helper_display_init(u8x8); - /* Bias, EV and Regulation Ration - * EV = 32 - * RR = V0 / ((1 - (63 - EV) / 162) * 2.1) - * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 5.5 (0b101) - * Bias = 1/9 (false) - */ - u8x8_d_st756x_erc_init(u8x8, 32, 0b101, false); + FuriHalVersionDisplay display = furi_hal_version_get_hw_display(); + if (display == FuriHalVersionDisplayMgg) { + /* MGG v0+(ST7567) + * EV = 32 + * RR = V0 / ((1 - (63 - EV) / 162) * 2.1) + * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110) + * Bias = 1/9 (false) + */ + u8x8_d_st756x_init(u8x8, 32, 0b110, false); + } else { + /* ERC v1(ST7565) and v2(ST7567) + * EV = 33 + * RR = V0 / ((1 - (63 - EV) / 162) * 2.1) + * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101) + * Bias = 1/9 (false) + */ + u8x8_d_st756x_init(u8x8, 33, 0b101, false); + } break; case U8X8_MSG_DISPLAY_SET_FLIP_MODE: if ( arg_int == 0 ) { @@ -244,10 +255,10 @@ uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ return 1; } -void u8g2_Setup_st756x_erc(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) { +void u8g2_Setup_st756x_flipper(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) { uint8_t tile_buf_height; uint8_t *buf; - u8g2_SetupDisplay(u8g2, u8x8_d_st756x_erc, u8x8_cad_001, byte_cb, gpio_and_delay_cb); + u8g2_SetupDisplay(u8g2, u8x8_d_st756x_flipper, u8x8_cad_001, byte_cb, gpio_and_delay_cb); buf = u8g2_m_16_8_f(&tile_buf_height); u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation); } diff --git a/lib/u8g2/u8g2_glue.h b/lib/u8g2/u8g2_glue.h index 10236df05..34324b37a 100644 --- a/lib/u8g2/u8g2_glue.h +++ b/lib/u8g2/u8g2_glue.h @@ -7,6 +7,6 @@ uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, vo uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr); -void u8g2_Setup_st756x_erc(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); +void u8g2_Setup_st756x_flipper(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb); -void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); +void u8x8_d_st756x_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias); diff --git a/make/rules.mk b/make/rules.mk index 0a8b2547c..390b9ee0c 100644 --- a/make/rules.mk +++ b/make/rules.mk @@ -140,7 +140,7 @@ generate_cscope_db: @cscope -b -k -i $(OBJ_DIR)/source.list -f $(OBJ_DIR)/cscope.out @rm -rf $(OBJ_DIR)/source.list $(OBJ_DIR)/source.list.p +# Prevent make from trying to find .d targets +%.d: ; -ifneq ("$(wildcard $(OBJ_DIR)/*.d)","") -include $(DEPS) -endif diff --git a/scripts/ReadMe.md b/scripts/ReadMe.md index 935a58e7d..2cd1ee0bd 100644 --- a/scripts/ReadMe.md +++ b/scripts/ReadMe.md @@ -1,6 +1,6 @@ # About -This folder contains differnt scripts that automates routine actions. +This folder contains supplementary scripts that automates routine actions. Flashing scripts are based on cli version of [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html). You will need to add STM32_Programmer_CLI to your path to use them. @@ -9,29 +9,26 @@ You will need to add STM32_Programmer_CLI to your path to use them. Always flash your device in the folllowing sequence: - OTP (Only on empty MCU) -- Core2 firmware -- Core1 firmware +- Core1 and Core2 firmware flashing - Option Bytes ## Otp flashing !!! Flashing incorrect OTP may permanently brick your device !!! -Normally OTP data generated and flashed at factory. +Normally OTP data generated and flashed at the factory. In case if MCU was replaced you'll need correct OTP data to be able to use companion applications. -Use `otp.py` to generate OTP data and `flash_otp_version_*` to flash OTP zone. +Use `otp.py` to generate and flash OTP data. You will need exact main board revision to genrate OTP data. It can be found on main PCB. +Also display type, region and etc... !!! Flashing incorrect OTP may permanently brick your device !!! -## Core2 flashing +## Core1 and Core2 firmware flashing -Script blindly updates FUS and Radiostack. This operation is going to corrupt bootloader and firmware. -Reflash Core1 after Core2. - -## Core1 flashing - -Script compiles and flashes both bootloader and firmware. +Main flashing sequence can be found in root `Makefile`. +Core2 goes first, then Core1. +Never flash FUS or you will loose your job, girlfriend and keys in secure enclave. ## Option Bytes diff --git a/scripts/flash.py b/scripts/flash.py new file mode 100755 index 000000000..0e920076c --- /dev/null +++ b/scripts/flash.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 + +import logging +import argparse +import sys +import os + +from flipper.app import App +from flipper.cube import CubeProgrammer + +STATEMENT = "AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE" + + +class Main(App): + def init(self): + self.subparsers = self.parser.add_subparsers(help="sub-command help") + # Wipe + self.parser_wipe = self.subparsers.add_parser("wipe", help="Wipe MCU Flash") + self.parser_wipe.set_defaults(func=self.wipe) + # Core 1 boot + self.parser_core1boot = self.subparsers.add_parser( + "core1boot", help="Flash Core1 Bootloader" + ) + self._addArgsSWD(self.parser_core1boot) + self.parser_core1boot.add_argument( + "bootloader", type=str, help="Bootloader binary" + ) + self.parser_core1boot.set_defaults(func=self.core1boot) + # Core 1 firmware + self.parser_core1firmware = self.subparsers.add_parser( + "core1firmware", help="Flash Core1 Firmware" + ) + self._addArgsSWD(self.parser_core1firmware) + self.parser_core1firmware.add_argument( + "firmware", type=str, help="Firmware binary" + ) + self.parser_core1firmware.set_defaults(func=self.core1firmware) + # Core 1 all + self.parser_core1 = self.subparsers.add_parser( + "core1", help="Flash Core1 Boot and Firmware" + ) + self._addArgsSWD(self.parser_core1) + self.parser_core1.add_argument("bootloader", type=str, help="Bootloader binary") + self.parser_core1.add_argument("firmware", type=str, help="Firmware binary") + self.parser_core1.set_defaults(func=self.core1) + # Core 2 fus + self.parser_core2fus = self.subparsers.add_parser( + "core2fus", help="Flash Core2 Firmware Update Service" + ) + self._addArgsSWD(self.parser_core2fus) + self.parser_core2fus.add_argument( + "--statement", + type=str, + help="NEVER FLASH FUS, IT WILL ERASE CRYPTO ENCLAVE", + required=True, + ) + self.parser_core2fus.add_argument( + "fus_address", type=str, help="Firmware Update Service Address" + ) + self.parser_core2fus.add_argument( + "fus", type=str, help="Firmware Update Service Binary" + ) + self.parser_core2fus.set_defaults(func=self.core2fus) + # Core 2 radio stack + self.parser_core2radio = self.subparsers.add_parser( + "core2radio", help="Flash Core2 Radio stack" + ) + self._addArgsSWD(self.parser_core2radio) + self.parser_core2radio.add_argument( + "radio_address", type=str, help="Radio Stack Binary Address" + ) + self.parser_core2radio.add_argument( + "radio", type=str, help="Radio Stack Binary" + ) + self.parser_core2radio.set_defaults(func=self.core2radio) + + def _addArgsSWD(self, parser): + parser.add_argument( + "--port", type=str, help="Port to connect: swd or usb1", default="swd" + ) + + def wipe(self): + self.logger.info(f"Wiping flash") + cp = CubeProgrammer("swd") + self.logger.info(f"Setting RDP to 0xBB") + cp.setOptionBytes({"RDP": ("0xBB", "rw")}) + self.logger.info(f"Verifying RDP") + r = cp.checkOptionBytes({"RDP": ("0xBB", "rw")}) + assert r == True + self.logger.info(f"Result: {r}") + self.logger.info(f"Setting RDP to 0xAA") + cp.setOptionBytes({"RDP": ("0xAA", "rw")}) + self.logger.info(f"Verifying RDP") + r = cp.checkOptionBytes({"RDP": ("0xAA", "rw")}) + assert r == True + self.logger.info(f"Result: {r}") + self.logger.info(f"Complete") + return 0 + + def core1boot(self): + self.logger.info(f"Flashing bootloader") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x08000000", self.args.bootloader) + self.logger.info(f"Complete") + cp.resetTarget() + return 0 + + def core1firmware(self): + self.logger.info(f"Flashing firmware") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x08008000", self.args.firmware) + self.logger.info(f"Complete") + cp.resetTarget() + return 0 + + def core1(self): + self.logger.info(f"Flashing bootloader") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x08000000", self.args.bootloader) + self.logger.info(f"Flashing firmware") + cp.flashBin("0x08008000", self.args.firmware) + cp.resetTarget() + self.logger.info(f"Complete") + return 0 + + def core2fus(self): + if self.args.statement != STATEMENT: + self.logger.error( + f"PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING" + ) + return 1 + self.logger.info(f"Flashing Firmware Update Service") + cp = CubeProgrammer(self.args.port) + cp.flashCore2(self.args.fus_address, self.args.fus) + self.logger.info(f"Complete") + return 0 + + def core2radio(self): + if int(self.args.radio_address, 16) > 0x080E0000: + self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER") + return 1 + cp = CubeProgrammer(self.args.port) + self.logger.info(f"Removing Current Radio Stack") + cp.deleteCore2RadioStack() + self.logger.info(f"Flashing Radio Stack") + cp.flashCore2(self.args.radio_address, self.args.radio) + self.logger.info(f"Complete") + return 0 + + +if __name__ == "__main__": + Main()() diff --git a/scripts/flash_core1_main_swd.sh b/scripts/flash_core1_main_swd.sh deleted file mode 100755 index 8a159df91..000000000 --- a/scripts/flash_core1_main_swd.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -x -e - -SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" - -rm "$PROJECT_DIR"/bootloader/.obj/f*/flash || true -make -C "$PROJECT_DIR"/bootloader -j9 flash - -rm "$PROJECT_DIR"/firmware/.obj/f*/flash || true -make -C "$PROJECT_DIR"/firmware -j9 flash diff --git a/scripts/flash_core2_ble_swd.sh b/scripts/flash_core2_ble_swd.sh deleted file mode 100755 index 71ea713f8..000000000 --- a/scripts/flash_core2_ble_swd.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -set -x -e - -SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -COPRO_DIR="$PROJECT_DIR/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x" - -STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_FUS_fw_for_fus_0_5_3.bin 0x080EC000 || true -STM32_Programmer_CLI -c port=swd - -STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_FUS_fw.bin 0x080EC000 || true -STM32_Programmer_CLI -c port=swd - -STM32_Programmer_CLI -c port=swd -fwdelete - -STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_BLE_Stack_full_fw.bin 0x080CA000 firstinstall=0 - -STM32_Programmer_CLI -c port=swd -ob nSWBOOT0=1 nBOOT0=1 - diff --git a/scripts/flash_otp_version_dfu.sh b/scripts/flash_otp_version_dfu.sh deleted file mode 100755 index 014f8883d..000000000 --- a/scripts/flash_otp_version_dfu.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -x -e - -if [ "$#" -ne 1 ]; then - echo "OTP file required" - exit -fi - -if [ ! -f "$1" ]; then - echo "Unable to open OTP file" - exit -fi - -STM32_Programmer_CLI -c port=usb1 -d "$1" 0x1FFF7000 - -STM32_Programmer_CLI -c port=usb1 -r8 0x1FFF7000 8 diff --git a/scripts/flash_otp_version_swd.sh b/scripts/flash_otp_version_swd.sh deleted file mode 100755 index 84e8dd715..000000000 --- a/scripts/flash_otp_version_swd.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -set -x -e - -if [ "$#" -ne 1 ]; then - echo "OTP file required" - exit -fi - -if [ ! -f "$1" ]; then - echo "Unable to open OTP file" - exit -fi - -STM32_Programmer_CLI -c port=swd -d "$1" 0x1FFF7000 - -STM32_Programmer_CLI -c port=swd -r8 0x1FFF7000 8 diff --git a/scripts/flash_wipe_swd.sh b/scripts/flash_wipe_swd.sh deleted file mode 100755 index 6eb67c879..000000000 --- a/scripts/flash_wipe_swd.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -x -e - -STM32_Programmer_CLI -c port=swd -ob RDP=0xBB - -STM32_Programmer_CLI -c port=swd -ob displ - -STM32_Programmer_CLI -c port=swd --readunprotect - -STM32_Programmer_CLI -c port=swd -ob displ diff --git a/scripts/flipper/app.py b/scripts/flipper/app.py new file mode 100644 index 000000000..eef61a230 --- /dev/null +++ b/scripts/flipper/app.py @@ -0,0 +1,47 @@ +import logging +import argparse +import sys +import os + + +class App: + def __init__(self): + # Argument Parser + self.parser = argparse.ArgumentParser() + self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") + # Logging + self.logger = logging.getLogger() + # Application specific initialization + self.init() + + def __call__(self): + self.args = self.parser.parse_args() + if "func" not in self.args: + self.parser.error("Choose something to do") + # configure log output + self.log_level = logging.DEBUG if self.args.debug else logging.INFO + self.logger.setLevel(self.log_level) + self.handler = logging.StreamHandler(sys.stdout) + self.handler.setLevel(self.log_level) + self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") + self.handler.setFormatter(self.formatter) + self.logger.addHandler(self.handler) + + # execute requested function + self.before() + return_code = self.args.func() + self.after() + if isinstance(return_code, int): + exit(return_code) + else: + self.logger.error(f"Missing return code") + exit(255) + + def init(self): + raise Exception("init() is not implemented") + + def before(self): + pass + + def after(self): + pass diff --git a/scripts/flipper/cube.py b/scripts/flipper/cube.py new file mode 100644 index 000000000..162382174 --- /dev/null +++ b/scripts/flipper/cube.py @@ -0,0 +1,91 @@ +import logging +import subprocess + + +class CubeProgrammer: + """STM32 Cube Programmer cli wrapper""" + + def __init__(self, port, params=[]): + self.port = port + self.params = params + # logging + self.logger = logging.getLogger() + + def _execute(self, args): + try: + output = subprocess.check_output( + [ + "STM32_Programmer_CLI", + "-q", + f"-c port={self.port}", + *self.params, + *args, + ] + ) + except subprocess.CalledProcessError as e: + if e.output: + print("Process output:\n", e.output.decode()) + print("Process return code:", e.returncode) + raise e + assert output + return output.decode() + + def getVersion(self): + output = self._execute(["--version"]) + + def checkOptionBytes(self, option_bytes): + output = self._execute(["-ob displ"]) + ob_correct = True + for line in output.split("\n"): + line = line.strip() + if not ":" in line: + self.logger.debug(f"Skipping line: {line}") + continue + key, data = line.split(":", 1) + key = key.strip() + data = data.strip() + if not key in option_bytes.keys(): + self.logger.debug(f"Skipping key: {key}") + continue + self.logger.debug(f"Processing key: {key} {data}") + value, comment = data.split(" ", 1) + value = value.strip() + comment = comment.strip() + if option_bytes[key][0] != value: + self.logger.error( + f"Invalid OB: {key} {value}, expected: {option_bytes[key][0]}" + ) + ob_correct = False + return ob_correct + + def setOptionBytes(self, option_bytes): + options = [] + for key, (value, attr) in option_bytes.items(): + if "w" in attr: + options.append(f"{key}={value}") + self._execute(["-ob", *options]) + return True + + def flashBin(self, address, filename): + self._execute( + [ + "-d", + filename, + f"{address}", + ] + ) + + def flashCore2(self, address, filename): + self._execute( + [ + "-fwupgrade", + filename, + f"{address}", + ] + ) + + def deleteCore2RadioStack(self): + self._execute(["-fwdelete"]) + + def resetTarget(self): + self._execute([]) diff --git a/scripts/ob.data b/scripts/ob.data index 2c2d7f6f9..ebb716d5b 100644 --- a/scripts/ob.data +++ b/scripts/ob.data @@ -25,7 +25,7 @@ SBRSA:0xA:r SBRV:0x32800:r PCROP1A_STRT:0x1FF:r PCROP1A_END:0x0:r -PCROP_RDP:0x1:r +PCROP_RDP:0x1:rw PCROP1B_STRT:0x1FF:r PCROP1B_END:0x0:r WRP1A_STRT:0xFF:r diff --git a/scripts/ob.py b/scripts/ob.py index be15a9831..903bfd567 100755 --- a/scripts/ob.py +++ b/scripts/ob.py @@ -6,12 +6,12 @@ import subprocess import sys import os +from flipper.app import App +from flipper.cube import CubeProgrammer -class Main: - def __init__(self): - # command args - self.parser = argparse.ArgumentParser() - self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") + +class Main(App): + def init(self): self.subparsers = self.parser.add_subparsers(help="sub-command help") self.parser_check = self.subparsers.add_parser( "check", help="Check Option Bytes" @@ -20,39 +20,16 @@ class Main: "--port", type=str, help="Port to connect: swd or usb1", default="swd" ) self.parser_check.set_defaults(func=self.check) + # Set command self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes") self.parser_set.add_argument( "--port", type=str, help="Port to connect: swd or usb1", default="swd" ) self.parser_set.set_defaults(func=self.set) - # logging - self.logger = logging.getLogger() # OB self.ob = {} - def __call__(self): - self.args = self.parser.parse_args() - if "func" not in self.args: - self.parser.error("Choose something to do") - # configure log output - self.log_level = logging.DEBUG if self.args.debug else logging.INFO - self.logger.setLevel(self.log_level) - self.handler = logging.StreamHandler(sys.stdout) - self.handler.setLevel(self.log_level) - self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") - self.handler.setFormatter(self.formatter) - self.logger.addHandler(self.handler) - # execute requested function - self.loadOB() - - return_code = self.args.func() - if isinstance(return_code, int): - return return_code - else: - self.logger.error(f"Forgotten return code") - return 255 - - def loadOB(self): + def before(self): self.logger.info(f"Loading Option Bytes data") file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data") file = open(file_path, "r") @@ -62,47 +39,8 @@ class Main: def check(self): self.logger.info(f"Checking Option Bytes") - try: - output = subprocess.check_output( - [ - "STM32_Programmer_CLI", - "-q", - "-c", - f"port={self.args.port}", - "-ob displ", - ] - ) - assert output - except subprocess.CalledProcessError as e: - self.logger.error(e.output.decode()) - self.logger.error(f"Failed to call STM32_Programmer_CLI") - return 127 - except Exception as e: - self.logger.error(f"Failed to call STM32_Programmer_CLI") - self.logger.exception(e) - return 126 - ob_correct = True - for line in output.decode().split("\n"): - line = line.strip() - if not ":" in line: - self.logger.debug(f"Skipping line: {line}") - continue - key, data = line.split(":", 1) - key = key.strip() - data = data.strip() - if not key in self.ob.keys(): - self.logger.debug(f"Skipping key: {key}") - continue - self.logger.debug(f"Processing key: {key} {data}") - value, comment = data.split(" ", 1) - value = value.strip() - comment = comment.strip() - if self.ob[key][0] != value: - self.logger.error( - f"Invalid OB: {key} {value}, expected: {self.ob[key][0]}" - ) - ob_correct = False - if ob_correct: + cp = CubeProgrammer(self.args.port) + if cp.checkOptionBytes(self.ob): self.logger.info(f"OB Check OK") return 0 else: @@ -111,34 +49,14 @@ class Main: def set(self): self.logger.info(f"Setting Option Bytes") - options = [] - for key, (value, attr) in self.ob.items(): - if "w" in attr: - options.append(f"{key}={value}") - try: - output = subprocess.check_output( - [ - "STM32_Programmer_CLI", - "-q", - "-c", - f"port={self.args.port}", - "-ob", - *options, - ] - ) - assert output - self.logger.info(f"Success") - except subprocess.CalledProcessError as e: - self.logger.error(e.output.decode()) - self.logger.error(f"Failed to call STM32_Programmer_CLI") - return 125 - except Exception as e: - self.logger.error(f"Failed to call STM32_Programmer_CLI") - self.logger.exception(e) - return 124 - return 0 + cp = CubeProgrammer(self.args.port) + if cp.setOptionBytes(self.ob): + self.logger.info(f"OB Set OK") + return 0 + else: + self.logger.error(f"OB Set FAIL") + return 255 if __name__ == "__main__": - return_code = Main()() - exit(return_code) + Main()() diff --git a/scripts/otp.py b/scripts/otp.py index 029c3aca2..c16f98fe6 100755 --- a/scripts/otp.py +++ b/scripts/otp.py @@ -32,12 +32,13 @@ OTP_DISPLAYS = { "mgg": 0x02, } +from flipper.app import App +from flipper.cube import CubeProgrammer -class Main: - def __init__(self): - # command args - self.parser = argparse.ArgumentParser() - self.parser.add_argument("-d", "--debug", action="store_true", help="Debug") + +class Main(App): + def init(self): + # SubParsers self.subparsers = self.parser.add_subparsers(help="sub-command help") # Generate All self.parser_generate_all = self.subparsers.add_parser( @@ -73,21 +74,6 @@ class Main: self.logger = logging.getLogger() self.timestamp = datetime.datetime.now().timestamp() - def __call__(self): - self.args = self.parser.parse_args() - if "func" not in self.args: - self.parser.error("Choose something to do") - # configure log output - self.log_level = logging.DEBUG if self.args.debug else logging.INFO - self.logger.setLevel(self.log_level) - self.handler = logging.StreamHandler(sys.stdout) - self.handler.setLevel(self.log_level) - self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s") - self.handler.setFormatter(self.formatter) - self.logger.addHandler(self.handler) - # execute requested function - self.args.func() - def _add_swd_args(self, parser): parser.add_argument( "--port", type=str, help="Port to connect: swd or usb1", default="swd" @@ -153,89 +139,90 @@ class Main: ) def generate_all(self): - self.logger.debug(f"Generating OTP") + self.logger.info(f"Generating OTP") self._process_first_args() self._process_second_args() open(f"{self.args.file}_first.bin", "wb").write(self._pack_first()) open(f"{self.args.file}_second.bin", "wb").write(self._pack_second()) + self.logger.info( + f"Generated files: {self.args.file}_first.bin and {self.args.file}_second.bin" + ) + + return 0 def flash_first(self): - self.logger.debug(f"Flashing first block of OTP") + self.logger.info(f"Flashing first block of OTP") self._process_first_args() filename = f"otp_unknown_first_{self.timestamp}.bin" - file = open(filename, "wb") - file.write(self._pack_first()) - file.close() - self._flash_bin("0x1FFF7000", filename) + try: + self.logger.info(f"Packing binary data") + file = open(filename, "wb") + file.write(self._pack_first()) + file.close() + self.logger.info(f"Flashing OTP") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x1FFF7000", filename) + cp.resetTarget() + self.logger.info(f"Flashed Successfully") + os.remove(filename) + except Exception as e: + self.logger.exception(e) + return 0 - os.remove(filename) + return 1 def flash_second(self): - self.logger.debug(f"Flashing second block of OTP") + self.logger.info(f"Flashing second block of OTP") self._process_second_args() filename = f"otp_{self.args.name}_second_{self.timestamp}.bin" - file = open(filename, "wb") - file.write(self._pack_second()) - file.close() - self._flash_bin("0x1FFF7010", filename) + try: + self.logger.info(f"Packing binary data") + file = open(filename, "wb") + file.write(self._pack_second()) + file.close() + self.logger.info(f"Flashing OTP") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x1FFF7010", filename) + cp.resetTarget() + self.logger.info(f"Flashed Successfully") + os.remove(filename) + except Exception as e: + self.logger.exception(e) + return 1 - os.remove(filename) + return 0 def flash_all(self): - self.logger.debug(f"Flashing OTP") + self.logger.info(f"Flashing OTP") self._process_first_args() self._process_second_args() filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin" - file = open(filename, "wb") - file.write(self._pack_first()) - file.write(self._pack_second()) - file.close() - self._flash_bin("0x1FFF7000", filename) - - os.remove(filename) - - def _flash_bin(self, address, filename): - self.logger.debug(f"Programming {filename} at {address}") try: - output = subprocess.check_output( - [ - "STM32_Programmer_CLI", - "-q", - "-c", - f"port={self.args.port}", - "-d", - filename, - f"{address}", - ] - ) - assert output - self.logger.info(f"Success") - except subprocess.CalledProcessError as e: - self.logger.error(e.output.decode()) - self.logger.error(f"Failed to call STM32_Programmer_CLI") - return + self.logger.info(f"Packing binary data") + file = open(filename, "wb") + file.write(self._pack_first()) + file.write(self._pack_second()) + file.close() + self.logger.info(f"Flashing OTP") + cp = CubeProgrammer(self.args.port) + cp.flashBin("0x1FFF7000", filename) + cp.resetTarget() + self.logger.info(f"Flashed Successfully") + os.remove(filename) except Exception as e: - self.logger.error(f"Failed to call STM32_Programmer_CLI") self.logger.exception(e) - return - # reboot - subprocess.check_output( - [ - "STM32_Programmer_CLI", - "-q", - "-c", - f"port={self.args.port}", - ] - ) + return 1 + + return 0 if __name__ == "__main__":