forked from MeloNX/MeloNX
293 lines
11 KiB
C#
293 lines
11 KiB
C#
using Ryujinx.Common;
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Memory;
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Ryujinx.Cpu.Nce
|
|
{
|
|
static class NcePatcher
|
|
{
|
|
private struct Context
|
|
{
|
|
public readonly ICpuMemoryManager MemoryManager;
|
|
public ulong PatchRegionAddress;
|
|
public ulong PatchRegionSize;
|
|
|
|
public Context(ICpuMemoryManager memoryManager, ulong patchRegionAddress, ulong patchRegionSize)
|
|
{
|
|
MemoryManager = memoryManager;
|
|
PatchRegionAddress = patchRegionAddress;
|
|
PatchRegionSize = patchRegionSize;
|
|
}
|
|
|
|
public ulong GetPatchWriteAddress(int length)
|
|
{
|
|
ulong byteLength = (ulong)length * 4;
|
|
|
|
if (PatchRegionSize < byteLength)
|
|
{
|
|
throw new Exception("No enough space for patch.");
|
|
}
|
|
|
|
ulong address = PatchRegionAddress;
|
|
PatchRegionAddress += byteLength;
|
|
PatchRegionSize -= byteLength;
|
|
|
|
return address;
|
|
}
|
|
}
|
|
|
|
public static void Patch(
|
|
ICpuMemoryManager memoryManager,
|
|
ulong textAddress,
|
|
ulong textSize,
|
|
ulong patchRegionAddress,
|
|
ulong patchRegionSize)
|
|
{
|
|
Context context = new Context(memoryManager, patchRegionAddress, patchRegionSize);
|
|
|
|
ulong address = textAddress;
|
|
while (address < textAddress + textSize)
|
|
{
|
|
uint inst = memoryManager.Read<uint>(address);
|
|
|
|
if ((inst & ~(0xffffu << 5)) == 0xd4000001u) // svc #0
|
|
{
|
|
uint svcId = (ushort)(inst >> 5);
|
|
PatchInstruction(memoryManager, address, WriteSvcPatch(ref context, address, svcId));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched SVC #{svcId} at 0x{address:X}.");
|
|
}
|
|
else if ((inst & ~0x1f) == 0xd53bd060) // mrs x0, tpidrro_el0
|
|
{
|
|
uint rd = inst & 0x1f;
|
|
PatchInstruction(memoryManager, address, WriteMrsTpidrroEl0Patch(ref context, address, rd));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidrro_el0 at 0x{address:X}.");
|
|
}
|
|
else if ((inst & ~0x1f) == 0xd53bd040) // mrs x0, tpidr_el0
|
|
{
|
|
uint rd = inst & 0x1f;
|
|
PatchInstruction(memoryManager, address, WriteMrsTpidrEl0Patch(ref context, address, rd));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, tpidr_el0 at 0x{address:X}.");
|
|
}
|
|
else if ((inst & ~0x1f) == 0xd53b0020 && OperatingSystem.IsMacOS()) // mrs x0, ctr_el0
|
|
{
|
|
uint rd = inst & 0x1f;
|
|
PatchInstruction(memoryManager, address, WriteMrsCtrEl0Patch(ref context, address, rd));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, ctr_el0 at 0x{address:X}.");
|
|
}
|
|
else if ((inst & ~0x1f) == 0xd51bd040) // msr tpidr_el0, x0
|
|
{
|
|
uint rd = inst & 0x1f;
|
|
PatchInstruction(memoryManager, address, WriteMsrTpidrEl0Patch(ref context, address, rd));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched MSR tpidr_el0, x{rd} at 0x{address:X}.");
|
|
}
|
|
else if ((inst & ~0x1f) == 0xd53be020) // mrs x0, cntpct_el0
|
|
{
|
|
uint rd = inst & 0x1f;
|
|
PatchInstruction(memoryManager, address, WriteMrsCntpctEl0Patch(ref context, address, rd));
|
|
Logger.Debug?.Print(LogClass.Cpu, $"Patched MRS x{rd}, cntpct_el0 at 0x{address:X}.");
|
|
}
|
|
|
|
address += 4;
|
|
}
|
|
|
|
ulong patchRegionConsumed = BitUtils.AlignUp(context.PatchRegionAddress - patchRegionAddress, 0x1000UL);
|
|
if (patchRegionConsumed != 0)
|
|
{
|
|
memoryManager.Reprotect(patchRegionAddress, patchRegionConsumed, MemoryPermission.ReadAndExecute);
|
|
}
|
|
}
|
|
|
|
private static void PatchInstruction(ICpuMemoryManager memoryManager, ulong instructionAddress, ulong targetAddress)
|
|
{
|
|
memoryManager.Write(instructionAddress, 0x14000000u | GetImm26(instructionAddress, targetAddress));
|
|
}
|
|
|
|
private static ulong WriteSvcPatch(ref Context context, ulong svcAddress, uint svcId)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.SvcPatchCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
int mov2Index = Array.IndexOf(code, 0x52800000u, movIndex + 1);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[mov2Index] |= svcId << 5;
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, svcAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
private static ulong WriteMrsTpidrroEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.MrsTpidrroEl0PatchCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[code.Length - 2] |= rd;
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
private static ulong WriteMrsTpidrEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.MrsTpidrEl0PatchCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[code.Length - 2] |= rd;
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
private static ulong WriteMrsCtrEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.MrsCtrEl0PatchCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[code.Length - 2] |= rd;
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
private static ulong WriteMsrTpidrEl0Patch(ref Context context, ulong msrAddress, uint rd)
|
|
{
|
|
uint r2 = rd == 0 ? 1u : 0u;
|
|
|
|
uint[] code = GetCopy(NceAsmTable.MsrTpidrEl0PatchCode);
|
|
|
|
code[0] |= rd << 10;
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, msrAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
private static ulong WriteMrsCntpctEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.MrsCntpctEl0PatchCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
int mov2Index = Array.IndexOf(code, 0xD2800000u, movIndex + 1);
|
|
|
|
WriteTickCounterAccessFunctionPointer(code, mov2Index);
|
|
|
|
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
|
|
|
code[code.Length - 2] |= rd;
|
|
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
|
|
|
WriteCode(context.MemoryManager, targetAddress, code);
|
|
|
|
return targetAddress;
|
|
}
|
|
|
|
public static uint[] GenerateExceptionHandlerEntry(IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr)
|
|
{
|
|
uint[] code = GetCopy(NceAsmTable.ExceptionHandlerEntryCode);
|
|
|
|
int movIndex = Array.IndexOf(code, 0xd2800018u);
|
|
|
|
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
|
|
|
int mov2Index = Array.IndexOf(code, 0xd2800008u, movIndex + 1);
|
|
|
|
WritePointer(code, mov2Index, (ulong)signalHandlerPtr);
|
|
|
|
int mov3Index = Array.IndexOf(code, 0xd2800000u, mov2Index + 1);
|
|
|
|
WritePointer(code, mov3Index, (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
|
|
|
int mov4Index = Array.IndexOf(code, 0xd2800003u, mov3Index + 1);
|
|
|
|
WritePointer(code, mov4Index, (ulong)oldSignalHandlerSegfaultPtr);
|
|
|
|
int cmpIndex = Array.IndexOf(code, 0x7100027fu);
|
|
|
|
code[cmpIndex] |= (uint)NceThreadPal.UnixSuspendSignal << 10;
|
|
|
|
return code;
|
|
}
|
|
|
|
private static void WriteTickCounterAccessFunctionPointer(uint[] code, int movIndex)
|
|
{
|
|
WritePointer(code, movIndex, (ulong)NceNativeInterface.GetTickCounterAccessFunctionPointer());
|
|
}
|
|
|
|
private static void WritePointer(uint[] code, int movIndex, ulong ptr)
|
|
{
|
|
code[movIndex] |= (uint)(ushort)ptr << 5;
|
|
code[movIndex + 1] |= (uint)(ushort)(ptr >> 16) << 5;
|
|
code[movIndex + 2] |= (uint)(ushort)(ptr >> 32) << 5;
|
|
code[movIndex + 3] |= (uint)(ushort)(ptr >> 48) << 5;
|
|
}
|
|
|
|
private static uint GetImm26(ulong sourceAddress, ulong targetAddress)
|
|
{
|
|
long offset = (long)(targetAddress - sourceAddress);
|
|
long offsetTrunc = (offset >> 2) & 0x3FFFFFF;
|
|
|
|
if ((offsetTrunc << 38) >> 36 != offset)
|
|
{
|
|
throw new Exception($"Offset out of range: 0x{sourceAddress:X} -> 0x{targetAddress:X} (0x{offset:X})");
|
|
}
|
|
|
|
return (uint)offsetTrunc;
|
|
}
|
|
|
|
private static uint[] GetCopy(uint[] code)
|
|
{
|
|
uint[] codeCopy = new uint[code.Length];
|
|
code.CopyTo(codeCopy, 0);
|
|
|
|
return codeCopy;
|
|
}
|
|
|
|
private static void WriteCode(ICpuMemoryManager memoryManager, ulong address, uint[] code)
|
|
{
|
|
for (int i = 0; i < code.Length; i++)
|
|
{
|
|
memoryManager.Write(address + (ulong)i * sizeof(uint), code[i]);
|
|
}
|
|
}
|
|
}
|
|
} |