MeloNX/src/Ryujinx.Cpu/Nce/NceCpuCodePatch.cs

81 lines
2.9 KiB
C#

using Ryujinx.Common;
using Ryujinx.Memory;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.Nce
{
/// <summary>
/// Native Code Execution CPU code patch.
/// </summary>
public class NceCpuCodePatch
{
private readonly List<uint> _code;
private readonly struct PatchTarget
{
public readonly int TextIndex;
public readonly int PatchStartIndex;
public readonly int PatchBranchIndex;
public PatchTarget(int textIndex, int patchStartIndex, int patchBranchIndex)
{
TextIndex = textIndex;
PatchStartIndex = patchStartIndex;
PatchBranchIndex = patchBranchIndex;
}
}
private readonly List<PatchTarget> _patchTargets;
/// <inheritdoc/>
public ulong Size => BitUtils.AlignUp((ulong)_code.Count * sizeof(uint), 0x1000UL);
public NceCpuCodePatch()
{
_code = new();
_patchTargets = new();
}
internal void AddCode(int textIndex, IEnumerable<uint> code)
{
int patchStartIndex = _code.Count;
_code.AddRange(code);
_patchTargets.Add(new PatchTarget(textIndex, patchStartIndex, _code.Count - 1));
}
/// <inheritdoc/>
public void Write(IVirtualMemoryManager memoryManager, ulong patchAddress, ulong textAddress)
{
uint[] code = _code.ToArray();
foreach (var patchTarget in _patchTargets)
{
ulong instPatchStartAddress = patchAddress + (ulong)patchTarget.PatchStartIndex * sizeof(uint);
ulong instPatchBranchAddress = patchAddress + (ulong)patchTarget.PatchBranchIndex * sizeof(uint);
ulong instTextAddress = textAddress + (ulong)patchTarget.TextIndex * sizeof(uint);
uint prevInst = memoryManager.Read<uint>(instTextAddress);
code[patchTarget.PatchBranchIndex] |= EncodeSImm26_2(checked((int)((long)instTextAddress - (long)instPatchBranchAddress + sizeof(uint))));
memoryManager.Write(instTextAddress, 0x14000000u | EncodeSImm26_2(checked((int)((long)instPatchStartAddress - (long)instTextAddress))));
uint newInst = memoryManager.Read<uint>(instTextAddress);
}
if (Size != 0)
{
memoryManager.Write(patchAddress, MemoryMarshal.Cast<uint, byte>(code));
memoryManager.Reprotect(patchAddress, Size, MemoryPermission.ReadAndExecute);
}
}
private static uint EncodeSImm26_2(int value)
{
uint imm = (uint)(value >> 2) & 0x3ffffff;
Debug.Assert(((int)imm << 6) >> 4 == value, $"Failed to encode constant 0x{value:X}.");
return imm;
}
}
}