From f2324e4d1ca57654e66e580c4ea14da145cc7e4c Mon Sep 17 00:00:00 2001 From: hedger Date: Fri, 14 Jul 2023 16:45:16 +0300 Subject: [PATCH] [FL-3377] Update error code descriptions (#2875) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * updater: added update error code descriptions * updater: separate ram/flash messages * updater: extra pre-update checks * updater: fixed string comparison * updater: Additional logging Co-authored-by: あく --- .../system/updater/util/update_task.c | 192 +++++++++++++++++- .../system/updater/util/update_task_i.h | 5 + .../updater/util/update_task_worker_backup.c | 5 - .../updater/util/update_task_worker_flasher.c | 26 ++- .../system/updater/views/updater_main.c | 15 +- documentation/OTA.md | 7 +- lib/update_util/update_operation.c | 21 +- lib/update_util/update_operation.h | 3 +- 8 files changed, 238 insertions(+), 36 deletions(-) diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index 708d10ce0..74d752b43 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -19,7 +19,7 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageRadioErase] = "Uninstalling radio FW", [UpdateTaskStageRadioWrite] = "Writing radio FW", [UpdateTaskStageRadioInstall] = "Installing radio FW", - [UpdateTaskStageRadioBusy] = "Radio is updating", + [UpdateTaskStageRadioBusy] = "Core 2 busy", [UpdateTaskStageOBValidation] = "Validating opt. bytes", [UpdateTaskStageLfsBackup] = "Backing up LFS", [UpdateTaskStageLfsRestore] = "Restoring LFS", @@ -30,6 +30,191 @@ static const char* update_task_stage_descr[] = { [UpdateTaskStageOBError] = "OB, report", }; +static const struct { + UpdateTaskStage stage; + uint8_t percent_min, percent_max; + const char* descr; +} update_task_error_detail[] = { + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 0, + .percent_max = 13, + .descr = "Wrong Updater HW", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 14, + .percent_max = 20, + .descr = "Manifest pointer error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 21, + .percent_max = 30, + .descr = "Manifest load error", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 31, + .percent_max = 40, + .descr = "Wrong package version", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 41, + .percent_max = 50, + .descr = "HW Target mismatch", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 51, + .percent_max = 60, + .descr = "No DFU file", + }, + { + .stage = UpdateTaskStageReadManifest, + .percent_min = 61, + .percent_max = 80, + .descr = "No Radio file", + }, +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsBackup, + .percent_min = 0, + .percent_max = 100, + .descr = "FS R/W error", + }, +#else + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 0, + .percent_max = 98, + .descr = "FS Read error", + }, + { + .stage = UpdateTaskStageRadioImageValidate, + .percent_min = 99, + .percent_max = 100, + .descr = "CRC mismatch", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 0, + .percent_max = 30, + .descr = "Stack remove: cmd error", + }, + { + .stage = UpdateTaskStageRadioErase, + .percent_min = 31, + .percent_max = 100, + .descr = "Stack remove: wait failed", + }, + { + .stage = UpdateTaskStageRadioWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Stack write: error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 0, + .percent_max = 10, + .descr = "Stack install: cmd error", + }, + { + .stage = UpdateTaskStageRadioInstall, + .percent_min = 11, + .percent_max = 100, + .descr = "Stack install: wait failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 0, + .percent_max = 10, + .descr = "Failed to start C2", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 11, + .percent_max = 20, + .descr = "C2 FUS swich failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 21, + .percent_max = 30, + .descr = "FUS operation failed", + }, + { + .stage = UpdateTaskStageRadioBusy, + .percent_min = 31, + .percent_max = 100, + .descr = "C2 Stach switch failed", + }, + { + .stage = UpdateTaskStageOBValidation, + .percent_min = 0, + .percent_max = 100, + .descr = "Uncorr. value mismatch", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 0, + .percent_max = 1, + .descr = "Failed to open DFU file", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 1, + .percent_max = 97, + .descr = "DFU file read error", + }, + { + .stage = UpdateTaskStageValidateDFUImage, + .percent_min = 98, + .percent_max = 100, + .descr = "DFU file CRC mismatch", + }, + { + .stage = UpdateTaskStageFlashWrite, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash write error", + }, + { + .stage = UpdateTaskStageFlashValidate, + .percent_min = 0, + .percent_max = 100, + .descr = "Flash compare error", + }, +#endif +#ifndef FURI_RAM_EXEC + { + .stage = UpdateTaskStageLfsRestore, + .percent_min = 0, + .percent_max = 100, + .descr = "LFS I/O error", + }, + { + .stage = UpdateTaskStageResourcesUpdate, + .percent_min = 0, + .percent_max = 100, + .descr = "SD card I/O error", + }, +#endif +}; + +static const char* update_task_get_error_message(UpdateTaskStage stage, uint8_t percent) { + for(size_t i = 0; i < COUNT_OF(update_task_error_detail); i++) { + if(update_task_error_detail[i].stage == stage && + percent >= update_task_error_detail[i].percent_min && + percent <= update_task_error_detail[i].percent_max) { + return update_task_error_detail[i].descr; + } + } + return "Unknown error"; +} + typedef struct { UpdateTaskStageGroup group; uint8_t weight; @@ -111,8 +296,9 @@ void update_task_set_progress(UpdateTask* update_task, UpdateTaskStage stage, ui if(stage >= UpdateTaskStageError) { furi_string_printf( update_task->state.status, - "%s #[%d-%d]", - update_task_stage_descr[stage], + "%s\n#[%d-%d]", + update_task_get_error_message( + update_task->state.stage, update_task->state.stage_progress), update_task->state.stage, update_task->state.stage_progress); } else { diff --git a/applications/system/updater/util/update_task_i.h b/applications/system/updater/util/update_task_i.h index 0dbeca5f4..1b664e57e 100644 --- a/applications/system/updater/util/update_task_i.h +++ b/applications/system/updater/util/update_task_i.h @@ -24,3 +24,8 @@ bool update_task_open_file(UpdateTask* update_task, FuriString* filename); int32_t update_task_worker_flash_writer(void* context); int32_t update_task_worker_backup_restore(void* context); + +#define CHECK_RESULT(x) \ + if(!(x)) { \ + break; \ + } diff --git a/applications/system/updater/util/update_task_worker_backup.c b/applications/system/updater/util/update_task_worker_backup.c index f2c33c2ed..ef4276fac 100644 --- a/applications/system/updater/util/update_task_worker_backup.c +++ b/applications/system/updater/util/update_task_worker_backup.c @@ -15,11 +15,6 @@ #define TAG "UpdWorkerBackup" -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } - static bool update_task_pre_update(UpdateTask* update_task) { bool success = false; FuriString* backup_file_path; diff --git a/applications/system/updater/util/update_task_worker_flasher.c b/applications/system/updater/util/update_task_worker_flasher.c index 5d2477464..d6dc13e37 100644 --- a/applications/system/updater/util/update_task_worker_flasher.c +++ b/applications/system/updater/util/update_task_worker_flasher.c @@ -13,11 +13,6 @@ #define TAG "UpdWorkerRAM" -#define CHECK_RESULT(x) \ - if(!(x)) { \ - break; \ - } - #define STM_DFU_VENDOR_ID 0x0483 #define STM_DFU_PRODUCT_ID 0xDF11 /* Written into DFU file by build pipeline */ @@ -137,7 +132,7 @@ static bool update_task_write_stack_data(UpdateTask* update_task) { } static void update_task_wait_for_restart(UpdateTask* update_task) { - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 70); furi_delay_ms(C2_MODE_SWITCH_TIMEOUT); furi_crash("C2 timeout"); } @@ -153,12 +148,12 @@ static bool update_task_write_stack(UpdateTask* update_task) { manifest->radio_crc); CHECK_RESULT(update_task_write_stack_data(update_task)); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 0); + update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 10); CHECK_RESULT( ble_glue_fus_stack_install(manifest->radio_address, 0) != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioInstall, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } while(false); @@ -170,9 +165,9 @@ static bool update_task_remove_stack(UpdateTask* update_task) { FURI_LOG_W(TAG, "Removing stack"); update_task_set_progress(update_task, UpdateTaskStageRadioErase, 30); CHECK_RESULT(ble_glue_fus_stack_delete() != BleGlueCommandResultError); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 80); + update_task_set_progress(update_task, UpdateTaskStageProgress, 80); CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); - update_task_set_progress(update_task, UpdateTaskStageRadioErase, 100); + update_task_set_progress(update_task, UpdateTaskStageProgress, 100); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } while(false); @@ -180,6 +175,7 @@ static bool update_task_remove_stack(UpdateTask* update_task) { } static bool update_task_manage_radiostack(UpdateTask* update_task) { + update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); bool success = false; do { CHECK_RESULT(ble_glue_wait_for_c2_start(FURI_HAL_BT_C2_START_TIMEOUT)); @@ -208,15 +204,17 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* Version or type mismatch. Let's boot to FUS and start updating. */ FURI_LOG_W(TAG, "Restarting to FUS"); furi_hal_rtc_set_flag(FuriHalRtcFlagC2Update); + update_task_set_progress(update_task, UpdateTaskStageProgress, 20); + CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeFUS)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); } } else if(c2_state->mode == BleGlueC2ModeFUS) { /* OK, we're in FUS mode. */ - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 10); FURI_LOG_W(TAG, "Waiting for FUS to settle"); - ble_glue_fus_wait_operation(); + update_task_set_progress(update_task, UpdateTaskStageProgress, 30); + CHECK_RESULT(ble_glue_fus_wait_operation() == BleGlueCommandResultOK); if(stack_version_match) { /* We can't check StackType with FUS, but partial version matches */ if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagC2Update)) { @@ -230,7 +228,7 @@ static bool update_task_manage_radiostack(UpdateTask* update_task) { /* We might just had the stack installed. * Let's start it up to check its version */ FURI_LOG_W(TAG, "Starting stack to check full version"); - update_task_set_progress(update_task, UpdateTaskStageRadioBusy, 40); + update_task_set_progress(update_task, UpdateTaskStageProgress, 50); CHECK_RESULT(furi_hal_bt_ensure_c2_mode(BleGlueC2ModeStack)); /* ...system will restart here. */ update_task_wait_for_restart(update_task); diff --git a/applications/system/updater/views/updater_main.c b/applications/system/updater/views/updater_main.c index 1199cc882..d32d51b7c 100644 --- a/applications/system/updater/views/updater_main.c +++ b/applications/system/updater/views/updater_main.c @@ -81,16 +81,17 @@ static void updater_main_draw_callback(Canvas* canvas, void* _model) { canvas_set_font(canvas, FontPrimary); if(model->failed) { - canvas_draw_str_aligned(canvas, 42, 16, AlignLeft, AlignTop, "Update Failed!"); + canvas_draw_icon(canvas, 2, 22, &I_Warning_30x23); + canvas_draw_str_aligned(canvas, 40, 9, AlignLeft, AlignTop, "Update Failed!"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str_aligned( - canvas, 42, 32, AlignLeft, AlignTop, furi_string_get_cstr(model->status)); - canvas_draw_icon(canvas, 7, 16, &I_Warning_30x23); + elements_multiline_text_aligned( + canvas, 75, 26, AlignCenter, AlignTop, furi_string_get_cstr(model->status)); + canvas_draw_str_aligned( - canvas, 18, 51, AlignLeft, AlignTop, "to retry, hold to abort"); - canvas_draw_icon(canvas, 7, 50, &I_Ok_btn_9x9); - canvas_draw_icon(canvas, 75, 51, &I_Pin_back_arrow_10x8); + canvas, 18, 55, AlignLeft, AlignTop, "to retry, hold to abort"); + canvas_draw_icon(canvas, 7, 54, &I_Ok_btn_9x9); + canvas_draw_icon(canvas, 75, 55, &I_Pin_back_arrow_10x8); } else { canvas_draw_str_aligned(canvas, 55, 14, AlignLeft, AlignTop, "UPDATING"); canvas_set_font(canvas, FontSecondary); diff --git a/documentation/OTA.md b/documentation/OTA.md index 799548f4d..ed75560cf 100644 --- a/documentation/OTA.md +++ b/documentation/OTA.md @@ -87,9 +87,12 @@ Even if something goes wrong, updater allows you to retry failed operations and | Uninstalling radio FW | **4** | **0** | SHCI Delete command error | | | | **80** | Error awaiting command status | | Writing radio FW | **5** | **0-100** | Block read/write error | -| Installing radio FW | **6** | **0** | SHCI Install command error | +| Installing radio FW | **6** | **10** | SHCI Install command error | | | | **80** | Error awaiting command status | -| Radio is updating | **7** | **10** | Error waiting for operation completion | +| Core2 is busy | **7** | **10** | Couldn't start C2 | +| | | **20** | Failed to switch C2 to FUS mode | +| | | **30** | Error in FUS operation | +| | | **50** | Failed to switch C2 to stack mode | | Validating opt. bytes | **8** | **yy** | Option byte code | | Checking DFU file | **9** | **0** | Error opening DFU file | | | | **1-98** | Error reading DFU file | diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index 6e05b0233..0cecfc016 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -20,7 +20,8 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultManifestInvalid] = "Invalid manifest data", [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", - [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCreateError] = "Failed to create update pointer file", + [UpdatePrepareResultManifestPointerCheckError] = "Update pointer file error (corrupted FS?)", [UpdatePrepareResultTargetMismatch] = "Hardware target mismatch", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", [UpdatePrepareResultIntFull] = "Need more free space in internal storage", @@ -142,8 +143,8 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { File* file = storage_file_alloc(storage); uint64_t free_int_space; - FuriString* stage_path; - stage_path = furi_string_alloc(); + FuriString* stage_path = furi_string_alloc(); + FuriString* manifest_path_check = furi_string_alloc(); do { if((storage_common_fs_info(storage, STORAGE_INT_PATH_PREFIX, NULL, &free_int_space) != FSE_OK) || @@ -188,7 +189,18 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } if(!update_operation_persist_manifest_path(storage, manifest_file_path)) { - result = UpdatePrepareResultManifestPointerError; + result = UpdatePrepareResultManifestPointerCreateError; + break; + } + + if(!update_operation_get_current_package_manifest_path(storage, manifest_path_check) || + (furi_string_cmpi_str(manifest_path_check, manifest_file_path) != 0)) { + FURI_LOG_E( + "update", + "Manifest pointer check failed: '%s' != '%s'", + furi_string_get_cstr(manifest_path_check), + manifest_file_path); + result = UpdatePrepareResultManifestPointerCheckError; break; } @@ -197,6 +209,7 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { } while(false); furi_string_free(stage_path); + furi_string_free(manifest_path_check); storage_file_free(file); update_manifest_free(manifest); diff --git a/lib/update_util/update_operation.h b/lib/update_util/update_operation.h index 65abf8e15..8e36b5a13 100644 --- a/lib/update_util/update_operation.h +++ b/lib/update_util/update_operation.h @@ -28,7 +28,8 @@ typedef enum { UpdatePrepareResultManifestInvalid, UpdatePrepareResultStageMissing, UpdatePrepareResultStageIntegrityError, - UpdatePrepareResultManifestPointerError, + UpdatePrepareResultManifestPointerCreateError, + UpdatePrepareResultManifestPointerCheckError, UpdatePrepareResultTargetMismatch, UpdatePrepareResultOutdatedManifestVersion, UpdatePrepareResultIntFull,