early-access version 1255
This commit is contained in:
176
src/core/hle/service/fatal/fatal.cpp
Executable file
176
src/core/hle/service/fatal/fatal.cpp
Executable file
@@ -0,0 +1,176 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <fmt/chrono.h>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
#include "core/reporter.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name)
|
||||
: ServiceFramework{system_, name}, module{std::move(module_)} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
struct FatalInfo {
|
||||
enum class Architecture : s32 {
|
||||
AArch64,
|
||||
AArch32,
|
||||
};
|
||||
|
||||
const char* ArchAsString() const {
|
||||
return arch == Architecture::AArch64 ? "AArch64" : "AArch32";
|
||||
}
|
||||
|
||||
std::array<u64_le, 31> registers{};
|
||||
u64_le sp{};
|
||||
u64_le pc{};
|
||||
u64_le pstate{};
|
||||
u64_le afsr0{};
|
||||
u64_le afsr1{};
|
||||
u64_le esr{};
|
||||
u64_le far{};
|
||||
|
||||
std::array<u64_le, 32> backtrace{};
|
||||
u64_le program_entry_point{};
|
||||
|
||||
// Bit flags that indicate which registers have been set with values
|
||||
// for this context. The service itself uses these to determine which
|
||||
// registers to specifically print out.
|
||||
u64_le set_flags{};
|
||||
|
||||
u32_le backtrace_size{};
|
||||
Architecture arch{};
|
||||
u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding?
|
||||
};
|
||||
static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
|
||||
|
||||
enum class FatalType : u32 {
|
||||
ErrorReportAndScreen = 0,
|
||||
ErrorReport = 1,
|
||||
ErrorScreen = 2,
|
||||
};
|
||||
|
||||
static void GenerateErrorReport(Core::System& system, ResultCode error_code,
|
||||
const FatalInfo& info) {
|
||||
const auto title_id = system.CurrentProcess()->GetTitleID();
|
||||
std::string crash_report = fmt::format(
|
||||
"Yuzu {}-{} crash report\n"
|
||||
"Title ID: {:016x}\n"
|
||||
"Result: 0x{:X} ({:04}-{:04d})\n"
|
||||
"Set flags: 0x{:16X}\n"
|
||||
"Program entry point: 0x{:16X}\n"
|
||||
"\n",
|
||||
Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw,
|
||||
2000 + static_cast<u32>(error_code.module.Value()),
|
||||
static_cast<u32>(error_code.description.Value()), info.set_flags, info.program_entry_point);
|
||||
if (info.backtrace_size != 0x0) {
|
||||
crash_report += "Registers:\n";
|
||||
for (size_t i = 0; i < info.registers.size(); i++) {
|
||||
crash_report +=
|
||||
fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]);
|
||||
}
|
||||
crash_report += fmt::format(" SP: {:016x}\n", info.sp);
|
||||
crash_report += fmt::format(" PC: {:016x}\n", info.pc);
|
||||
crash_report += fmt::format(" PSTATE: {:016x}\n", info.pstate);
|
||||
crash_report += fmt::format(" AFSR0: {:016x}\n", info.afsr0);
|
||||
crash_report += fmt::format(" AFSR1: {:016x}\n", info.afsr1);
|
||||
crash_report += fmt::format(" ESR: {:016x}\n", info.esr);
|
||||
crash_report += fmt::format(" FAR: {:016x}\n", info.far);
|
||||
crash_report += "\nBacktrace:\n";
|
||||
for (size_t i = 0; i < info.backtrace_size; i++) {
|
||||
crash_report +=
|
||||
fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]);
|
||||
}
|
||||
|
||||
crash_report += fmt::format("Architecture: {}\n", info.ArchAsString());
|
||||
crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10);
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_Fatal, "{}", crash_report);
|
||||
|
||||
system.GetReporter().SaveCrashReport(
|
||||
title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
|
||||
info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
|
||||
info.backtrace_size, info.ArchAsString(), info.unk10);
|
||||
}
|
||||
|
||||
static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
|
||||
const FatalInfo& info) {
|
||||
LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", fatal_type,
|
||||
error_code.raw);
|
||||
|
||||
switch (fatal_type) {
|
||||
case FatalType::ErrorReportAndScreen:
|
||||
GenerateErrorReport(system, error_code, info);
|
||||
[[fallthrough]];
|
||||
case FatalType::ErrorScreen:
|
||||
// Since we have no fatal:u error screen. We should just kill execution instead
|
||||
ASSERT(false);
|
||||
break;
|
||||
// Should not throw a fatal screen but should generate an error report
|
||||
case FatalType::ErrorReport:
|
||||
GenerateErrorReport(system, error_code, info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_Fatal, "called");
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto error_code = rp.Pop<ResultCode>();
|
||||
|
||||
ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_Fatal, "called");
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto error_code = rp.Pop<ResultCode>();
|
||||
const auto fatal_type = rp.PopEnum<FatalType>();
|
||||
|
||||
ThrowFatalError(system, error_code, fatal_type,
|
||||
{}); // No info is passed with ThrowFatalWithPolicy
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) {
|
||||
LOG_ERROR(Service_Fatal, "called");
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto error_code = rp.Pop<ResultCode>();
|
||||
const auto fatal_type = rp.PopEnum<FatalType>();
|
||||
const auto fatal_info = ctx.ReadBuffer();
|
||||
FatalInfo info{};
|
||||
|
||||
ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
|
||||
std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
|
||||
|
||||
ThrowFatalError(system, error_code, fatal_type, info);
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager);
|
||||
std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager);
|
||||
}
|
||||
|
||||
} // namespace Service::Fatal
|
34
src/core/hle/service/fatal/fatal.h
Executable file
34
src/core/hle/service/fatal/fatal.h
Executable file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
class Module final {
|
||||
public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module_, Core::System& system_,
|
||||
const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void ThrowFatal(Kernel::HLERequestContext& ctx);
|
||||
void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx);
|
||||
void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
};
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
|
||||
|
||||
} // namespace Service::Fatal
|
14
src/core/hle/service/fatal/fatal_p.cpp
Executable file
14
src/core/hle/service/fatal/fatal_p.cpp
Executable file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_p.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Fatal_P::Fatal_P(std::shared_ptr<Module> module_, Core::System& system_)
|
||||
: Interface(std::move(module_), system_, "fatal:p") {}
|
||||
|
||||
Fatal_P::~Fatal_P() = default;
|
||||
|
||||
} // namespace Service::Fatal
|
17
src/core/hle/service/fatal/fatal_p.h
Executable file
17
src/core/hle/service/fatal/fatal_p.h
Executable file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
class Fatal_P final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_P(std::shared_ptr<Module> module_, Core::System& system_);
|
||||
~Fatal_P() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Fatal
|
21
src/core/hle/service/fatal/fatal_u.cpp
Executable file
21
src/core/hle/service/fatal/fatal_u.cpp
Executable file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/service/fatal/fatal_u.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
Fatal_U::Fatal_U(std::shared_ptr<Module> module_, Core::System& system_)
|
||||
: Interface(std::move(module_), system_, "fatal:u") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &Fatal_U::ThrowFatal, "ThrowFatal"},
|
||||
{1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
|
||||
{2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"},
|
||||
};
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
Fatal_U::~Fatal_U() = default;
|
||||
|
||||
} // namespace Service::Fatal
|
17
src/core/hle/service/fatal/fatal_u.h
Executable file
17
src/core/hle/service/fatal/fatal_u.h
Executable file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/fatal/fatal.h"
|
||||
|
||||
namespace Service::Fatal {
|
||||
|
||||
class Fatal_U final : public Module::Interface {
|
||||
public:
|
||||
explicit Fatal_U(std::shared_ptr<Module> module_, Core::System& system_);
|
||||
~Fatal_U() override;
|
||||
};
|
||||
|
||||
} // namespace Service::Fatal
|
Reference in New Issue
Block a user