From 33e673ceb84edf31dcb29abdf5df004cfd3beca5 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 2 Oct 2022 10:30:46 +0200 Subject: [PATCH] fatal: Implement Service (#3573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fatal: Implement Service This PR adds a basic implementation of fatal service, guest processes call it when there is something wrong. But since we can already have all informations by debugging it's not really useful. In any case, that's avoid an unimplemented service exception. Structs/Enum are based on Atmosphère source code. After logs the error report, I call SvcBreak. Feedbacks are welcome on this, since some guests calls it right after fatal service so I can remove it if needed. * Addresses gdkchan feedback --- Ryujinx.Common/Logging/LogClass.cs | 1 + Ryujinx.HLE/HOS/Services/Fatal/IService.cs | 141 +++++++++++++++++- .../HOS/Services/Fatal/Types/CpuContext32.cs | 25 ++++ .../HOS/Services/Fatal/Types/CpuContext64.cs | 24 +++ .../HOS/Services/Fatal/Types/FatalPolicy.cs | 9 ++ 5 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs create mode 100644 Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index 20c8da3f..2e936fc7 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -30,6 +30,7 @@ namespace Ryujinx.Common.Logging ServiceBsd, ServiceBtm, ServiceCaps, + ServiceFatal, ServiceFriend, ServiceFs, ServiceHid, diff --git a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index 692d2b0b..6d663a4d 100644 --- a/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -1,8 +1,147 @@ -namespace Ryujinx.HLE.HOS.Services.Fatal +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Fatal.Types; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Fatal { [Service("fatal:u")] class IService : IpcService { public IService(ServiceCtx context) { } + + [CommandHipc(0)] + // ThrowFatal(u64 result_code, u64 pid) + public ResultCode ThrowFatal(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + ulong pid = context.Request.HandleDesc.PId; + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, FatalPolicy.ErrorReportAndErrorScreen, null); + } + + [CommandHipc(1)] + // ThrowFatalWithPolicy(u64 result_code, u32 fatal_policy, u64 pid) + public ResultCode ThrowFatalWithPolicy(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); + ulong pid = context.Request.HandleDesc.PId; + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, null); + } + + [CommandHipc(2)] + // ThrowFatalWithCpuContext(u64 result_code, u32 fatal_policy, u64 pid, buffer cpu_context) + public ResultCode ThrowFatalWithCpuContext(ServiceCtx context) + { + ResultCode resultCode = (ResultCode)context.RequestData.ReadUInt64(); + FatalPolicy fatalPolicy = (FatalPolicy)context.RequestData.ReadUInt32(); + ulong pid = context.Request.HandleDesc.PId; + + ulong cpuContextPosition = context.Request.SendBuff[0].Position; + ulong cpuContextSize = context.Request.SendBuff[0].Size; + + ReadOnlySpan cpuContextData = context.Memory.GetSpan(cpuContextPosition, (int)cpuContextSize); + + return ThrowFatalWithCpuContextImpl(context, resultCode, pid, fatalPolicy, cpuContextData); + } + + private ResultCode ThrowFatalWithCpuContextImpl(ServiceCtx context, ResultCode resultCode, ulong pid, FatalPolicy fatalPolicy, ReadOnlySpan cpuContext) + { + StringBuilder errorReport = new StringBuilder(); + + errorReport.AppendLine(); + errorReport.AppendLine("ErrorReport log:"); + + errorReport.AppendLine($"\tTitleId: {context.Device.Application.TitleId:x16}"); + errorReport.AppendLine($"\tPid: {pid}"); + errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}"); + errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}"); + + if (cpuContext != null) + { + errorReport.AppendLine("CPU Context:"); + + if (context.Device.Application.TitleIs64Bit) + { + CpuContext64 cpuContext64 = MemoryMarshal.Cast(cpuContext)[0]; + + errorReport.AppendLine($"\tStartAddress: 0x{cpuContext64.StartAddress:x16}"); + errorReport.AppendLine($"\tRegisterSetFlags: {cpuContext64.RegisterSetFlags}"); + + if (cpuContext64.StackTraceSize > 0) + { + errorReport.AppendLine("\tStackTrace:"); + + for (int i = 0; i < cpuContext64.StackTraceSize; i++) + { + errorReport.AppendLine($"\t\t0x{cpuContext64.StackTrace[i]:x16}"); + } + } + + errorReport.AppendLine("\tRegisters:"); + + for (int i = 0; i < cpuContext64.X.Length; i++) + { + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext64.X[i]:x16}"); + } + + errorReport.AppendLine(); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext64.FP:x16}"); + errorReport.AppendLine($"\t\tLR:\t0x{cpuContext64.LR:x16}"); + errorReport.AppendLine($"\t\tSP:\t0x{cpuContext64.SP:x16}"); + errorReport.AppendLine($"\t\tPC:\t0x{cpuContext64.PC:x16}"); + errorReport.AppendLine($"\t\tPState:\t0x{cpuContext64.PState:x16}"); + errorReport.AppendLine($"\t\tAfsr0:\t0x{cpuContext64.Afsr0:x16}"); + errorReport.AppendLine($"\t\tAfsr1:\t0x{cpuContext64.Afsr1:x16}"); + errorReport.AppendLine($"\t\tEsr:\t0x{cpuContext64.Esr:x16}"); + errorReport.AppendLine($"\t\tFar:\t0x{cpuContext64.Far:x16}"); + } + else + { + CpuContext32 cpuContext32 = MemoryMarshal.Cast(cpuContext)[0]; + + errorReport.AppendLine($"\tStartAddress: 0x{cpuContext32.StartAddress:16}"); + errorReport.AppendLine($"\tRegisterSetFlags: {cpuContext32.RegisterSetFlags}"); + + if (cpuContext32.StackTraceSize > 0) + { + errorReport.AppendLine("\tStackTrace:"); + + for (int i = 0; i < cpuContext32.StackTraceSize; i++) + { + errorReport.AppendLine($"\t\t0x{cpuContext32.StackTrace[i]:x16}"); + } + } + + errorReport.AppendLine("\tRegisters:"); + + for (int i = 0; i < cpuContext32.X.Length; i++) + { + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext32.X[i]:x16}"); + } + + errorReport.AppendLine(); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext32.FP:x16}"); + errorReport.AppendLine($"\t\tFP:\t0x{cpuContext32.IP:x16}"); + errorReport.AppendLine($"\t\tSP:\t0x{cpuContext32.SP:x16}"); + errorReport.AppendLine($"\t\tLR:\t0x{cpuContext32.LR:x16}"); + errorReport.AppendLine($"\t\tPC:\t0x{cpuContext32.PC:x16}"); + errorReport.AppendLine($"\t\tPState:\t0x{cpuContext32.PState:x16}"); + errorReport.AppendLine($"\t\tAfsr0:\t0x{cpuContext32.Afsr0:x16}"); + errorReport.AppendLine($"\t\tAfsr1:\t0x{cpuContext32.Afsr1:x16}"); + errorReport.AppendLine($"\t\tEsr:\t0x{cpuContext32.Esr:x16}"); + errorReport.AppendLine($"\t\tFar:\t0x{cpuContext32.Far:x16}"); + } + } + + Logger.Info?.Print(LogClass.ServiceFatal, errorReport.ToString()); + + context.Device.System.KernelContext.Syscall.Break((ulong)resultCode); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs new file mode 100644 index 00000000..5c0b116b --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext32.cs @@ -0,0 +1,25 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + public struct CpuContext32 + { + public Array11 X; + public uint FP; + public uint IP; + public uint SP; + public uint LR; + public uint PC; + + public uint PState; + public uint Afsr0; + public uint Afsr1; + public uint Esr; + public uint Far; + + public Array32 StackTrace; + public uint StackTraceSize; + public uint StartAddress; + public uint RegisterSetFlags; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs new file mode 100644 index 00000000..24829a78 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/CpuContext64.cs @@ -0,0 +1,24 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + public struct CpuContext64 + { + public Array29 X; + public ulong FP; + public ulong LR; + public ulong SP; + public ulong PC; + + public ulong PState; + public ulong Afsr0; + public ulong Afsr1; + public ulong Esr; + public ulong Far; + + public Array32 StackTrace; + public ulong StartAddress; + public ulong RegisterSetFlags; + public uint StackTraceSize; + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs b/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs new file mode 100644 index 00000000..fe55cf12 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Fatal/Types/FatalPolicy.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Fatal.Types +{ + enum FatalPolicy + { + ErrorReportAndErrorScreen, + ErrorReport, + ErrorScreen + } +} \ No newline at end of file