1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <array>
6 #include <chrono>
7 #include <iomanip>
8 #include <sstream>
9 #include "common/archives.h"
10 #include "common/bit_field.h"
11 #include "common/common_types.h"
12 #include "common/logging/log.h"
13 #include "core/core.h"
14 #include "core/hle/ipc.h"
15 #include "core/hle/ipc_helpers.h"
16 #include "core/hle/result.h"
17 #include "core/hle/service/err_f.h"
18 #undef exception_info // We use 'exception_info' as a plain identifier, but MSVC defines this in one
19 // of its many headers.
20
21 SERIALIZE_EXPORT_IMPL(Service::ERR::ERR_F)
22
23 namespace boost::serialization {
24 template <class Archive>
load_construct_data(Archive & ar,Service::ERR::ERR_F * t,const unsigned int)25 void load_construct_data(Archive& ar, Service::ERR::ERR_F* t, const unsigned int) {
26 ::new (t) Service::ERR::ERR_F(Core::Global<Core::System>());
27 }
28
29 template void load_construct_data<iarchive>(iarchive& ar, Service::ERR::ERR_F* t,
30 const unsigned int);
31 } // namespace boost::serialization
32
33 namespace Service::ERR {
34
35 enum class FatalErrType : u32 {
36 Generic = 0,
37 Corrupted = 1,
38 CardRemoved = 2,
39 Exception = 3,
40 ResultFailure = 4,
41 Logged = 5,
42 };
43
44 enum class ExceptionType : u32 {
45 PrefetchAbort = 0,
46 DataAbort = 1,
47 Undefined = 2,
48 VectorFP = 3,
49 };
50
51 struct ExceptionInfo {
52 u8 exception_type;
53 INSERT_PADDING_BYTES(3);
54 u32 sr;
55 u32 ar;
56 u32 fpexc;
57 u32 fpinst;
58 u32 fpinst2;
59 };
60 static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size");
61
62 struct ExceptionContext final {
63 std::array<u32, 16> arm_regs;
64 u32 cpsr;
65 };
66 static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size");
67
68 struct ExceptionData {
69 ExceptionInfo exception_info;
70 ExceptionContext exception_context;
71 INSERT_PADDING_WORDS(1);
72 };
73 static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size");
74
75 struct ErrInfo {
76 struct ErrInfoCommon {
77 u8 specifier; // 0x0
78 u8 rev_high; // 0x1
79 u16 rev_low; // 0x2
80 u32 result_code; // 0x4
81 u32 pc_address; // 0x8
82 u32 pid; // 0xC
83 u32 title_id_low; // 0x10
84 u32 title_id_high; // 0x14
85 u32 app_title_id_low; // 0x18
86 u32 app_title_id_high; // 0x1C
87 } errinfo_common;
88 static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size");
89
90 union {
91 struct {
92 char data[0x60]; // 0x20
93 } generic;
94
95 struct {
96 ExceptionData exception_data; // 0x20
97 } exception;
98
99 struct {
100 char message[0x60]; // 0x20
101 } result_failure;
102 };
103 };
104
GetErrType(u8 type_code)105 static std::string GetErrType(u8 type_code) {
106 switch (static_cast<FatalErrType>(type_code)) {
107 case FatalErrType::Generic:
108 return "Generic";
109 case FatalErrType::Corrupted:
110 return "Corrupted";
111 case FatalErrType::CardRemoved:
112 return "CardRemoved";
113 case FatalErrType::Exception:
114 return "Exception";
115 case FatalErrType::ResultFailure:
116 return "ResultFailure";
117 case FatalErrType::Logged:
118 return "Logged";
119 default:
120 return "Unknown Error Type";
121 }
122 }
123
GetExceptionType(u8 type_code)124 static std::string GetExceptionType(u8 type_code) {
125 switch (static_cast<ExceptionType>(type_code)) {
126 case ExceptionType::PrefetchAbort:
127 return "Prefetch Abort";
128 case ExceptionType::DataAbort:
129 return "Data Abort";
130 case ExceptionType::Undefined:
131 return "Undefined Exception";
132 case ExceptionType::VectorFP:
133 return "Vector Floating Point Exception";
134 default:
135 return "Unknown Exception Type";
136 }
137 }
138
GetCurrentSystemTime()139 static std::string GetCurrentSystemTime() {
140 auto now = std::chrono::system_clock::now();
141 auto time = std::chrono::system_clock::to_time_t(now);
142
143 std::stringstream time_stream;
144 time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S");
145 return time_stream.str();
146 }
147
LogGenericInfo(const ErrInfo::ErrInfoCommon & errinfo_common)148 static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) {
149 LOG_CRITICAL(Service_ERR, "PID: 0x{:08X}", errinfo_common.pid);
150 LOG_CRITICAL(Service_ERR, "REV: 0x{:08X}_0x{:08X}", errinfo_common.rev_high,
151 errinfo_common.rev_low);
152 LOG_CRITICAL(Service_ERR, "TID: 0x{:08X}_0x{:08X}", errinfo_common.title_id_high,
153 errinfo_common.title_id_low);
154 LOG_CRITICAL(Service_ERR, "AID: 0x{:08X}_0x{:08X}", errinfo_common.app_title_id_high,
155 errinfo_common.app_title_id_low);
156 LOG_CRITICAL(Service_ERR, "ADR: 0x{:08X}", errinfo_common.pc_address);
157
158 ResultCode result_code{errinfo_common.result_code};
159 LOG_CRITICAL(Service_ERR, "RSL: 0x{:08X}", result_code.raw);
160 LOG_CRITICAL(Service_ERR, " Level: {}", static_cast<u32>(result_code.level.Value()));
161 LOG_CRITICAL(Service_ERR, " Summary: {}", static_cast<u32>(result_code.summary.Value()));
162 LOG_CRITICAL(Service_ERR, " Module: {}", static_cast<u32>(result_code.module.Value()));
163 LOG_CRITICAL(Service_ERR, " Desc: {}", static_cast<u32>(result_code.description.Value()));
164 }
165
ThrowFatalError(Kernel::HLERequestContext & ctx)166 void ERR_F::ThrowFatalError(Kernel::HLERequestContext& ctx) {
167 IPC::RequestParser rp(ctx, 1, 32, 0);
168
169 LOG_CRITICAL(Service_ERR, "Fatal error");
170 const ErrInfo errinfo = rp.PopRaw<ErrInfo>();
171 LOG_CRITICAL(Service_ERR, "Fatal error type: {}", GetErrType(errinfo.errinfo_common.specifier));
172 system.SetStatus(Core::System::ResultStatus::ErrorUnknown);
173
174 // Generic Info
175 LogGenericInfo(errinfo.errinfo_common);
176
177 switch (static_cast<FatalErrType>(errinfo.errinfo_common.specifier)) {
178 case FatalErrType::Generic:
179 case FatalErrType::Corrupted:
180 case FatalErrType::CardRemoved:
181 case FatalErrType::Logged: {
182 LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
183 break;
184 }
185 case FatalErrType::Exception: {
186 const auto& errtype = errinfo.exception;
187
188 // Register Info
189 LOG_CRITICAL(Service_ERR, "ARM Registers:");
190 for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size();
191 ++index) {
192 if (index < 13) {
193 LOG_DEBUG(Service_ERR, "r{}=0x{:08X}", index,
194 errtype.exception_data.exception_context.arm_regs.at(index));
195 } else if (index == 13) {
196 LOG_CRITICAL(Service_ERR, "SP=0x{:08X}",
197 errtype.exception_data.exception_context.arm_regs.at(index));
198 } else if (index == 14) {
199 LOG_CRITICAL(Service_ERR, "LR=0x{:08X}",
200 errtype.exception_data.exception_context.arm_regs.at(index));
201 } else if (index == 15) {
202 LOG_CRITICAL(Service_ERR, "PC=0x{:08X}",
203 errtype.exception_data.exception_context.arm_regs.at(index));
204 }
205 }
206 LOG_CRITICAL(Service_ERR, "CPSR=0x{:08X}", errtype.exception_data.exception_context.cpsr);
207
208 // Exception Info
209 LOG_CRITICAL(Service_ERR, "EXCEPTION TYPE: {}",
210 GetExceptionType(errtype.exception_data.exception_info.exception_type));
211 switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) {
212 case ExceptionType::PrefetchAbort:
213 LOG_CRITICAL(Service_ERR, "IFSR: 0x{:08X}", errtype.exception_data.exception_info.sr);
214 LOG_CRITICAL(Service_ERR, "r15: 0x{:08X}", errtype.exception_data.exception_info.ar);
215 break;
216 case ExceptionType::DataAbort:
217 LOG_CRITICAL(Service_ERR, "DFSR: 0x{:08X}", errtype.exception_data.exception_info.sr);
218 LOG_CRITICAL(Service_ERR, "DFAR: 0x{:08X}", errtype.exception_data.exception_info.ar);
219 break;
220 case ExceptionType::VectorFP:
221 LOG_CRITICAL(Service_ERR, "FPEXC: 0x{:08X}",
222 errtype.exception_data.exception_info.fpinst);
223 LOG_CRITICAL(Service_ERR, "FINST: 0x{:08X}",
224 errtype.exception_data.exception_info.fpinst);
225 LOG_CRITICAL(Service_ERR, "FINST2: 0x{:08X}",
226 errtype.exception_data.exception_info.fpinst2);
227 break;
228 case ExceptionType::Undefined:
229 break; // Not logging exception_info for this case
230 }
231 LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
232 break;
233 }
234
235 case FatalErrType::ResultFailure: {
236 const auto& errtype = errinfo.result_failure;
237
238 // Failure Message
239 LOG_CRITICAL(Service_ERR, "Failure Message: {}", errtype.message);
240 LOG_CRITICAL(Service_ERR, "Datetime: {}", GetCurrentSystemTime());
241 break;
242 }
243
244 } // switch FatalErrType
245
246 IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
247 rb.Push(RESULT_SUCCESS);
248 }
249
ERR_F(Core::System & system)250 ERR_F::ERR_F(Core::System& system) : ServiceFramework("err:f", 1), system(system) {
251 static const FunctionInfo functions[] = {
252 {0x00010800, &ERR_F::ThrowFatalError, "ThrowFatalError"},
253 {0x00020042, nullptr, "SetUserString"},
254 };
255 RegisterHandlers(functions);
256 }
257
258 ERR_F::~ERR_F() = default;
259
InstallInterfaces(Core::System & system)260 void InstallInterfaces(Core::System& system) {
261 auto errf = std::make_shared<ERR_F>(system);
262 errf->InstallAsNamedPort(system.Kernel());
263 }
264
265 } // namespace Service::ERR
266