// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include #include "common/archives.h" #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/err_f.h" #undef exception_info SERIALIZE_EXPORT_IMPL(Service::ERR::ERR_F) namespace boost::serialization { template void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int) { ::new (t) Service::ERR::ERR_F(Core::Global()); } template void load_construct_data(iarchive& ar, Service::ERR::ERR_F* t, const unsigned int); } // namespace boost::serialization namespace Service::ERR { enum class FatalErrType : u32 { Generic = 0, Corrupted = 1, CardRemoved = 2, Exception = 3, ResultFailure = 4, Logged = 5, }; enum class ExceptionType : u32 { PrefetchAbort = 0, DataAbort = 1, Undefined = 2, VectorFP = 3, }; struct ExceptionInfo { u8 exception_type; INSERT_PADDING_BYTES(3); u32 sr; u32 ar; u32 fpexc; u32 fpinst; u32 fpinst2; }; static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size"); struct ExceptionContext final { std::array arm_regs; u32 cpsr; }; static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size"); struct ExceptionData { ExceptionInfo exception_info; ExceptionContext exception_context; INSERT_PADDING_WORDS(1); }; static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size"); struct ErrInfo { struct ErrInfoCommon { u8 specifier; // 0x0 u8 rev_high; // 0x1 u16 rev_low; // 0x2 u32 result_code; // 0x4 u32 pc_address; // 0x8 u32 pid; // 0xC u32 title_id_low; // 0x10 u32 title_id_high; // 0x14 u32 app_title_id_low; // 0x18 u32 app_title_id_high; // 0x1C } errinfo_common; static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size"); union { struct { char data[0x60]; // 0x20 } generic; struct { ExceptionData exception_data; // 0x20 } exception; struct { char message[0x60]; // 0x20 } result_failure; }; }; static std::string GetErrType(u8 type_code) { switch (static_cast(type_code)) { case FatalErrType::Generic: return "Generic"; case FatalErrType::Corrupted: return "Corrupted"; case FatalErrType::CardRemoved: return "CardRemoved"; case FatalErrType::Exception: return "Exception"; case FatalErrType::ResultFailure: return "ResultFailure"; case FatalErrType::Logged: return "Logged"; default: return "Unknown Error Type"; } } static std::string GetExceptionType(u8 type_code) { switch (static_cast(type_code)) { case ExceptionType::PrefetchAbort: return "Prefetch Abort"; case ExceptionType::DataAbort: return "Data Abort"; case ExceptionType::Undefined: return "Undefined Exception"; case ExceptionType::VectorFP: return "Vector Floating Point Exception"; default: return "Unknown Exception Type"; } } static std::string GetCurrentSystemTime() { auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); std::stringstream time_stream; time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S"); return time_stream.str(); } static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) { LOG_CRITICAL(Service_ERR, "PID: 0x{:08X}", errinfo_common.pid); LOG_CRITICAL(Service_ERR, "REV: 0x{:08X}_0x{:08X}", errinfo_common.rev_high, errinfo_common.rev_low); LOG_CRITICAL(Service_ERR, "TID: 0x{:08X}_0x{:08X}", errinfo_common.title_id_high, errinfo_common.title_id_low); LOG_CRITICAL(Service_ERR, "AID: 0x{:08X}_0x{:08X}", errinfo_common.app_title_id_high, errinfo_common.app_title_id_low); LOG_CRITICAL(Service_ERR, "ADR: 0x{:08X}", errinfo_common.pc_address); ResultCode result_code{errinfo_common.result_code}; LOG_CRITICAL(Service_ERR, "RSL: 0x{:08X}", result_code.raw); LOG_CRITICAL(Service_ERR, " Level: {}", static_cast(result_code.level.Value())); LOG_CRITICAL(Service_ERR, " Summary: {}", static_cast(result_code.summary.Value())); LOG_CRITICAL(Service_ERR, " Module: {}", static_cast(result_code.module.Value())); LOG_CRITICAL(Service_ERR, " Desc: {}", static_cast(result_code.description.Value())); } void ERR_F::ThrowFatalError(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 1, 32, 0); LOG_CRITICAL(Service_ERR, "Fatal error"); const ErrInfo errinfo = rp.PopRaw(); LOG_CRITICAL(Service_ERR, "Fatal error type: {}", GetErrType(errinfo.errinfo_common.specifier)); system.SetStatus(Core::System::ResultStatus::ErrorUnknown); // Generic Info LogGenericInfo(errinfo.errinfo_common); switch (static_cast(errinfo.errinfo_common.specifier)) { case FatalErrType::Generic: case FatalErrType::Corrupted: case FatalErrType::CardRemoved: case FatalErrType::Logged: { LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime()); break; } case FatalErrType::Exception: { const auto& errtype = errinfo.exception; // Register Info LOG_CRITICAL(Service_ERR, "ARM Registers:"); for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size(); ++index) { if (index < 13) { LOG_DEBUG(Service_ERR, "r{}=0x{:08X}", index, errtype.exception_data.exception_context.arm_regs.at(index)); } else if (index == 13) { LOG_CRITICAL(Service_ERR, "SP=0x{:08X}", errtype.exception_data.exception_context.arm_regs.at(index)); } else if (index == 14) { LOG_CRITICAL(Service_ERR, "LR=0x{:08X}", errtype.exception_data.exception_context.arm_regs.at(index)); } else if (index == 15) { LOG_CRITICAL(Service_ERR, "PC=0x{:08X}", errtype.exception_data.exception_context.arm_regs.at(index)); } } LOG_CRITICAL(Service_ERR, "CPSR=0x{:08X}", errtype.exception_data.exception_context.cpsr); // Exception Info LOG_CRITICAL(Service_ERR, "EXCEPTION TYPE: {}", GetExceptionType(errtype.exception_data.exception_info.exception_type)); switch (static_cast(errtype.exception_data.exception_info.exception_type)) { case ExceptionType::PrefetchAbort: LOG_CRITICAL(Service_ERR, "IFSR: 0x{:08X}", errtype.exception_data.exception_info.sr); LOG_CRITICAL(Service_ERR, "r15: 0x{:08X}", errtype.exception_data.exception_info.ar); break; case ExceptionType::DataAbort: LOG_CRITICAL(Service_ERR, "DFSR: 0x{:08X}", errtype.exception_data.exception_info.sr); LOG_CRITICAL(Service_ERR, "DFAR: 0x{:08X}", errtype.exception_data.exception_info.ar); break; case ExceptionType::VectorFP: LOG_CRITICAL(Service_ERR, "FPEXC: 0x{:08X}", errtype.exception_data.exception_info.fpinst); LOG_CRITICAL(Service_ERR, "FINST: 0x{:08X}", errtype.exception_data.exception_info.fpinst); LOG_CRITICAL(Service_ERR, "FINST2: 0x{:08X}", errtype.exception_data.exception_info.fpinst2); break; case ExceptionType::Undefined: break; // Not logging exception_info for this case } LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime()); break; } case FatalErrType::ResultFailure: { const auto& errtype = errinfo.result_failure; // Failure Message LOG_CRITICAL(Service_ERR, "Failure Message: {}", errtype.message); LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime()); break; } } // switch FatalErrType IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(RESULT_SUCCESS); } ERR_F::ERR_F(Core::System& system) : ServiceFramework("err:f", 1), system(system) { static const FunctionInfo functions[] = { {0x00010800, &ERR_F::ThrowFatalError, "ThrowFatalError"}, {0x00020042, nullptr, "SetUserString"}, }; RegisterHandlers(functions); } ERR_F::~ERR_F() = default; void InstallInterfaces(Core::System& system) { auto errf = std::make_shared(system); errf->InstallAsNamedPort(system.Kernel()); } } // namespace Service::ERR