| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file error_handling.cpp | |||
| 3 | * | |||
| 4 | * @date Apr 1, 2014 | |||
| 5 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 6 | */ | |||
| 7 | ||||
| 8 | #include "pch.h" | |||
| 9 | #include "rusefi/efistringutil.h" | |||
| 10 | #include "os_util.h" | |||
| 11 | #include "backup_ram.h" | |||
| 12 | #include "error_handling_led.h" | |||
| 13 | #include "log_hard_fault.h" | |||
| 14 | #include "rusefi/critical_error.h" | |||
| 15 | #include "rusefi/efistring.h" | |||
| 16 | ||||
| 17 | #if EFI_USE_OPENBLT | |||
| 18 | /* communication with OpenBLT that is plain C, not to modify external file */ | |||
| 19 | extern "C" { | |||
| 20 | #include "openblt/shared_params.h" | |||
| 21 | }; | |||
| 22 | #endif | |||
| 23 | ||||
| 24 | using namespace rusefi::stringutil; | |||
| 25 | ||||
| 26 | /** | |||
| 27 | * Executes the BKPT instruction that causes the debugger to stop. | |||
| 28 | */ | |||
| 29 | #define bkpt() __asm volatile("BKPT #0\n") | |||
| 30 | ||||
| 31 | static critical_msg_t warningBuffer; | |||
| 32 | static critical_msg_t criticalErrorMessageBuffer; | |||
| 33 | static critical_msg_t configErrorMessageBuffer; // recoverable configuration error, non-critical | |||
| 34 | ||||
| 35 | bool hasCriticalFirmwareErrorFlag = false; | |||
| 36 | /** | |||
| 37 | * not critical error: TS would display text error message until clearConfigErrorMessage() is invoked | |||
| 38 | */ | |||
| 39 | static bool hasConfigErrorFlag = false; | |||
| 40 | static bool hasReportFile = false; | |||
| 41 | ||||
| 42 | // todo: revisit very questionable code! | |||
| 43 | // todo: reuse hasCriticalFirmwareErrorFlag? something? | |||
| 44 | bool isInHardFaultHandler = false; | |||
| 45 | ||||
| 46 | const char *dbg_panic_file; | |||
| 47 | int dbg_panic_line; | |||
| 48 | ||||
| 49 | // todo: need vararg version of 'firmwareError' to make this method vararg? | |||
| 50 | ✗ | void efiCriticalError(const char *message) { | ||
| 51 | ✗ | criticalError(message); | ||
| 52 | ✗ | } | ||
| 53 | ||||
| 54 | ✗ | const char* getCriticalErrorMessage() { | ||
| 55 | ✗ | return criticalErrorMessageBuffer; | ||
| 56 | } | |||
| 57 | ||||
| 58 | 531078 | bool hasConfigError() { | ||
| 59 | 531078 | return hasConfigErrorFlag; | ||
| 60 | } | |||
| 61 | ||||
| 62 | ✗ | void clearConfigErrorMessage() { | ||
| 63 | ✗ | hasConfigErrorFlag = false; | ||
| 64 | ✗ | } | ||
| 65 | ||||
| 66 | 531078 | bool hasErrorReportFile() { | ||
| 67 | 531078 | return hasReportFile; | ||
| 68 | } | |||
| 69 | ||||
| 70 | #if EFI_PROD_CODE | |||
| 71 | ||||
| 72 | #if EFI_BACKUP_SRAM | |||
| 73 | static backupErrorState lastBootError; | |||
| 74 | static uint32_t bootCount = 0; | |||
| 75 | #endif // EFI_BACKUP_SRAM | |||
| 76 | ||||
| 77 | void errorHandlerInit() { | |||
| 78 | #if EFI_BACKUP_SRAM | |||
| 79 | /* copy error state from backup RAM and clear it in backup RAM. | |||
| 80 | * so few users can access previous error state and we should not care about who sohuld clear backup ram. */ | |||
| 81 | auto sramState = getBackupSram(); | |||
| 82 | memcpy(&lastBootError, &sramState->err, sizeof(backupErrorState)); | |||
| 83 | memset(&sramState->err, 0x00, sizeof(sramState->err)); | |||
| 84 | // Reset cookie so we don't report it again. | |||
| 85 | sramState->err.Cookie = ErrorCookie::None; | |||
| 86 | ||||
| 87 | // Cookie can be some random value at first power on | |||
| 88 | // reset to None to avoid generating 'Unknown' fail report | |||
| 89 | if ((lastBootError.Cookie != ErrorCookie::FirmwareError) && | |||
| 90 | (lastBootError.Cookie != ErrorCookie::HardFault) && | |||
| 91 | (lastBootError.Cookie != ErrorCookie::ChibiOsPanic)) { | |||
| 92 | lastBootError.Cookie = ErrorCookie::None; | |||
| 93 | } | |||
| 94 | ||||
| 95 | //bootcount | |||
| 96 | if (sramState->BootCountCookie != 0xdeadbeef) { | |||
| 97 | sramState->BootCountCookie = 0xdeadbeef; | |||
| 98 | sramState->BootCount = 0; | |||
| 99 | } | |||
| 100 | // save current bootcounter | |||
| 101 | bootCount = sramState->BootCount; | |||
| 102 | ||||
| 103 | sramState->BootCount++; | |||
| 104 | ||||
| 105 | if (engineConfiguration->rethrowHardFault) { | |||
| 106 | backupErrorState *err = &lastBootError; | |||
| 107 | if (err->Cookie == ErrorCookie::HardFault) { | |||
| 108 | criticalError("Last boot had hard fault type: %lx addr: %lx CSFR: %lx", | |||
| 109 | err->FaultType, err->FaultAddress, err->Csfr); | |||
| 110 | } | |||
| 111 | } | |||
| 112 | ||||
| 113 | Reset_Cause_t cause = getMCUResetCause(); | |||
| 114 | // if reset by watchdog, signal a fatal error | |||
| 115 | if ((cause == Reset_Cause_IWatchdog) || (cause == Reset_Cause_WWatchdog)) { | |||
| 116 | firmwareError(ObdCode::OBD_PCM_Processor_Fault, "Watchdog Reset detected! Check SD card for report file."); | |||
| 117 | } | |||
| 118 | #endif // EFI_PROD_CODE | |||
| 119 | ||||
| 120 | // see https://github.com/rusefi/rusefi/wiki/Resilience | |||
| 121 | addConsoleAction("chibi_fault", [](){ chDbgCheck(0); } ); | |||
| 122 | addConsoleAction("soft_fault", [](){ firmwareError(ObdCode::RUNTIME_CRITICAL_TEST_ERROR, "firmwareError: %d", getRusEfiVersion()); }); | |||
| 123 | addConsoleAction("hard_fault", [](){ causeHardFault(); } ); | |||
| 124 | } | |||
| 125 | ||||
| 126 | bool errorHandlerIsStartFromError() { | |||
| 127 | #if EFI_BACKUP_SRAM | |||
| 128 | return (lastBootError.Cookie != ErrorCookie::None); | |||
| 129 | #else | |||
| 130 | return 0; | |||
| 131 | #endif | |||
| 132 | } | |||
| 133 | ||||
| 134 | const char *errorCookieToName(ErrorCookie cookie) | |||
| 135 | { | |||
| 136 | switch (cookie) { | |||
| 137 | case ErrorCookie::None: | |||
| 138 | return "No error"; | |||
| 139 | case ErrorCookie::FirmwareError: | |||
| 140 | return "firmware"; | |||
| 141 | case ErrorCookie::HardFault: | |||
| 142 | return "HardFault"; | |||
| 143 | case ErrorCookie::ChibiOsPanic: | |||
| 144 | return "ChibiOS panic"; | |||
| 145 | } | |||
| 146 | ||||
| 147 | return "Unknown"; | |||
| 148 | } | |||
| 149 | ||||
| 150 | #define printResetReason() \ | |||
| 151 | PRINT("Reset Cause: %s", getMCUResetCause(getMCUResetCause())) | |||
| 152 | ||||
| 153 | #if EFI_USE_OPENBLT | |||
| 154 | #define printWdResetCounter() \ | |||
| 155 | do { \ | |||
| 156 | uint8_t wd_counter = 0; \ | |||
| 157 | SharedParamsReadByIndex(1, &wd_counter); \ | |||
| 158 | PRINT("WD resets: %u", (unsigned int)wd_counter); \ | |||
| 159 | } while (0) | |||
| 160 | #else | |||
| 161 | #define printWdResetCounter() \ | |||
| 162 | do {} while(0) | |||
| 163 | #endif | |||
| 164 | ||||
| 165 | ||||
| 166 | #define printErrorState() \ | |||
| 167 | do { \ | |||
| 168 | PRINT("Power cycle count: %lu", bootCount); \ | |||
| 169 | \ | |||
| 170 | if (cookie == ErrorCookie::None) { \ | |||
| 171 | break; \ | |||
| 172 | } \ | |||
| 173 | \ | |||
| 174 | PRINT("Last error type %s", errorCookieToName(err->Cookie)); \ | |||
| 175 | \ | |||
| 176 | switch (cookie) { \ | |||
| 177 | case ErrorCookie::FirmwareError: \ | |||
| 178 | { \ | |||
| 179 | PRINT("%s", err->msg); \ | |||
| 180 | } \ | |||
| 181 | break; \ | |||
| 182 | case ErrorCookie::HardFault: \ | |||
| 183 | { \ | |||
| 184 | PRINT("type: 0x%08lx addr: 0x%08lx CSFR: 0x%08lx", \ | |||
| 185 | err->FaultType, err->FaultAddress, err->Csfr); \ | |||
| 186 | \ | |||
| 187 | auto ctx = &err->FaultCtx; \ | |||
| 188 | PRINT("r0 0x%08lx", ctx->r0); \ | |||
| 189 | PRINT("r1 0x%08lx", ctx->r1); \ | |||
| 190 | PRINT("r2 0x%08lx", ctx->r2); \ | |||
| 191 | PRINT("r3 0x%08lx", ctx->r3); \ | |||
| 192 | PRINT("r12 0x%08lx", ctx->r12); \ | |||
| 193 | PRINT("lr (thread) 0x%08lx", ctx->lr_thd); \ | |||
| 194 | PRINT("pc 0x%08lx", ctx->pc); \ | |||
| 195 | PRINT("xpsr 0x%08lx", ctx->xpsr); \ | |||
| 196 | \ | |||
| 197 | /* FPU registers - not very useful for debug */ \ | |||
| 198 | if (0) { \ | |||
| 199 | /* Print rest the context as a sequence of uintptr */ \ | |||
| 200 | uintptr_t* data = reinterpret_cast<uintptr_t*>(&err->FaultCtx); \ | |||
| 201 | for (size_t i = 8; i < sizeof(port_extctx) / sizeof(uintptr_t); i++) { \ | |||
| 202 | PRINT("Fault ctx %d: 0x%08x", i, data[i]); \ | |||
| 203 | } \ | |||
| 204 | } \ | |||
| 205 | } \ | |||
| 206 | break; \ | |||
| 207 | case ErrorCookie::ChibiOsPanic: \ | |||
| 208 | { \ | |||
| 209 | PRINT("msg %s", err->msg); \ | |||
| 210 | PRINT("file %s", err->file); \ | |||
| 211 | PRINT("line %d", err->line); \ | |||
| 212 | } \ | |||
| 213 | break; \ | |||
| 214 | default: \ | |||
| 215 | /* No cookie stored or invalid cookie (ie, backup RAM contains random garbage) */ \ | |||
| 216 | break; \ | |||
| 217 | } \ | |||
| 218 | } while(0) | |||
| 219 | ||||
| 220 | #define printErrorStack() \ | |||
| 221 | do { \ | |||
| 222 | PRINT("SP 0x%08lx", err->sp); \ | |||
| 223 | for (size_t i = 0; i < ERROR_STACK_DEPTH; i++) { \ | |||
| 224 | uint32_t cur = err->stack[i]; \ | |||
| 225 | if (cur != 0) { \ | |||
| 226 | PRINT(" 0x%08lx: 0x%08lx", err->sp - i * 4, cur); \ | |||
| 227 | } \ | |||
| 228 | } \ | |||
| 229 | } while(0) | |||
| 230 | ||||
| 231 | // TODO: reuse this code for writing crash report file | |||
| 232 | void errorHandlerShowBootReasonAndErrors() { | |||
| 233 | //this is console print | |||
| 234 | #define PRINT(...) efiPrintf(__VA_ARGS__) | |||
| 235 | ||||
| 236 | printResetReason(); | |||
| 237 | printWdResetCounter(); | |||
| 238 | ||||
| 239 | #if EFI_BACKUP_SRAM | |||
| 240 | backupErrorState *err = &lastBootError; | |||
| 241 | ErrorCookie cookie = err->Cookie; | |||
| 242 | ||||
| 243 | printErrorState(); | |||
| 244 | printErrorStack(); | |||
| 245 | #endif // EFI_BACKUP_SRAM | |||
| 246 | #undef PRINT | |||
| 247 | } | |||
| 248 | ||||
| 249 | #if EFI_FILE_LOGGING | |||
| 250 | #include "ff.h" | |||
| 251 | ||||
| 252 | #define FAIL_REPORT_PREFIX "fail" | |||
| 253 | ||||
| 254 | PUBLIC_API_WEAK void onBoardWriteErrorFile(FIL *) { | |||
| 255 | } | |||
| 256 | ||||
| 257 | static const char *errorHandlerGetErrorName(ErrorCookie cookie) | |||
| 258 | { | |||
| 259 | switch (cookie) { | |||
| 260 | case ErrorCookie::None: | |||
| 261 | return "none"; | |||
| 262 | case ErrorCookie::FirmwareError: | |||
| 263 | return "FWerror"; | |||
| 264 | case ErrorCookie::HardFault: | |||
| 265 | return "HardFault"; | |||
| 266 | case ErrorCookie::ChibiOsPanic: | |||
| 267 | return "OSpanic"; | |||
| 268 | } | |||
| 269 | ||||
| 270 | return "unknown"; | |||
| 271 | } | |||
| 272 | ||||
| 273 | bool needErrorReportFile = false; | |||
| 274 | ||||
| 275 | void errorHandlerWriteReportFile(FIL *fd) { | |||
| 276 | #if EFI_BACKUP_SRAM | |||
| 277 | backupErrorState *err = &lastBootError; | |||
| 278 | ErrorCookie cookie = err->Cookie; | |||
| 279 | #else | |||
| 280 | ErrorCookie cookie = ErrorCookie::None; | |||
| 281 | #endif | |||
| 282 | ||||
| 283 | if (cookie != ErrorCookie::None) { | |||
| 284 | needErrorReportFile = true; | |||
| 285 | } | |||
| 286 | ||||
| 287 | auto cause = getMCUResetCause(); | |||
| 288 | // TODO: should we also report Unknown? | |||
| 289 | if ((cause != Reset_Cause_NRST_Pin) && (cause != Reset_Cause_BOR) && | |||
| 290 | (cause != Reset_Cause_POR) && (cause != Reset_Cause_Unknown)) { | |||
| 291 | // not an expected cause | |||
| 292 | needErrorReportFile = true; | |||
| 293 | } | |||
| 294 | ||||
| 295 | if (needErrorReportFile) { | |||
| 296 | char fileName[_MAX_FILLER + 20]; | |||
| 297 | memset(fd, 0, sizeof(FIL)); // clear the memory | |||
| 298 | //TODO: use date + time for file name? | |||
| 299 | #if EFI_BACKUP_SRAM | |||
| 300 | sprintf(fileName, "%05ld_%s_%s.txt", | |||
| 301 | bootCount, FAIL_REPORT_PREFIX, errorHandlerGetErrorName(cookie)); | |||
| 302 | #else | |||
| 303 | sprintf(fileName, "last_%s_%s.txt", | |||
| 304 | FAIL_REPORT_PREFIX, errorHandlerGetErrorName(cookie)); | |||
| 305 | #endif | |||
| 306 | ||||
| 307 | FRESULT ret = f_open(fd, fileName, FA_CREATE_ALWAYS | FA_WRITE); | |||
| 308 | if (ret == FR_OK) { | |||
| 309 | //this is file print | |||
| 310 | #define PRINT(format, ...) f_printf(fd, format "\r\n", __VA_ARGS__) | |||
| 311 | printResetReason(); | |||
| 312 | printWdResetCounter(); | |||
| 313 | #if EFI_BACKUP_SRAM | |||
| 314 | printErrorState(); | |||
| 315 | printErrorStack(); | |||
| 316 | #endif // EFI_BACKUP_SRAM | |||
| 317 | f_printf(fd, "rusEFI v%d@%u", getRusEfiVersion(), /*do we have a working way to print 64 bit values?!*/(int)SIGNATURE_HASH); | |||
| 318 | // additional board-specific data | |||
| 319 | onBoardWriteErrorFile(fd); | |||
| 320 | // todo: figure out what else would be useful | |||
| 321 | f_close(fd); | |||
| 322 | enginePins.warningLedPin.setValue(1); | |||
| 323 | } | |||
| 324 | } | |||
| 325 | } | |||
| 326 | ||||
| 327 | static int errorHandlerIsReportExist(ErrorCookie cookie) { | |||
| 328 | bool exist = false; | |||
| 329 | FRESULT fr; /* Return value */ | |||
| 330 | DIR dj; /* Directory object */ | |||
| 331 | FILINFO fno; /* File information */ | |||
| 332 | TCHAR pattern[32]; | |||
| 333 | ||||
| 334 | sprintf(pattern, "*%s*", errorHandlerGetErrorName(cookie)); | |||
| 335 | ||||
| 336 | fr = f_findfirst(&dj, &fno, "", pattern); | |||
| 337 | exist = ((fr == FR_OK) && (fno.fname[0])); | |||
| 338 | f_closedir(&dj); | |||
| 339 | ||||
| 340 | return exist; | |||
| 341 | } | |||
| 342 | ||||
| 343 | int errorHandlerCheckReportFiles() { | |||
| 344 | hasReportFile = | |||
| 345 | (errorHandlerIsReportExist(ErrorCookie::FirmwareError) > 0) || | |||
| 346 | (errorHandlerIsReportExist(ErrorCookie::HardFault) > 0) || | |||
| 347 | (errorHandlerIsReportExist(ErrorCookie::ChibiOsPanic) > 0); | |||
| 348 | ||||
| 349 | return hasReportFile; | |||
| 350 | } | |||
| 351 | ||||
| 352 | static void errorHandlerDeleteTypedReport(ErrorCookie cookie) { | |||
| 353 | bool failed = false; | |||
| 354 | FRESULT fr; /* Return value */ | |||
| 355 | DIR dj; /* Directory object */ | |||
| 356 | FILINFO fno; /* File information */ | |||
| 357 | TCHAR pattern[32]; | |||
| 358 | ||||
| 359 | sprintf(pattern, "*%s*", errorHandlerGetErrorName(cookie)); | |||
| 360 | ||||
| 361 | do { | |||
| 362 | fr = f_findfirst(&dj, &fno, "", pattern); | |||
| 363 | f_closedir(&dj); | |||
| 364 | ||||
| 365 | if ((fr == FR_OK) && (fno.fname[0])) { | |||
| 366 | efiPrintf("deleting %s", fno.fname); | |||
| 367 | FRESULT ret = f_unlink(fno.fname); | |||
| 368 | if (ret != FR_OK) { | |||
| 369 | efiPrintf("Faield to delete %s: %d", fno.fname, ret); | |||
| 370 | failed = true; | |||
| 371 | } else { | |||
| 372 | efiPrintf("%s removed", fno.fname); | |||
| 373 | } | |||
| 374 | } | |||
| 375 | } while ((!failed) && (fr == FR_OK) && (fno.fname[0])); | |||
| 376 | } | |||
| 377 | ||||
| 378 | void errorHandlerDeleteReports() { | |||
| 379 | errorHandlerDeleteTypedReport(ErrorCookie::FirmwareError); | |||
| 380 | errorHandlerDeleteTypedReport(ErrorCookie::HardFault); | |||
| 381 | errorHandlerDeleteTypedReport(ErrorCookie::ChibiOsPanic); | |||
| 382 | ||||
| 383 | // update | |||
| 384 | errorHandlerCheckReportFiles(); | |||
| 385 | } | |||
| 386 | ||||
| 387 | #endif | |||
| 388 | ||||
| 389 | #if EFI_BACKUP_SRAM | |||
| 390 | static void errorHandlerSaveStack(backupErrorState *err, uint32_t *sp) | |||
| 391 | { | |||
| 392 | err->sp = (uint32_t)sp; | |||
| 393 | for (size_t i = 0; i < ERROR_STACK_DEPTH; i++) { | |||
| 394 | // avoid optimizatio and usage of __builtin_memcpy | |||
| 395 | // to avoid "error: '__builtin_memcpy' reading 128 bytes from a region of size 4" | |||
| 396 | err->stack[i] = *(volatile uint32_t *)sp; | |||
| 397 | sp++; | |||
| 398 | } | |||
| 399 | } | |||
| 400 | #endif // EFI_BACKUP_SRAM | |||
| 401 | ||||
| 402 | void logHardFault(uint32_t type, uintptr_t faultAddress, void* sp, port_extctx* ctx, uint32_t csfr) { | |||
| 403 | // todo: reuse hasCriticalFirmwareErrorFlag? something? | |||
| 404 | isInHardFaultHandler = true; | |||
| 405 | // Evidence first! | |||
| 406 | #if EFI_BACKUP_SRAM | |||
| 407 | auto bkpram = getBackupSram(); | |||
| 408 | auto err = &bkpram->err; | |||
| 409 | if (err->Cookie == ErrorCookie::None) { | |||
| 410 | err->FaultType = type; | |||
| 411 | err->FaultAddress = faultAddress; | |||
| 412 | err->Csfr = csfr; | |||
| 413 | memcpy(&err->FaultCtx, ctx, sizeof(port_extctx)); | |||
| 414 | err->Cookie = ErrorCookie::HardFault; | |||
| 415 | // copy stack last as it can be corrupted and cause another exeption | |||
| 416 | errorHandlerSaveStack(err, (uint32_t *)sp); | |||
| 417 | } | |||
| 418 | #endif // EFI_BACKUP_SRAM | |||
| 419 | // criticalShutdown() shutdown can cause cascaded fault. | |||
| 420 | // So we first save some valuable evidence and only after try to gracefully shutdown HW | |||
| 421 | criticalShutdown(); | |||
| 422 | } | |||
| 423 | ||||
| 424 | #endif /* EFI_PROD_CODE */ | |||
| 425 | ||||
| 426 | #if EFI_SIMULATOR || EFI_PROD_CODE | |||
| 427 | ||||
| 428 | void chDbgPanic3(const char *msg, const char * file, int line) { | |||
| 429 | #if EFI_PROD_CODE | |||
| 430 | #if EFI_BACKUP_SRAM | |||
| 431 | // following is allocated on stack | |||
| 432 | // add some marker | |||
| 433 | uint32_t tmp = 0xfffffa11; | |||
| 434 | #endif | |||
| 435 | // Attempt to break in to the debugger, if attached | |||
| 436 | if (CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) | |||
| 437 | { | |||
| 438 | bkpt(); | |||
| 439 | } | |||
| 440 | #endif // EFI_PROD_CODE | |||
| 441 | ||||
| 442 | #if EFI_BACKUP_SRAM | |||
| 443 | auto bkpram = getBackupSram(); | |||
| 444 | auto err = &bkpram->err; | |||
| 445 | if (err->Cookie == ErrorCookie::None) { | |||
| 446 | strlncpy(err->file, file, efi::size(err->file)); | |||
| 447 | err->line = line; | |||
| 448 | strlncpy(err->msg, msg, efi::size(err->msg)); | |||
| 449 | err->Cookie = ErrorCookie::ChibiOsPanic; | |||
| 450 | // copy stack last as it can be corrupted and cause another exeption | |||
| 451 | uint32_t *sp = &tmp; | |||
| 452 | errorHandlerSaveStack(err, sp); | |||
| 453 | } | |||
| 454 | #endif // EFI_BACKUP_SRAM | |||
| 455 | ||||
| 456 | if (hasOsPanicError()) | |||
| 457 | return; | |||
| 458 | dbg_panic_file = file; | |||
| 459 | dbg_panic_line = line; | |||
| 460 | #if CH_DBG_SYSTEM_STATE_CHECK | |||
| 461 | ch0.dbg.panic_msg = msg; | |||
| 462 | #endif /* CH_DBG_SYSTEM_STATE_CHECK */ | |||
| 463 | ||||
| 464 | #if !EFI_PROD_CODE | |||
| 465 | printf("chDbgPanic3 %s %s%d", msg, file, line); | |||
| 466 | exit(-1); | |||
| 467 | #else // EFI_PROD_CODE | |||
| 468 | ||||
| 469 | criticalError("assert fail %s %s:%d", msg, file, line); | |||
| 470 | ||||
| 471 | // If on the main thread, longjmp back to the init process so we can keep USB alive | |||
| 472 | if (chThdGetSelfX()->threadId == 0) { | |||
| 473 | // Force unlock, since we may be throwing-under-lock | |||
| 474 | chSysUnconditionalUnlock(); | |||
| 475 | ||||
| 476 | // there was a port_disable in chSysHalt, reenable interrupts so USB works | |||
| 477 | port_enable(); | |||
| 478 | ||||
| 479 | __NO_RETURN void onAssertionFailure(); | |||
| 480 | onAssertionFailure(); | |||
| 481 | } else { | |||
| 482 | // Not the main thread. | |||
| 483 | // All hope is now lost. | |||
| 484 | ||||
| 485 | // Reboot! | |||
| 486 | NVIC_SystemReset(); | |||
| 487 | } | |||
| 488 | ||||
| 489 | #endif // EFI_PROD_CODE | |||
| 490 | } | |||
| 491 | #endif /* EFI_SIMULATOR || EFI_PROD_CODE */ | |||
| 492 | ||||
| 493 | /** | |||
| 494 | * @returns TRUE in case there were warnings recently | |||
| 495 | */ | |||
| 496 |
1/1✓ Decision 'true' taken 1238 times.
|
1238 | bool warningVA(ObdCode code, bool reportToTs, const char *fmt, va_list args) { | |
| 497 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1238 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1238 times.
|
1238 | if (hasCriticalFirmwareErrorFlag) { |
| 498 | ✗ | return true; | ||
| 499 | } | |||
| 500 | ||||
| 501 | 1238 | bool known = engine->engineState.warnings.isWarningNow(code); | ||
| 502 | ||||
| 503 |
2/2✓ Branch 0 taken 1078 times.
✓ Branch 1 taken 160 times.
|
2/2✓ Decision 'true' taken 1078 times.
✓ Decision 'false' taken 160 times.
|
1238 | if (known) { |
| 504 | // if known - just reset timer | |||
| 505 | 1078 | engine->engineState.warnings.addWarningCode(code); | ||
| 506 | #if EFI_SIMULATOR || EFI_PROD_CODE | |||
| 507 | // we just had this same warning, let's not spam | |||
| 508 | return true; | |||
| 509 | #endif | |||
| 510 | } | |||
| 511 | ||||
| 512 | // print Pxxxx (for standard OBD) or Cxxxx (for custom) prefix | |||
| 513 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1228 times.
|
1238 | size_t size = snprintf(warningBuffer, sizeof(warningBuffer), "%s%04d: ", | |
| 514 | 1238 | code < ObdCode::CUSTOM_NAN_ENGINE_LOAD ? "P" : "C", (int) code); | ||
| 515 | ||||
| 516 | 1238 | chvsnprintf(warningBuffer + size, sizeof(warningBuffer) - size, fmt, args); | ||
| 517 | ||||
| 518 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1238 times.
|
1238 | engine->engineState.warnings.addWarningCode(code, reportToTs ? warningBuffer : nullptr); | |
| 519 | #if EFI_SIMULATOR || EFI_PROD_CODE | |||
| 520 | efiPrintf("WARNING: %s", warningBuffer); | |||
| 521 | #else | |||
| 522 | 1238 | printf("WARNING: %s\n", warningBuffer); | ||
| 523 | #endif /* EFI_SIMULATOR || EFI_PROD_CODE */ | |||
| 524 | ||||
| 525 | 1238 | return false; | ||
| 526 | } | |||
| 527 | ||||
| 528 | 1238 | bool warning(ObdCode code, const char *fmt, ...) { | ||
| 529 | 1238 | va_list args; | ||
| 530 | 1238 | va_start(args, fmt); | ||
| 531 |
1/1✓ Branch 1 taken 1238 times.
|
1238 | bool ret = warningVA(code, false, fmt, args); | |
| 532 | 1238 | va_end(args); | ||
| 533 | 1238 | return ret; | ||
| 534 | } | |||
| 535 | ||||
| 536 | ✗ | bool warningTsReport(ObdCode code, const char *fmt, ...) { | ||
| 537 | ✗ | va_list args; | ||
| 538 | ✗ | va_start(args, fmt); | ||
| 539 | ✗ | bool ret = warningVA(code, true, fmt, args); | ||
| 540 | ✗ | va_end(args); | ||
| 541 | ✗ | return ret; | ||
| 542 | } | |||
| 543 | ||||
| 544 | #if EFI_CLOCK_LOCKS | |||
| 545 | uint32_t lastLockTime; | |||
| 546 | /** | |||
| 547 | * Maximum time before requesting lock and releasing lock at the end of critical section | |||
| 548 | */ | |||
| 549 | uint32_t maxLockedDuration = 0; | |||
| 550 | ||||
| 551 | /** | |||
| 552 | * this depends on chdebug.h patch | |||
| 553 | #if CH_DBG_SYSTEM_STATE_CHECK == TRUE | |||
| 554 | -#define _dbg_enter_lock() (ch.dbg.lock_cnt = (cnt_t)1) | |||
| 555 | -#define _dbg_leave_lock() (ch.dbg.lock_cnt = (cnt_t)0) | |||
| 556 | +#define _dbg_enter_lock() {(ch.dbg.lock_cnt = (cnt_t)1); ON_LOCK_HOOK;} | |||
| 557 | +#define _dbg_leave_lock() {ON_UNLOCK_HOOK;(ch.dbg.lock_cnt = (cnt_t)0);} | |||
| 558 | #endif | |||
| 559 | */ | |||
| 560 | #endif /* EFI_CLOCK_LOCKS */ | |||
| 561 | ||||
| 562 | ✗ | void onLockHook(void) { | ||
| 563 | #if ENABLE_PERF_TRACE | |||
| 564 | perfEventInstantGlobal(PE::GlobalLock); | |||
| 565 | #endif /* ENABLE_PERF_TRACE */ | |||
| 566 | ||||
| 567 | #if EFI_CLOCK_LOCKS | |||
| 568 | lastLockTime = getTimeNowLowerNt(); | |||
| 569 | #endif /* EFI_CLOCK_LOCKS */ | |||
| 570 | ✗ | } | ||
| 571 | ||||
| 572 | ✗ | void onUnlockHook(void) { | ||
| 573 | #if EFI_CLOCK_LOCKS | |||
| 574 | uint32_t lockedDuration = getTimeNowLowerNt() - lastLockTime; | |||
| 575 | if (lockedDuration > maxLockedDuration) { | |||
| 576 | maxLockedDuration = lockedDuration; | |||
| 577 | } | |||
| 578 | // if (lockedDuration > 2800) { | |||
| 579 | // // un-comment this if you want a nice stop for a breakpoint | |||
| 580 | // maxLockedDuration = lockedDuration + 1; | |||
| 581 | // } | |||
| 582 | #endif /* EFI_CLOCK_LOCKS */ | |||
| 583 | ||||
| 584 | #if ENABLE_PERF_TRACE | |||
| 585 | perfEventInstantGlobal(PE::GlobalUnlock); | |||
| 586 | #endif /* ENABLE_PERF_TRACE */ | |||
| 587 | ✗ | } | ||
| 588 | ||||
| 589 | #if EFI_SIMULATOR || EFI_UNIT_TEST | |||
| 590 | #include <stdexcept> | |||
| 591 | #endif | |||
| 592 | ||||
| 593 | ✗ | void configError(const char *fmt, ...) { | ||
| 594 | ✗ | va_list ap; | ||
| 595 | ✗ | va_start(ap, fmt); | ||
| 596 | ✗ | chvsnprintf(configErrorMessageBuffer, sizeof(configErrorMessageBuffer), fmt, ap); | ||
| 597 | ✗ | va_end(ap); | ||
| 598 | ✗ | hasConfigErrorFlag = true; | ||
| 599 | ✗ | } | ||
| 600 | ||||
| 601 | ✗ | const char* getConfigErrorMessage() { | ||
| 602 | ✗ | return configErrorMessageBuffer; | ||
| 603 | } | |||
| 604 | ||||
| 605 | 79 | static void firmwareErrorV(ObdCode code, const char *fmt, va_list ap) { | ||
| 606 | #if EFI_PROD_CODE | |||
| 607 | #if EFI_BACKUP_SRAM | |||
| 608 | // following is allocated on stack | |||
| 609 | // add some marker | |||
| 610 | uint32_t tmp = 0xfaaaaa11; | |||
| 611 | #endif | |||
| 612 | if (hasCriticalFirmwareErrorFlag) | |||
| 613 | return; | |||
| 614 | hasCriticalFirmwareErrorFlag = true; | |||
| 615 | ||||
| 616 | // construct error message | |||
| 617 | if (indexOf(fmt, '%') == -1) { | |||
| 618 | /** | |||
| 619 | * in case of simple error message let's reduce stack usage | |||
| 620 | * chvsnprintf could cause an overflow if we're already low | |||
| 621 | */ | |||
| 622 | strlncpy((char*) criticalErrorMessageBuffer, fmt, sizeof(criticalErrorMessageBuffer)); | |||
| 623 | criticalErrorMessageBuffer[sizeof(criticalErrorMessageBuffer) - 1] = 0; // just to be sure | |||
| 624 | } else { | |||
| 625 | chvsnprintf(criticalErrorMessageBuffer, sizeof(criticalErrorMessageBuffer), fmt, ap); | |||
| 626 | } | |||
| 627 | ||||
| 628 | int errorMessageSize = strlen((char*)criticalErrorMessageBuffer); | |||
| 629 | static char versionBuffer[32]; | |||
| 630 | chsnprintf(versionBuffer, sizeof(versionBuffer), " %d@%s", getRusEfiVersion(), FIRMWARE_ID); | |||
| 631 | ||||
| 632 | if (errorMessageSize + strlen(versionBuffer) < sizeof(criticalErrorMessageBuffer)) { | |||
| 633 | strcpy((char*)(criticalErrorMessageBuffer) + errorMessageSize, versionBuffer); | |||
| 634 | } | |||
| 635 | ||||
| 636 | // Evidence first! | |||
| 637 | #if EFI_BACKUP_SRAM | |||
| 638 | auto bkpram = getBackupSram(); | |||
| 639 | auto err = &bkpram->err; | |||
| 640 | if (err->Cookie == ErrorCookie::None) { | |||
| 641 | strlncpy(err->msg, criticalErrorMessageBuffer, sizeof(err->msg)); | |||
| 642 | err->Cookie = ErrorCookie::FirmwareError; | |||
| 643 | // copy stack last as it can be corrupted and cause another exeption | |||
| 644 | uint32_t *sp = &tmp; | |||
| 645 | errorHandlerSaveStack(err, sp); | |||
| 646 | } | |||
| 647 | #endif // EFI_BACKUP_SRAM | |||
| 648 | ||||
| 649 | #if EFI_ENGINE_CONTROL | |||
| 650 | getLimpManager()->fatalError(); | |||
| 651 | #endif // EFI_ENGINE_CONTROL | |||
| 652 | engine->engineState.warnings.addWarningCode(code); | |||
| 653 | // criticalShutdown() shutdown can cause cascaded fault. | |||
| 654 | // So we first save some valuable evidence and only after try to gracefully shutdown HW | |||
| 655 | criticalShutdown(); | |||
| 656 | enginePins.communicationLedPin.setValue(1, /*force*/true); | |||
| 657 | #else // EFI_PROD_CODE | |||
| 658 | ||||
| 659 | // large buffer on stack is risky we better use normal memory | |||
| 660 | static char errorBuffer[200]; | |||
| 661 | ||||
| 662 |
1/1✓ Decision 'true' taken 79 times.
|
79 | vsnprintf(errorBuffer, sizeof(errorBuffer), fmt, ap); | |
| 663 | ||||
| 664 | 79 | printf("\x1B[31m>>>>>>>>>> firmwareError [%s]\r\n\x1B[0m\r\n", errorBuffer); | ||
| 665 | ||||
| 666 |
1/1✓ Branch 2 taken 79 times.
|
79 | throw std::logic_error(errorBuffer); | |
| 667 | #endif // EFI_PROD_CODE | |||
| 668 | } | |||
| 669 | ||||
| 670 | 79 | void firmwareError(ObdCode code, const char *fmt, ...) { | ||
| 671 | 79 | va_list ap; | ||
| 672 | 79 | va_start(ap, fmt); | ||
| 673 |
0/1✗ Branch 1 not taken.
|
79 | firmwareErrorV(code, fmt, ap); | |
| 674 | ✗ | va_end(ap); | ||
| 675 | ✗ | } | ||
| 676 | ||||
| 677 | // This is critical error for plain C use. | |||
| 678 | // From plain C file we cannot use somethings like this: | |||
| 679 | // #define criticalError(...) firmwareError(ObdCode::OBD_PCM_Processor_Fault, __VA_ARGS__) | |||
| 680 | // because of ObdCode:: | |||
| 681 | // Also we can not write some wrapper that will pass variable length argument list to another variable length argument list function | |||
| 682 | ✗ | void criticalErrorC(const char *fmt, ...) { | ||
| 683 | ✗ | va_list ap; | ||
| 684 | ✗ | va_start(ap, fmt); | ||
| 685 | ✗ | firmwareErrorV(ObdCode::OBD_PCM_Processor_Fault, fmt, ap); | ||
| 686 | ✗ | va_end(ap); | ||
| 687 | ✗ | } | ||
| 688 |