Skip to content

Commit

Permalink
Implement FW switching
Browse files Browse the repository at this point in the history
  • Loading branch information
0xchocolate committed Aug 18, 2023
1 parent 70505e7 commit fb4155d
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 24 deletions.
2 changes: 1 addition & 1 deletion esp_flasher_app.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
extern "C" {
#endif

#define ESP_FLASHER_APP_VERSION "v1.1"
#define ESP_FLASHER_APP_VERSION "v1.2"

typedef struct EspFlasherApp EspFlasherApp;

Expand Down
14 changes: 12 additions & 2 deletions esp_flasher_app_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,18 @@ typedef enum SelectedFlashOptions {
SelectedFlashPart,
SelectedFlashNvs,
SelectedFlashBootApp0,
SelectedFlashApp,
SelectedFlashAppA,
SelectedFlashAppB,
SelectedFlashCustom,
NUM_FLASH_OPTIONS
} SelectedFlashOptions;

typedef enum {
SwitchNotSet,
SwitchToFirmwareA,
SwitchToFirmwareB,
} SwitchFirmware;

struct EspFlasherApp {
Gui* gui;
ViewDispatcher* view_dispatcher;
Expand All @@ -59,13 +66,16 @@ struct EspFlasherApp {
bool reset;
bool boot;

SwitchFirmware switch_fw;

bool selected_flash_options[NUM_FLASH_OPTIONS];
int num_selected_flash_options;
char bin_file_path_boot[100];
char bin_file_path_part[100];
char bin_file_path_nvs[100];
char bin_file_path_boot_app0[100];
char bin_file_path_app[100];
char bin_file_path_app_a[100];
char bin_file_path_app_b[100];
char bin_file_path_custom[100];
FuriThread* flash_worker;
bool flash_worker_busy;
Expand Down
100 changes: 93 additions & 7 deletions esp_flasher_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,85 @@ static esp_loader_error_t _flash_file(EspFlasherApp* app, char* filepath, uint32
return ESP_LOADER_SUCCESS;
}

// This in-app FW switch "exploits" the otadata (boot_app0)
// - the first four bytes of each array are the counter and the last four bytes are just a CRC of that counter
// - the bootloader will just boot whichever app has the highest counter in the otadata partition
// so we'll just pick 1 for A, and then B will use either 0 or 2 depending on whether it's the slot in use

#define MAGIC_PAYLOAD_SIZE (32)

const uint8_t magic_payload_app_a[MAGIC_PAYLOAD_SIZE] = {0x01, 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,
0x9a, 0x98, 0x43, 0x47};

const uint8_t magic_payload_app_b_unset[MAGIC_PAYLOAD_SIZE] = {
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};

const uint8_t magic_payload_app_b_set[MAGIC_PAYLOAD_SIZE] = {
0x02, 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, 0x74, 0x37, 0xf6, 0x55};

// return true if "switching" fw selected instead of flashing new fw
// (this does not indicate success)
static bool _switch_fw(EspFlasherApp* app) {
if(app->switch_fw == SwitchNotSet) {
return false;
}

esp_loader_error_t err;
char user_msg[256];

loader_port_debug_print("Preparing to set flags for firmware A\n");
err = esp_loader_flash_start(
ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_A,
MAGIC_PAYLOAD_SIZE,
MAGIC_PAYLOAD_SIZE);
if(err != ESP_LOADER_SUCCESS) {
snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err);
loader_port_debug_print(user_msg);
return true;
}

loader_port_debug_print("Setting flags for firmware A\n");
const uint8_t* which_payload_app_a = magic_payload_app_a;
err = esp_loader_flash_write((void*)which_payload_app_a, MAGIC_PAYLOAD_SIZE);
if(err != ESP_LOADER_SUCCESS) {
snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err);
loader_port_debug_print(user_msg);
return true;
}

loader_port_debug_print("Preparing to set flags for firmware B\n");
err = esp_loader_flash_start(
ESP_ADDR_BOOT_APP0 + ESP_ADDR_OTADATA_OFFSET_APP_B,
MAGIC_PAYLOAD_SIZE,
MAGIC_PAYLOAD_SIZE);
if(err != ESP_LOADER_SUCCESS) {
snprintf(user_msg, sizeof(user_msg), "Erasing flash failed with error %d\n", err);
loader_port_debug_print(user_msg);
return true;
}

loader_port_debug_print("Setting flags for firmware B\n");
const uint8_t* which_payload_app_b =
(app->switch_fw == SwitchToFirmwareB ? magic_payload_app_b_set :
magic_payload_app_b_unset);
err = esp_loader_flash_write((void*)which_payload_app_b, MAGIC_PAYLOAD_SIZE);
if(err != ESP_LOADER_SUCCESS) {
snprintf(user_msg, sizeof(user_msg), "Packet could not be written! Error: %u\n", err);
loader_port_debug_print(user_msg);
return true;
}

loader_port_debug_print("Finished programming\n");
return true;
}

typedef struct {
SelectedFlashOptions selected;
const char* description;
Expand All @@ -85,7 +164,7 @@ static void _flash_all_files(EspFlasherApp* app) {
esp_loader_error_t err;
const int num_steps = app->num_selected_flash_options;

#define NUM_FLASH_ITEMS 6
#define NUM_FLASH_ITEMS 7
FlashItem items[NUM_FLASH_ITEMS] = {
{SelectedFlashBoot,
"bootloader",
Expand All @@ -94,7 +173,8 @@ static void _flash_all_files(EspFlasherApp* app) {
{SelectedFlashPart, "partition table", app->bin_file_path_part, ESP_ADDR_PART},
{SelectedFlashNvs, "NVS", app->bin_file_path_nvs, ESP_ADDR_NVS},
{SelectedFlashBootApp0, "boot_app0", app->bin_file_path_boot_app0, ESP_ADDR_BOOT_APP0},
{SelectedFlashApp, "firmware", app->bin_file_path_app, ESP_ADDR_APP},
{SelectedFlashAppA, "firmware A", app->bin_file_path_app_a, ESP_ADDR_APP_A},
{SelectedFlashAppB, "firmware B", app->bin_file_path_app_b, ESP_ADDR_APP_B},
{SelectedFlashCustom, "custom data", app->bin_file_path_custom, 0x0},
/* if you add more entries, update NUM_FLASH_ITEMS above! */
};
Expand Down Expand Up @@ -167,12 +247,16 @@ static int32_t esp_flasher_flash_bin(void* context) {

if(!err) {
loader_port_debug_print("Connected\n");
_flash_all_files(app);
if(!_switch_fw(app)) {
_flash_all_files(app);
}
app->switch_fw = SwitchNotSet;
#if 0
loader_port_debug_print("Restoring transmission rate\n");
furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
#endif
loader_port_debug_print("Done flashing. Please reset the board manually if it doesn't auto-reset.\n");
loader_port_debug_print(
"Done flashing. Please reset the board manually if it doesn't auto-reset.\n");

// auto-reset for supported boards
loader_port_reset_target();
Expand Down Expand Up @@ -222,10 +306,10 @@ static int32_t esp_flasher_reset(void* context) {
_setRTS(false);
_initRTS();

if (app->reset) {
if(app->reset) {
loader_port_debug_print("Resetting board\n");
loader_port_reset_target();
} else if (app->boot) {
} else if(app->boot) {
loader_port_debug_print("Entering bootloader\n");
loader_port_enter_bootloader();
}
Expand All @@ -245,7 +329,7 @@ void esp_flasher_worker_start_thread(EspFlasherApp* app) {
furi_thread_set_name(app->flash_worker, "EspFlasherFlashWorker");
furi_thread_set_stack_size(app->flash_worker, 2048);
furi_thread_set_context(app->flash_worker, app);
if (app->reset || app->boot) {
if(app->reset || app->boot) {
furi_thread_set_callback(app->flash_worker, esp_flasher_reset);
} else {
furi_thread_set_callback(app->flash_worker, esp_flasher_flash_bin);
Expand Down Expand Up @@ -280,6 +364,8 @@ void loader_port_reset_target(void) {
}

void loader_port_enter_bootloader(void) {
// adapted from custom usb-jtag-serial reset in esptool
// (works on official wifi dev board)
_setDTR(true);
loader_port_delay_ms(SERIAL_FLASHER_RESET_HOLD_TIME_MS);
_setRTS(true);
Expand Down
6 changes: 5 additions & 1 deletion esp_flasher_worker.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
#define ESP_ADDR_PART 0x8000
#define ESP_ADDR_NVS 0x9000
#define ESP_ADDR_BOOT_APP0 0xE000
#define ESP_ADDR_APP 0x10000
#define ESP_ADDR_APP_A 0x10000
#define ESP_ADDR_APP_B 0x150000

#define ESP_ADDR_OTADATA_OFFSET_APP_A 0x0
#define ESP_ADDR_OTADATA_OFFSET_APP_B 0x1000

void esp_flasher_worker_start_thread(EspFlasherApp* app);
void esp_flasher_worker_stop_thread(EspFlasherApp* app);
Expand Down
52 changes: 39 additions & 13 deletions scenes/esp_flasher_scene_browse.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ enum SubmenuIndex {
SubmenuIndexPart,
SubmenuIndexNvs,
SubmenuIndexBootApp0,
SubmenuIndexApp,
SubmenuIndexAppA,
SubmenuIndexAppB,
SubmenuIndexCustom,
SubmenuIndexFlash,
};
Expand Down Expand Up @@ -97,19 +98,35 @@ static void esp_flasher_scene_browse_callback(void* context, uint32_t index) {
}
view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu);
break;
case SubmenuIndexApp:
app->selected_flash_options[SelectedFlashApp] =
!app->selected_flash_options[SelectedFlashApp];
case SubmenuIndexAppA:
app->selected_flash_options[SelectedFlashAppA] =
!app->selected_flash_options[SelectedFlashAppA];
if(dialog_file_browser_show(
app->dialogs, selected_filepath, predefined_filepath, &browser_options)) {
strncpy(
app->bin_file_path_app,
app->bin_file_path_app_a,
furi_string_get_cstr(selected_filepath),
sizeof(app->bin_file_path_app));
sizeof(app->bin_file_path_app_a));
}
if(app->bin_file_path_app[0] == '\0') {
if(app->bin_file_path_app_a[0] == '\0') {
// if user didn't select a file, leave unselected
app->selected_flash_options[SelectedFlashApp] = false;
app->selected_flash_options[SelectedFlashAppA] = false;
}
view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu);
break;
case SubmenuIndexAppB:
app->selected_flash_options[SelectedFlashAppB] =
!app->selected_flash_options[SelectedFlashAppB];
if(dialog_file_browser_show(
app->dialogs, selected_filepath, predefined_filepath, &browser_options)) {
strncpy(
app->bin_file_path_app_b,
furi_string_get_cstr(selected_filepath),
sizeof(app->bin_file_path_app_b));
}
if(app->bin_file_path_app_b[0] == '\0') {
// if user didn't select a file, leave unselected
app->selected_flash_options[SelectedFlashAppB] = false;
}
view_dispatcher_send_custom_event(app->view_dispatcher, EspFlasherEventRefreshSubmenu);
break;
Expand Down Expand Up @@ -157,7 +174,8 @@ static void esp_flasher_scene_browse_callback(void* context, uint32_t index) {
#define STR_PART "Part Table (" TOSTRING(ESP_ADDR_PART) ")"
#define STR_NVS "NVS (" TOSTRING(ESP_ADDR_NVS) ")"
#define STR_BOOT_APP0 "boot_app0 (" TOSTRING(ESP_ADDR_BOOT_APP0) ")"
#define STR_APP "Firmware (" TOSTRING(ESP_ADDR_APP) ")"
#define STR_APP_A "FirmwareA(" TOSTRING(ESP_ADDR_APP_A) ")"
#define STR_APP_B "FirmwareB(" TOSTRING(ESP_ADDR_APP_B) ")"
#define STR_CUSTOM "Custom"
#define STR_FLASH_S3 "[>] FLASH (ESP32-S3)"
#define STR_FLASH "[>] FLASH"
Expand Down Expand Up @@ -213,9 +231,16 @@ static void _refresh_submenu(EspFlasherApp* app) {
app);
submenu_add_item(
submenu,
app->selected_flash_options[SelectedFlashApp] ? STR_SELECT " " STR_APP :
STR_UNSELECT " " STR_APP,
SubmenuIndexApp,
app->selected_flash_options[SelectedFlashAppA] ? STR_SELECT " " STR_APP_A :
STR_UNSELECT " " STR_APP_A,
SubmenuIndexAppA,
esp_flasher_scene_browse_callback,
app);
submenu_add_item(
submenu,
app->selected_flash_options[SelectedFlashAppB] ? STR_SELECT " " STR_APP_B :
STR_UNSELECT " " STR_APP_B,
SubmenuIndexAppB,
esp_flasher_scene_browse_callback,
app);
// TODO: custom addr
Expand All @@ -241,7 +266,8 @@ void esp_flasher_scene_browse_on_enter(void* context) {
app->bin_file_path_part[0] = '\0';
app->bin_file_path_nvs[0] = '\0';
app->bin_file_path_boot_app0[0] = '\0';
app->bin_file_path_app[0] = '\0';
app->bin_file_path_app_a[0] = '\0';
app->bin_file_path_app_b[0] = '\0';
app->bin_file_path_custom[0] = '\0';

_refresh_submenu(app);
Expand Down
22 changes: 22 additions & 0 deletions scenes/esp_flasher_scene_start.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

enum SubmenuIndex {
SubmenuIndexEspFlasherFlash,
SubmenuIndexEspFlasherSwitchA,
SubmenuIndexEspFlasherSwitchB,
SubmenuIndexEspFlasherAbout,
SubmenuIndexEspFlasherReset,
SubmenuIndexEspFlasherBootloader,
Expand All @@ -25,6 +27,18 @@ void esp_flasher_scene_start_on_enter(void* context) {
SubmenuIndexEspFlasherFlash,
esp_flasher_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Switch to Firmware A",
SubmenuIndexEspFlasherSwitchA,
esp_flasher_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Switch to Firmware B",
SubmenuIndexEspFlasherSwitchB,
esp_flasher_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Reset Board",
Expand Down Expand Up @@ -62,6 +76,14 @@ bool esp_flasher_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexEspFlasherFlash) {
scene_manager_next_scene(app->scene_manager, EspFlasherSceneBrowse);
consumed = true;
} else if(event.event == SubmenuIndexEspFlasherSwitchA) {
app->switch_fw = SwitchToFirmwareA;
scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput);
consumed = true;
} else if(event.event == SubmenuIndexEspFlasherSwitchB) {
app->switch_fw = SwitchToFirmwareB;
scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput);
consumed = true;
} else if(event.event == SubmenuIndexEspFlasherReset) {
app->reset = true;
scene_manager_next_scene(app->scene_manager, EspFlasherSceneConsoleOutput);
Expand Down

0 comments on commit fb4155d

Please sign in to comment.