From e253c2c00543ab96067dc241de8dd32d72e6ec99 Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Tue, 10 Jan 2023 23:42:35 +0000 Subject: [PATCH] errf: implement ERRF_SetUserString & clarify --- libctru/include/3ds/errf.h | 65 +++++++++++++++++++++++--------------- libctru/source/errf.c | 59 ++++++++++++++++++++++++++++++---- 2 files changed, 92 insertions(+), 32 deletions(-) diff --git a/libctru/include/3ds/errf.h b/libctru/include/3ds/errf.h index f1c10b5..ea830b4 100644 --- a/libctru/include/3ds/errf.h +++ b/libctru/include/3ds/errf.h @@ -9,12 +9,12 @@ /// Types of errors that can be thrown by err:f. typedef enum { - ERRF_ERRTYPE_GENERIC = 0, ///< For generic errors. Shows miscellaneous info. - ERRF_ERRTYPE_MEM_CORRUPT = 1, ///< Same output as generic, but informs the user that "the System Memory has been damaged". - ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Displays the "The Game Card was removed." message. - ERRF_ERRTYPE_EXCEPTION = 3, ///< For exceptions, or more specifically 'crashes'. union data should be exception_data. - ERRF_ERRTYPE_FAILURE = 4, ///< For general failure. Shows a message. union data should have a string set in failure_mesg - ERRF_ERRTYPE_LOGGED = 5, ///< Outputs logs to NAND in some cases. + ERRF_ERRTYPE_GENERIC = 0, ///< Generic fatal error. Shows miscellaneous info, including the address of the caller + ERRF_ERRTYPE_NAND_DAMAGED = 1, ///< Damaged NAND (CC_ERROR after reading CSR) + ERRF_ERRTYPE_CARD_REMOVED = 2, ///< Game content storage medium (cartridge and/or SD card) ejected. Not logged + ERRF_ERRTYPE_EXCEPTION = 3, ///< CPU or VFP exception + ERRF_ERRTYPE_FAILURE = 4, ///< Fatal error with a message instead of the caller's address + ERRF_ERRTYPE_LOG_ONLY = 5, ///< Log-level failure. Does not display the exception and does not force the system to reboot } ERRF_ErrType; /// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION @@ -46,12 +46,12 @@ typedef struct { u16 revLow; ///< Low revision ID u32 resCode; ///< Result code u32 pcAddr; ///< PC address at exception - u32 procId; ///< Process ID. - u64 titleId; ///< Title ID. - u64 appTitleId; ///< Application Title ID. + u32 procId; ///< Process ID of the caller + u64 titleId; ///< Title ID of the caller + u64 appTitleId; ///< Title ID of the running application union { ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION - char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE + char failure_mesg[0x60]; ///< String for when type is ERRF_ERRTYPE_FAILURE } data; ///< The different types of data for errors. } ERRF_FatalErrInfo; @@ -68,13 +68,19 @@ void errfExit(void); Handle *errfGetSessionHandle(void); /** - * @brief Throws a system error and possibly results in ErrDisp triggering. + * @brief Throws a system error and possibly logs it. * @param[in] error Error to throw. * - * After performing this, the system may panic and need to be rebooted. Extra information will be displayed on the - * top screen with a developer console or the proper patches in a CFW applied. + * ErrDisp may convert the error info to \ref ERRF_ERRTYPE_NAND_DAMAGED or \ref ERRF_ERRTYPE_CARD_REMOVED + * depending on the error code. * - * The error may not be shown and execution aborted until errfExit(void) is called. + * Except with \ref ERRF_ERRTYPE_LOG_ONLY, the system will panic and will need to be rebooted. + * Fatal error information will also be logged into a file, unless the type either \ref ERRF_ERRTYPE_NAND_DAMAGED + * or \ref ERRF_ERRTYPE_CARD_REMOVED. + * + * No error will be shown if the system is asleep. + * + * On retail units with vanilla firmware, no detailed information will be displayed on screen. * * You may wish to use ERRF_ThrowResult() or ERRF_ThrowResultWithMessage() instead of * constructing the ERRF_FatalErrInfo struct yourself. @@ -82,35 +88,44 @@ Handle *errfGetSessionHandle(void); Result ERRF_Throw(const ERRF_FatalErrInfo* error); /** - * @brief Throws a system error with the given Result code. + * @brief Throws (and logs) a system error with the given Result code. * @param[in] failure Result code to throw. * - * This calls ERRF_Throw() with error type ERRF_ERRTYPE_GENERIC and fills in the required data. + * This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_GENERIC and fills in the required data. * * This function \em does fill in the address where this function was called from. - * - * See https://3dbrew.org/wiki/ERR:Throw#Generic for expected top screen output - * on development units/patched ErrDisp. */ Result ERRF_ThrowResult(Result failure); +/** + * @brief Logs a system error with the given Result code. + * @param[in] failure Result code to log. + * + * Similar to \ref ERRF_Throw, except that it does not display anything on the screen, + * nor does it force the system to reboot. + * + * This function \em does fill in the address where this function was called from. + */ +Result ERRF_LogResult(Result failure); + /** * @brief Throws a system error with the given Result code and message. * @param[in] failure Result code to throw. * @param[in] message The message to display. * - * This calls ERRF_Throw() with error type ERRF_ERRTYPE_FAILURE and fills in the required data. + * This calls \ref ERRF_Throw with error type \ref ERRF_ERRTYPE_FAILURE and fills in the required data. * * This function does \em not fill in the address where this function was called from because it * would not be displayed. - * - * The message is only displayed on development units/patched ErrDisp. - * - * See https://3dbrew.org/wiki/ERR:Throw#Result_Failure for expected top screen output - * on development units/patched ErrDisp. */ Result ERRF_ThrowResultWithMessage(Result failure, const char* message); +/** + * @brief Specify an additional user string to use for error reporting. + * @param[in] user_string User string (up to 256 bytes, not including NUL byte) + */ +Result ERRF_SetUserString(const char* user_string); + /** * @brief Handles an exception using ErrDisp. * @param excep Exception information diff --git a/libctru/source/errf.c b/libctru/source/errf.c index a0b1bce..fbcd7ad 100644 --- a/libctru/source/errf.c +++ b/libctru/source/errf.c @@ -42,7 +42,7 @@ Handle* errfGetSessionHandle(void) Result ERRF_Throw(const ERRF_FatalErrInfo* error) { - uint32_t *cmdbuf = getThreadCommandBuffer(); + u32 *cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x1,32,0); // 0x10800 memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo)); @@ -83,11 +83,33 @@ Result ERRF_ThrowResult(Result failure) return ret; } +Result ERRF_LogResult(Result failure) +{ + ERRF_FatalErrInfo error; + Result ret; + + if (R_FAILED(ret = errfInit())) + return ret; + + memset(&error, 0, sizeof(error)); + + error.type = ERRF_ERRTYPE_LOG_ONLY; + + // pcAddr is not used by ErrDisp for ERRF_ERRTYPE_FAILURE + error.pcAddr = (u32)__builtin_extract_return_addr(__builtin_return_address(0)); + getCommonErrorData(&error, failure); + + ret = ERRF_Throw(&error); + + errfExit(); + + return ret; +} + Result ERRF_ThrowResultWithMessage(Result failure, const char* message) { ERRF_FatalErrInfo error; Result ret; - size_t msglen; if (R_FAILED(ret = errfInit())) return ret; @@ -97,11 +119,12 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message) error.type = ERRF_ERRTYPE_FAILURE; getCommonErrorData(&error, failure); - if ((msglen = strlen(message)) > sizeof(error.data.failure_mesg) - 1) - msglen = sizeof(error.data.failure_mesg) - 1; - - memcpy(error.data.failure_mesg, message, msglen); - error.data.failure_mesg[msglen] = '\0'; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstringop-truncation" + // Official client code always copies at most 95 bytes + NUL byte, but server codes uses %.96s + // and explicitely handles 96 non-NUL bytes. + strncpy(error.data.failure_mesg, message, sizeof(error.data.failure_mesg)); +#pragma GCC diagnostic pop ret = ERRF_Throw(&error); @@ -110,6 +133,28 @@ Result ERRF_ThrowResultWithMessage(Result failure, const char* message) return ret; } +Result ERRF_SetUserString(const char* user_string) +{ + Result ret = errfInit(); + size_t size = strnlen(user_string, 256); + + if (R_FAILED(ret)) + return ret; + + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x2,1,2); // 0x20042 + cmdbuf[1] = size; // unused + cmdbuf[2] = IPC_Desc_StaticBuffer(size, 0); + cmdbuf[3] = (u32)user_string; + + if (R_SUCCEEDED(ret = svcSendSyncRequest(errfHandle))) + ret = cmdbuf[1]; + + errfExit(); + return ret; +} + void ERRF_ExceptionHandler(ERRF_ExceptionInfo* excep, CpuRegisters* regs) { ERRF_FatalErrInfo error;