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 |