GCC Code Coverage Report


Directory: ./
File: firmware/controllers/core/error_handling.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 44.6% 29 0 65
Functions: 40.0% 6 0 15
Branches: 61.5% 8 0 13
Decisions: 83.3% 5 - 6

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 522954 bool hasConfigError() {
59 522954 return hasConfigErrorFlag;
60 }
61
62 void clearConfigErrorMessage() {
63 hasConfigErrorFlag = false;
64 }
65
66 522954 bool hasErrorReportFile() {
67 522954 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 1050 times.
1050 bool warningVA(ObdCode code, bool reportToTs, const char *fmt, va_list args) {
497
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1050 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1050 times.
1050 if (hasCriticalFirmwareErrorFlag) {
498 return true;
499 }
500
501 1050 bool known = engine->engineState.warnings.isWarningNow(code);
502
503
2/2
✓ Branch 0 taken 931 times.
✓ Branch 1 taken 119 times.
2/2
✓ Decision 'true' taken 931 times.
✓ Decision 'false' taken 119 times.
1050 if (known) {
504 // if known - just reset timer
505 931 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 1040 times.
1050 size_t size = snprintf(warningBuffer, sizeof(warningBuffer), "%s%04d: ",
514 1050 code < ObdCode::CUSTOM_NAN_ENGINE_LOAD ? "P" : "C", (int) code);
515
516 1050 chvsnprintf(warningBuffer + size, sizeof(warningBuffer) - size, fmt, args);
517
518
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1050 times.
1050 engine->engineState.warnings.addWarningCode(code, reportToTs ? warningBuffer : nullptr);
519 #if EFI_SIMULATOR || EFI_PROD_CODE
520 efiPrintf("WARNING: %s", warningBuffer);
521 #else
522 1050 printf("unit_test_warning: %s\n", warningBuffer);
523 #endif /* EFI_SIMULATOR || EFI_PROD_CODE */
524
525 1050 return false;
526 }
527
528 1050 bool warning(ObdCode code, const char *fmt, ...) {
529 1050 va_list args;
530 1050 va_start(args, fmt);
531
1/1
✓ Branch 1 taken 1050 times.
1050 bool ret = warningVA(code, false, fmt, args);
532 1050 va_end(args);
533 1050 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