mirror of
https://git.743378673.xyz/MeloNX/MeloNX.git
synced 2025-07-19 13:36:53 +02:00
Rewrite NceAsmTable using dynamic generation instead to be more robust, fix bugs
This commit is contained in:
parent
c694d5774d
commit
8e23eb88bb
19 changed files with 2335 additions and 955 deletions
|
@ -1,13 +1,18 @@
|
|||
using Ryujinx.Cpu.Nce.Arm64;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Nce
|
||||
{
|
||||
static class NcePatcher
|
||||
{
|
||||
private const int ScratchBaseReg = 19;
|
||||
|
||||
private const uint IntCalleeSavedRegsMask = 0x1ff80000; // X19 to X28
|
||||
private const uint FpCalleeSavedRegsMask = 0xff00; // D8 to D15
|
||||
|
||||
private struct Context
|
||||
{
|
||||
public readonly ICpuMemoryManager MemoryManager;
|
||||
|
@ -76,18 +81,18 @@ namespace Ryujinx.Cpu.Nce
|
|||
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}.");
|
||||
}
|
||||
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}.");
|
||||
}
|
||||
|
||||
address += 4;
|
||||
}
|
||||
|
@ -106,159 +111,242 @@ namespace Ryujinx.Cpu.Nce
|
|||
|
||||
private static ulong WriteSvcPatch(ref Context context, ulong svcAddress, uint svcId)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.SvcPatchCode);
|
||||
Assembler asm = new();
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
WriteManagedCall(asm, (asm, ctx, tmp, tmp2) =>
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
asm.StrRiUn(Gpr(i), ctx, NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
WriteInManagedLockAcquire(asm, ctx, tmp, tmp2);
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0x52800000u, movIndex + 1);
|
||||
asm.Mov(Gpr(0, OperandType.I32), svcId);
|
||||
asm.LdrRiUn(tmp, ctx, NceNativeContext.GetSvcCallHandlerOffset());
|
||||
asm.Blr(tmp);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
Operand lblContinue = asm.CreateLabel();
|
||||
Operand lblQuit = asm.CreateLabel();
|
||||
|
||||
code[mov2Index] |= svcId << 5;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, svcAddress + 4);
|
||||
asm.Cbnz(Gpr(0, OperandType.I32), lblContinue);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
asm.MarkLabel(lblQuit);
|
||||
|
||||
CreateRegisterSaveRestoreForManaged().WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblContinue);
|
||||
|
||||
WriteInManagedLockRelease(asm, ctx, tmp, tmp2, ThreadExitMethod.Label, lblQuit);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
asm.LdrRiUn(Gpr(i), ctx, NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
}, 0xff);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(asm.CodeWords + sizeof(uint));
|
||||
|
||||
asm.B(GetOffset(targetAddress + (ulong)asm.CodeWords * sizeof(uint), svcAddress + sizeof(uint)));
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, asm.GetCode());
|
||||
|
||||
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;
|
||||
return WriteMrsContextRead(ref context, mrsAddress, rd, NceNativeContext.GetTpidrroEl0Offset());
|
||||
}
|
||||
|
||||
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;
|
||||
return WriteMrsContextRead(ref context, mrsAddress, rd, NceNativeContext.GetTpidrEl0Offset());
|
||||
}
|
||||
|
||||
private static ulong WriteMrsCtrEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsCtrEl0PatchCode);
|
||||
return WriteMrsContextRead(ref context, mrsAddress, rd, NceNativeContext.GetCtrEl0Offset());
|
||||
}
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
private static ulong WriteMrsCntpctEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
WriteManagedCall(asm, (asm, ctx, tmp, tmp2) =>
|
||||
{
|
||||
WriteInManagedLockAcquire(asm, ctx, tmp, tmp2);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
asm.Mov(tmp, (ulong)NceNativeInterface.GetTickCounterAccessFunctionPointer());
|
||||
asm.Blr(tmp);
|
||||
asm.StrRiUn(Gpr(0), ctx, NceNativeContext.GetTempStorageOffset());
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
WriteInManagedLockRelease(asm, ctx, tmp, tmp2, ThreadExitMethod.GenerateReturn);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
asm.LdrRiUn(Gpr((int)rd), ctx, NceNativeContext.GetTempStorageOffset());
|
||||
}, 1u << (int)rd);
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(asm.CodeWords + sizeof(uint));
|
||||
|
||||
asm.B(GetOffset(targetAddress + (ulong)asm.CodeWords * sizeof(uint), mrsAddress + sizeof(uint)));
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, asm.GetCode());
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMsrTpidrEl0Patch(ref Context context, ulong msrAddress, uint rd)
|
||||
{
|
||||
uint r2 = rd == 0 ? 1u : 0u;
|
||||
Assembler asm = new();
|
||||
|
||||
uint[] code = GetCopy(NceAsmTable.MsrTpidrEl0PatchCode);
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, 1u << (int)rd);
|
||||
|
||||
code[0] |= rd << 10;
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]));
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
asm.StrRiUn(Gpr((int)rd), Gpr(scratchRegs[0]),NceNativeContext.GetTpidrEl0Offset());
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, msrAddress + 4);
|
||||
ulong targetAddress = context.GetPatchWriteAddress(asm.CodeWords + sizeof(uint));
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
asm.B(GetOffset(targetAddress + (ulong)asm.CodeWords * sizeof(uint), msrAddress + sizeof(uint)));
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, asm.GetCode());
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
private static ulong WriteMrsCntpctEl0Patch(ref Context context, ulong mrsAddress, uint rd)
|
||||
private static ulong WriteMrsContextRead(ref Context context, ulong mrsAddress, uint rd, int contextOffset)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.MrsCntpctEl0PatchCode);
|
||||
Assembler asm = new();
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800013u);
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, 1u << (int)rd);
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]));
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0xD2800000u, movIndex + 1);
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WriteTickCounterAccessFunctionPointer(code, mov2Index);
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
asm.Add(Gpr((int)rd), Gpr(scratchRegs[0]), Const((ulong)contextOffset));
|
||||
|
||||
ulong targetAddress = context.GetPatchWriteAddress(code.Length);
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
code[code.Length - 2] |= rd;
|
||||
code[code.Length - 1] |= GetImm26(targetAddress + (ulong)(code.Length - 1) * 4, mrsAddress + 4);
|
||||
asm.LdrRiUn(Gpr((int)rd), Gpr((int)rd), 0);
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, code);
|
||||
ulong targetAddress = context.GetPatchWriteAddress(asm.CodeWords + sizeof(uint));
|
||||
|
||||
asm.B(GetOffset(targetAddress + (ulong)asm.CodeWords * sizeof(uint), mrsAddress + sizeof(uint)));
|
||||
|
||||
WriteCode(context.MemoryManager, targetAddress, asm.GetCode());
|
||||
|
||||
return targetAddress;
|
||||
}
|
||||
|
||||
public static uint[] GenerateExceptionHandlerEntry(IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr)
|
||||
private static void WriteLoadContext(Assembler asm, Operand tmp0, Operand tmp1, Operand tmp2)
|
||||
{
|
||||
uint[] code = GetCopy(NceAsmTable.ExceptionHandlerEntryCode);
|
||||
asm.Mov(tmp0, (ulong)NceThreadTable.EntriesPointer);
|
||||
|
||||
int movIndex = Array.IndexOf(code, 0xd2800018u);
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
asm.MrsTpidrroEl0(tmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.MrsTpidrEl0(tmp1);
|
||||
}
|
||||
|
||||
WritePointer(code, movIndex, (ulong)NceThreadTable.EntriesPointer);
|
||||
Operand lblFound = asm.CreateLabel();
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
int mov2Index = Array.IndexOf(code, 0xd2800008u, movIndex + 1);
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
WritePointer(code, mov2Index, (ulong)signalHandlerPtr);
|
||||
asm.LdrRiPost(tmp2, tmp0, 16);
|
||||
asm.Cmp(tmp1, tmp2);
|
||||
asm.B(lblFound, ArmCondition.Eq);
|
||||
asm.B(lblLoop);
|
||||
|
||||
int mov3Index = Array.IndexOf(code, 0xd2800000u, mov2Index + 1);
|
||||
asm.MarkLabel(lblFound);
|
||||
|
||||
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;
|
||||
asm.Ldur(tmp0, tmp0, -8);
|
||||
}
|
||||
|
||||
private static void WriteTickCounterAccessFunctionPointer(uint[] code, int movIndex)
|
||||
private static void WriteLoadContextSafe(Assembler asm, Operand lblFail, Operand tmp0, Operand tmp1, Operand tmp2, Operand tmp3)
|
||||
{
|
||||
WritePointer(code, movIndex, (ulong)NceNativeInterface.GetTickCounterAccessFunctionPointer());
|
||||
asm.Mov(tmp0, (ulong)NceThreadTable.EntriesPointer);
|
||||
asm.Ldur(tmp3, tmp0, -8);
|
||||
asm.Add(tmp3, tmp0, tmp3, ArmShiftType.Lsl, 4);
|
||||
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
asm.MrsTpidrroEl0(tmp1);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.MrsTpidrEl0(tmp1);
|
||||
}
|
||||
|
||||
Operand lblFound = asm.CreateLabel();
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Cmp(tmp0, tmp3);
|
||||
asm.B(lblFail, ArmCondition.GeUn);
|
||||
asm.LdrRiPost(tmp2, tmp0, 16);
|
||||
asm.Cmp(tmp1, tmp2);
|
||||
asm.B(lblFound, ArmCondition.Eq);
|
||||
asm.B(lblLoop);
|
||||
|
||||
asm.MarkLabel(lblFound);
|
||||
|
||||
asm.Ldur(tmp0, tmp0, -8);
|
||||
}
|
||||
|
||||
private static void WritePointer(uint[] code, int movIndex, ulong ptr)
|
||||
private static void PickScratchRegs(Span<int> scratchRegs, uint blacklistedRegMask)
|
||||
{
|
||||
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;
|
||||
int scratchReg = ScratchBaseReg;
|
||||
|
||||
for (int i = 0; i < scratchRegs.Length; i++)
|
||||
{
|
||||
while ((blacklistedRegMask & (1u << scratchReg)) != 0)
|
||||
{
|
||||
scratchReg++;
|
||||
}
|
||||
|
||||
if (scratchReg >= 29)
|
||||
{
|
||||
throw new ArgumentException($"No enough register for {scratchRegs.Length} scratch register, started from {ScratchBaseReg}");
|
||||
}
|
||||
|
||||
scratchRegs[i] = scratchReg++;
|
||||
}
|
||||
}
|
||||
|
||||
private static Operand Gpr(int register, OperandType type = OperandType.I64)
|
||||
{
|
||||
return new Operand(register, RegisterType.Integer, type);
|
||||
}
|
||||
|
||||
private static Operand Vec(int register, OperandType type = OperandType.V128)
|
||||
{
|
||||
return new Operand(register, RegisterType.Vector, type);
|
||||
}
|
||||
|
||||
private static Operand Const(ulong value)
|
||||
{
|
||||
return new Operand(OperandType.I64, value);
|
||||
}
|
||||
|
||||
private static Operand Const(OperandType type, ulong value)
|
||||
{
|
||||
return new Operand(type, value);
|
||||
}
|
||||
|
||||
private static uint GetImm26(ulong sourceAddress, ulong targetAddress)
|
||||
|
@ -274,6 +362,13 @@ namespace Ryujinx.Cpu.Nce
|
|||
return (uint)offsetTrunc;
|
||||
}
|
||||
|
||||
private static int GetOffset(ulong sourceAddress, ulong targetAddress)
|
||||
{
|
||||
long offset = (long)(targetAddress - sourceAddress);
|
||||
|
||||
return checked((int)offset);
|
||||
}
|
||||
|
||||
private static uint[] GetCopy(uint[] code)
|
||||
{
|
||||
uint[] codeCopy = new uint[code.Length];
|
||||
|
@ -282,6 +377,290 @@ namespace Ryujinx.Cpu.Nce
|
|||
return codeCopy;
|
||||
}
|
||||
|
||||
private static void WriteManagedCall(Assembler asm, Action<Assembler, Operand, Operand, Operand> writeCall, uint blacklistedRegMask)
|
||||
{
|
||||
int intMask = 0x7fffffff & (int)~blacklistedRegMask;
|
||||
int vecMask = unchecked((int)0xffffffff);
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[3];
|
||||
PickScratchRegs(scratchRegs, blacklistedRegMask);
|
||||
|
||||
RegisterSaveRestore rsr = new(intMask, vecMask, OperandType.V128);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
WriteLoadContext(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
|
||||
asm.MovSp(Gpr(scratchRegs[1]), Gpr(Assembler.SpRegister));
|
||||
asm.StrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetGuestSPOffset());
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
writeCall(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetGuestSPOffset());
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
}
|
||||
|
||||
public static uint[] GenerateThreadStartCode()
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
CreateRegisterSaveRestoreForManaged().WritePrologue(asm);
|
||||
|
||||
asm.MovSp(Gpr(1), Gpr(Assembler.SpRegister));
|
||||
asm.StrRiUn(Gpr(1), Gpr(0), NceNativeContext.GetHostSPOffset());
|
||||
|
||||
for (int i = 2; i < 30; i += 2)
|
||||
{
|
||||
asm.LdpRiUn(Gpr(i), Gpr(i + 1), Gpr(0), NceNativeContext.GetXOffset(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 32; i += 2)
|
||||
{
|
||||
asm.LdpRiUn(Vec(i), Vec(i + 1), Gpr(0), NceNativeContext.GetVOffset(i));
|
||||
}
|
||||
|
||||
asm.LdpRiUn(Gpr(30), Gpr(1), Gpr(0), NceNativeContext.GetXOffset(30));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(1));
|
||||
|
||||
asm.StrRiUn(Gpr(Assembler.ZrRegister, OperandType.I32), Gpr(0), NceNativeContext.GetInManagedOffset());
|
||||
|
||||
asm.LdpRiUn(Gpr(0), Gpr(1), Gpr(0), NceNativeContext.GetXOffset(0));
|
||||
asm.Br(Gpr(30));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
public static uint[] GenerateSuspendExceptionHandler()
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[4];
|
||||
PickScratchRegs(scratchRegs, 0u);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]) | (1 << scratchRegs[3]), hasCall: true);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
Operand lblAgain = asm.CreateLabel();
|
||||
Operand lblFail = asm.CreateLabel();
|
||||
|
||||
WriteLoadContextSafe(asm, lblFail, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(scratchRegs[2]), Gpr(Assembler.SpRegister));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
asm.Cmp(Gpr(0, OperandType.I32), Const((ulong)NceThreadPal.UnixSuspendSignal));
|
||||
asm.B(lblFail, ArmCondition.Ne);
|
||||
|
||||
// SigUsr2
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[1], OperandType.I32), Const(OperandType.I32, 1));
|
||||
asm.StrRiUn(Gpr(scratchRegs[1], OperandType.I32), Gpr(scratchRegs[0]), NceNativeContext.GetInManagedOffset());
|
||||
|
||||
asm.MarkLabel(lblAgain);
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[3]), (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
||||
asm.Blr(Gpr(scratchRegs[3]));
|
||||
|
||||
// TODO: Check return value, exit if we must.
|
||||
|
||||
WriteInManagedLockReleaseForSuspendHandler(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]), lblAgain);
|
||||
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[2]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblFail);
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
public static uint[] GenerateWrapperExceptionHandler(IntPtr oldSignalHandlerSegfaultPtr, IntPtr signalHandlerPtr)
|
||||
{
|
||||
Assembler asm = new();
|
||||
|
||||
Span<int> scratchRegs = stackalloc int[4];
|
||||
PickScratchRegs(scratchRegs, 0u);
|
||||
|
||||
RegisterSaveRestore rsr = new((1 << scratchRegs[0]) | (1 << scratchRegs[1]) | (1 << scratchRegs[2]) | (1 << scratchRegs[3]), hasCall: true);
|
||||
|
||||
rsr.WritePrologue(asm);
|
||||
|
||||
Operand lblFail = asm.CreateLabel();
|
||||
|
||||
WriteLoadContextSafe(asm, lblFail, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[2]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.LdrRiUn(Gpr(scratchRegs[1]), Gpr(scratchRegs[0]), NceNativeContext.GetHostSPOffset());
|
||||
asm.MovSp(Gpr(scratchRegs[2]), Gpr(Assembler.SpRegister));
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[1]));
|
||||
|
||||
// SigSegv
|
||||
|
||||
WriteInManagedLockAcquire(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]));
|
||||
|
||||
asm.Mov(Gpr(scratchRegs[3]), (ulong)signalHandlerPtr);
|
||||
asm.Blr(Gpr(scratchRegs[3]));
|
||||
|
||||
WriteInManagedLockRelease(asm, Gpr(scratchRegs[0]), Gpr(scratchRegs[1]), Gpr(scratchRegs[3]), ThreadExitMethod.None);
|
||||
|
||||
asm.MovSp(Gpr(Assembler.SpRegister), Gpr(scratchRegs[2]));
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
|
||||
asm.MarkLabel(lblFail);
|
||||
|
||||
rsr.WriteEpilogue(asm);
|
||||
|
||||
asm.Mov(Gpr(3), (ulong)oldSignalHandlerSegfaultPtr);
|
||||
asm.Br(Gpr(3));
|
||||
|
||||
return asm.GetCode();
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockAcquire(Assembler asm, Operand ctx, Operand tmp, Operand tmp2)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (that case should be impossible here).
|
||||
// Bit 1 being set means there is a signal pending, we should wait for the signal, otherwise it could trigger
|
||||
// while running managed code.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cbnz(tmp2Uint, lblLoop);
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
}
|
||||
|
||||
private enum ThreadExitMethod
|
||||
{
|
||||
None,
|
||||
GenerateReturn,
|
||||
Label
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockRelease(Assembler asm, Operand ctx, Operand tmp, Operand tmp2, ThreadExitMethod exitMethod, Operand lblQuit = default)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
Operand lblInterrupt = asm.CreateLabel();
|
||||
Operand lblDone = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (it should be always set here, as we just returned from managed code).
|
||||
// Bit 1 being set means a interrupt was requested while it was in managed, we should service it.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cmp(tmp2Uint, Const(OperandType.I32, 3));
|
||||
asm.B(lblInterrupt, ArmCondition.Eq);
|
||||
asm.Stlxr(Gpr(Assembler.ZrRegister, OperandType.I32), tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblDone);
|
||||
|
||||
asm.MarkLabel(lblInterrupt);
|
||||
|
||||
// If we got here, a interrupt was requested while it was in managed code.
|
||||
// Let's service the interrupt and check what we should do next.
|
||||
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.Mov(tmp, (ulong)NceNativeInterface.GetSuspendThreadHandlerFunctionPointer());
|
||||
asm.Blr(tmp);
|
||||
|
||||
// The return value from the interrupt handler indicates if we should continue running.
|
||||
// From here, we either try to release the lock again. We might have received another interrupt
|
||||
// request in the meantime, in which case we should service it again.
|
||||
// If we were requested to exit, then we exit if we can.
|
||||
// TODO: We should also exit while on a signal handler. To do that we need to modify the PC value on the
|
||||
// context. It's a bit more tricky to do, so for now we ignore that case with "ThreadExitMethod.None".
|
||||
|
||||
if (exitMethod == ThreadExitMethod.None)
|
||||
{
|
||||
asm.B(lblLoop);
|
||||
}
|
||||
else
|
||||
{
|
||||
asm.Cbnz(Gpr(0, OperandType.I32), lblLoop);
|
||||
|
||||
if (exitMethod == ThreadExitMethod.Label)
|
||||
{
|
||||
asm.B(lblQuit);
|
||||
}
|
||||
else if (exitMethod == ThreadExitMethod.GenerateReturn)
|
||||
{
|
||||
CreateRegisterSaveRestoreForManaged().WriteEpilogue(asm);
|
||||
|
||||
asm.Ret(Gpr(30));
|
||||
}
|
||||
}
|
||||
|
||||
asm.MarkLabel(lblDone);
|
||||
}
|
||||
|
||||
private static void WriteInManagedLockReleaseForSuspendHandler(Assembler asm, Operand ctx, Operand tmp, Operand tmp2, Operand lblAgain)
|
||||
{
|
||||
Operand tmpUint = new Operand(tmp.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
Operand tmp2Uint = new Operand(tmp2.GetRegister().Index, RegisterType.Integer, OperandType.I32);
|
||||
|
||||
Operand lblLoop = asm.CreateLabel();
|
||||
Operand lblInterrupt = asm.CreateLabel();
|
||||
Operand lblDone = asm.CreateLabel();
|
||||
|
||||
// Bit 0 set means that the thread is currently executing managed code (it should be always set here, as we just returned from managed code).
|
||||
// Bit 1 being set means a interrupt was requested while it was in managed, we should service it.
|
||||
|
||||
asm.MarkLabel(lblLoop);
|
||||
|
||||
asm.Add(tmp, ctx, Const((ulong)NceNativeContext.GetInManagedOffset()));
|
||||
asm.Ldaxr(tmp2Uint, tmp);
|
||||
asm.Cmp(tmp2Uint, Const(OperandType.I32, 3));
|
||||
asm.B(lblInterrupt, ArmCondition.Eq);
|
||||
asm.Stlxr(Gpr(Assembler.ZrRegister, OperandType.I32), tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblDone);
|
||||
|
||||
asm.MarkLabel(lblInterrupt);
|
||||
|
||||
// If we got here, a interrupt was requested while it was in managed code.
|
||||
// Let's service the interrupt and check what we should do next.
|
||||
|
||||
asm.Mov(tmp2Uint, Const(OperandType.I32, 1));
|
||||
asm.Stlxr(tmp2Uint, tmp, tmpUint);
|
||||
asm.Cbnz(tmpUint, lblLoop); // Retry if store failed.
|
||||
asm.B(lblAgain);
|
||||
|
||||
asm.MarkLabel(lblDone);
|
||||
}
|
||||
|
||||
private static RegisterSaveRestore CreateRegisterSaveRestoreForManaged()
|
||||
{
|
||||
return new RegisterSaveRestore((int)IntCalleeSavedRegsMask, unchecked((int)FpCalleeSavedRegsMask), OperandType.FP64, hasCall: true);
|
||||
}
|
||||
|
||||
private static void WriteCode(ICpuMemoryManager memoryManager, ulong address, uint[] code)
|
||||
{
|
||||
for (int i = 0; i < code.Length; i++)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue