Ryujinx-uplift/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
gdkchan b323a01738
Implement host tracked memory manager mode (#6356)
* Add host tracked memory manager mode

* Skipping flush is no longer needed

* Formatting + revert unrelated change

* LightningJit: Ensure that dest register is saved for load ops that do partial updates

* avoid allocations when doing address space lookup

Add missing improvement

* IsRmwMemory -> IsPartialRegisterUpdateMemory

* Ensure we iterate all private allocations in range

* PR feedback and potential fixes

* Simplified bridges a lot

* Skip calling SignalMappingChanged if Guest is true

* Late map bridge too

* Force address masking for prefetch instructions

* Reprotection for bridges

* Move partition list validation to separate debug method

* Move host tracked related classes to HostTracked folder

* New HostTracked namespace

* Move host tracked modes to the end of enum to avoid PPTC invalidation

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-03-26 23:33:24 -03:00

185 lines
5.9 KiB
C#

using ARMeilleure.Signal;
using Ryujinx.Common;
using Ryujinx.Memory;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.Signal
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SignalHandlerRange
{
public int IsActive;
public nuint RangeAddress;
public nuint RangeEndAddress;
public IntPtr ActionPointer;
}
[InlineArray(NativeSignalHandlerGenerator.MaxTrackedRanges)]
struct SignalHandlerRangeArray
{
public SignalHandlerRange Range0;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct SignalHandlerConfig
{
/// <summary>
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
/// </summary>
public int StructAddressOffset;
/// <summary>
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
/// </summary>
public int StructWriteOffset;
/// <summary>
/// The sigaction handler that was registered before this one. (unix only)
/// </summary>
public nuint UnixOldSigaction;
/// <summary>
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
/// </summary>
public int UnixOldSigaction3Arg;
/// <summary>
/// Fixed size array of tracked ranges.
/// </summary>
public SignalHandlerRangeArray Ranges;
}
static class NativeSignalHandler
{
private static readonly IntPtr _handlerConfig;
private static IntPtr _signalHandlerPtr;
private static MemoryBlock _codeBlock;
private static readonly object _lock = new();
private static bool _initialized;
static NativeSignalHandler()
{
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
ref SignalHandlerConfig config = ref GetConfigRef();
config = new SignalHandlerConfig();
}
public static void InitializeSignalHandler(Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
{
if (_initialized)
{
return;
}
lock (_lock)
{
if (_initialized)
{
return;
}
int rangeStructSize = Unsafe.SizeOf<SignalHandlerRange>();
ref SignalHandlerConfig config = ref GetConfigRef();
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{
_signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize));
if (customSignalHandlerFactory != null)
{
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
}
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
config.UnixOldSigaction3Arg = old.sa_flags & 4;
}
else
{
config.StructAddressOffset = 40; // ExceptionInformation1
config.StructWriteOffset = 32; // ExceptionInformation0
_signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateWindowsSignalHandler(_handlerConfig, rangeStructSize));
if (customSignalHandlerFactory != null)
{
_signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
}
WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
}
_initialized = true;
}
}
private static IntPtr MapCode(ReadOnlySpan<byte> code)
{
Debug.Assert(_codeBlock == null);
ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize());
_codeBlock = new MemoryBlock(codeSizeAligned);
_codeBlock.Write(0, code);
_codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute);
return _codeBlock.Pointer;
}
private static unsafe ref SignalHandlerConfig GetConfigRef()
{
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
}
public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
{
Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
{
if (ranges[i].IsActive == 0)
{
ranges[i].RangeAddress = address;
ranges[i].RangeEndAddress = endAddress;
ranges[i].ActionPointer = action;
ranges[i].IsActive = 1;
return true;
}
}
return false;
}
public static bool RemoveTrackedRegion(nuint address)
{
Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
{
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
{
ranges[i].IsActive = 0;
return true;
}
}
return false;
}
public static bool SupportsFaultAddressPatching()
{
return NativeSignalHandlerGenerator.SupportsFaultAddressPatchingForHost();
}
}
}