fatal: Implement Service (#3573)

* 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
This commit is contained in:
Ac_K 2022-10-02 10:30:46 +02:00 committed by GitHub
parent 9c2500de5f
commit 33e673ceb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 199 additions and 1 deletions

View file

@ -30,6 +30,7 @@ namespace Ryujinx.Common.Logging
ServiceBsd,
ServiceBtm,
ServiceCaps,
ServiceFatal,
ServiceFriend,
ServiceFs,
ServiceHid,

View file

@ -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<bytes, 0x15> 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<byte> 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<byte> 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<byte, CpuContext64>(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<byte, CpuContext32>(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;
}
}
}

View file

@ -0,0 +1,25 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.HLE.HOS.Services.Fatal.Types
{
public struct CpuContext32
{
public Array11<uint> 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<uint> StackTrace;
public uint StackTraceSize;
public uint StartAddress;
public uint RegisterSetFlags;
}
}

View file

@ -0,0 +1,24 @@
using Ryujinx.Common.Memory;
namespace Ryujinx.HLE.HOS.Services.Fatal.Types
{
public struct CpuContext64
{
public Array29<ulong> 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<ulong> StackTrace;
public ulong StartAddress;
public ulong RegisterSetFlags;
public uint StackTraceSize;
}
}

View file

@ -0,0 +1,9 @@
namespace Ryujinx.HLE.HOS.Services.Fatal.Types
{
enum FatalPolicy
{
ErrorReportAndErrorScreen,
ErrorReport,
ErrorScreen
}
}