diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 1dc1683..b5c0cb6 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -14,6 +14,7 @@ extern "C" { #include <3ds/ipc.h> #include <3ds/svc.h> #include <3ds/srv.h> +#include <3ds/errf.h> #include <3ds/os.h> #include <3ds/synchronization.h> #include <3ds/thread.h> @@ -106,4 +107,4 @@ extern "C" { * @example threads/event/source/main.c * @example time/rtc/source/main.c */ - + diff --git a/libctru/include/3ds/errf.h b/libctru/include/3ds/errf.h new file mode 100644 index 0000000..d79de34 --- /dev/null +++ b/libctru/include/3ds/errf.h @@ -0,0 +1,80 @@ +/** + * @file errf.h + * @brief Error Display API + */ + +#pragma once + +#include <3ds/types.h> + +/// Used for register dumps. +typedef struct { + u32 r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, cpsr; +} ERRF_ExceptionContext; + +/// Types of errors that can be thrown by err:f. +typedef enum { + ERRF_ERRTYPE_GENERIC = 0, ///< For generic errors. Shows miscellaneous info. + 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; + +/// Types of 'Exceptions' thrown for ERRF_ERRTYPE_EXCEPTION +typedef enum { + ERRF_EXCEPTION_PREFETCH_ABORT = 0, ///< Prefetch Abort + ERRF_EXCEPTION_DATA_ABORT = 1, ///< Data abort + ERRF_EXCEPTION_UNDEFINED = 2, ///< Undefined instruction + ERRF_EXCEPTION_VFP = 3 ///< VFP (floating point) exception. +} ERRF_ExceptionType; + +typedef struct { + ERRF_ExceptionType type; ///< Type of the exception. One of the ERRF_EXCEPTION_* values. + u8 reserved[3]; + u32 reg1; ///< If type is prefetch, this should be ifsr, and on data abort dfsr + u32 reg2; ///< If type is prefetch, this should be r15, and dfar on data abort + u32 fpexc; + u32 fpinst; + u32 fpint2; +} ERRF_ExceptionInfo; + +typedef struct { + ERRF_ExceptionInfo excep; ///< Exception info struct + ERRF_ExceptionContext regs; ///< Register dump. + u8 pad[4]; +} ERRF_ExceptionData; + +typedef struct { + ERRF_ErrType type; ///< Type, one of the ERRF_ERRTYPE_* enum + u8 revHigh; ///< High revison ID + 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. + union { + ERRF_ExceptionData exception_data; ///< Data for when type is ERRF_ERRTYPE_EXCEPTION + char failure_mesg[60]; ///< String for when type is ERRF_ERRTYPE_FAILURE + } data; ///< The different types of data for errors. +} ERRF_FatalErrInfo; + +/// Initializes ERR:f. +Result errfInit(void); + +/// Exits ERR:f. +void errfExit(void); + +/** + * @brief Gets the current err:f API session handle. + * @return The current err:f API session handle. + */ +Handle *errfGetSessionHandle(void); + +/** + * @brief Throws a system error and possibly results in ErrDisp triggering. 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. + * @param error Error to throw. + */ +Result ERRF_Throw(ERRF_FatalErrInfo *error); diff --git a/libctru/source/errf.c b/libctru/source/errf.c new file mode 100644 index 0000000..a15a719 --- /dev/null +++ b/libctru/source/errf.c @@ -0,0 +1,52 @@ +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/synchronization.h> +#include <3ds/errf.h> +#include <3ds/ipc.h> +#include <3ds/env.h> +#include "internal.h" + +static Handle errfHandle; +static int errfRefCount; + +Result errfInit(void) +{ + Result rc = 0; + + if (AtomicPostIncrement(&errfRefCount)) return 0; + + rc = svcConnectToPort(&errfHandle, "err:f"); + if (R_FAILED(rc)) goto end; + +end: + if (R_FAILED(rc)) errfExit(); + return rc; +} + +void errfExit(void) +{ + if (AtomicDecrement(&errfRefCount)) + return; + svcCloseHandle(errfHandle); +} + +Handle* errfGetSessionHandle(void) +{ + return &errfHandle; +} + +Result ERRF_Throw(ERRF_FatalErrInfo *error) +{ + uint32_t *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x10800; + memcpy(&cmdbuf[1], error, sizeof(ERRF_FatalErrInfo)); + + Result ret = 0; + if (R_FAILED(ret = svcSendSyncRequest(errfHandle))) + return ret; + + return cmdbuf[1]; +}