aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Cpu/Jit/MemoryManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.Cpu/Jit/MemoryManager.cs')
-rw-r--r--src/Ryujinx.Cpu/Jit/MemoryManager.cs148
1 files changed, 103 insertions, 45 deletions
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
index bbfdf536..c87c8b8c 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
@@ -33,6 +33,8 @@ namespace Ryujinx.Cpu.Jit
private readonly MemoryBlock _pageTable;
+ private readonly ManagedPageFlags _pages;
+
/// <summary>
/// Page table base pointer.
/// </summary>
@@ -70,6 +72,8 @@ namespace Ryujinx.Cpu.Jit
AddressSpaceSize = asSize;
_pageTable = new MemoryBlock((asSize / PageSize) * PteSize);
+ _pages = new ManagedPageFlags(AddressSpaceBits);
+
Tracking = new MemoryTracking(this, PageSize);
}
@@ -89,6 +93,7 @@ namespace Ryujinx.Cpu.Jit
remainingSize -= PageSize;
}
+ _pages.AddMapping(oVa, size);
Tracking.Map(oVa, size);
}
@@ -111,6 +116,7 @@ namespace Ryujinx.Cpu.Jit
UnmapEvent?.Invoke(va, size);
Tracking.Unmap(va, size);
+ _pages.RemoveMapping(va, size);
ulong remainingSize = size;
while (remainingSize != 0)
@@ -149,6 +155,26 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
+ public T ReadGuest<T>(ulong va) where T : unmanaged
+ {
+ try
+ {
+ SignalMemoryTrackingImpl(va, (ulong)Unsafe.SizeOf<T>(), false, true);
+
+ return Read<T>(va);
+ }
+ catch (InvalidMemoryRegionException)
+ {
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
+
+ return default;
+ }
+ }
+
+ /// <inheritdoc/>
public override void Read(ulong va, Span<byte> data)
{
try
@@ -184,6 +210,16 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
+ public void WriteGuest<T>(ulong va, T value) where T : unmanaged
+ {
+ Span<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateSpan(ref value, 1));
+
+ SignalMemoryTrackingImpl(va, (ulong)data.Length, true, true);
+
+ WriteImpl(va, data);
+ }
+
+ /// <inheritdoc/>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
if (data.Length == 0)
@@ -520,50 +556,57 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
- public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
+ public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest)
{
AssertValidAddressAndSize(va, size);
- // Protection is inverted on software pages, since the default value is 0.
- protection = (~protection) & MemoryPermission.ReadAndWrite;
-
- long tag = protection switch
+ if (guest)
{
- MemoryPermission.None => 0L,
- MemoryPermission.Write => 2L << PointerTagBit,
- _ => 3L << PointerTagBit,
- };
+ // Protection is inverted on software pages, since the default value is 0.
+ protection = (~protection) & MemoryPermission.ReadAndWrite;
- int pages = GetPagesCount(va, (uint)size, out va);
- ulong pageStart = va >> PageBits;
- long invTagMask = ~(0xffffL << 48);
-
- for (int page = 0; page < pages; page++)
- {
- ref long pageRef = ref _pageTable.GetRef<long>(pageStart * PteSize);
+ long tag = protection switch
+ {
+ MemoryPermission.None => 0L,
+ MemoryPermission.Write => 2L << PointerTagBit,
+ _ => 3L << PointerTagBit,
+ };
- long pte;
+ int pages = GetPagesCount(va, (uint)size, out va);
+ ulong pageStart = va >> PageBits;
+ long invTagMask = ~(0xffffL << 48);
- do
+ for (int page = 0; page < pages; page++)
{
- pte = Volatile.Read(ref pageRef);
- }
- while (pte != 0 && Interlocked.CompareExchange(ref pageRef, (pte & invTagMask) | tag, pte) != pte);
+ ref long pageRef = ref _pageTable.GetRef<long>(pageStart * PteSize);
+
+ long pte;
- pageStart++;
+ do
+ {
+ pte = Volatile.Read(ref pageRef);
+ }
+ while (pte != 0 && Interlocked.CompareExchange(ref pageRef, (pte & invTagMask) | tag, pte) != pte);
+
+ pageStart++;
+ }
+ }
+ else
+ {
+ _pages.TrackingReprotect(va, size, protection);
}
}
/// <inheritdoc/>
- public RegionHandle BeginTracking(ulong address, ulong size, int id)
+ public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
{
- return Tracking.BeginTracking(address, size, id);
+ return Tracking.BeginTracking(address, size, id, flags);
}
/// <inheritdoc/>
- public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id)
+ public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None)
{
- return Tracking.BeginGranularTracking(address, size, handles, granularity, id);
+ return Tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
}
/// <inheritdoc/>
@@ -572,8 +615,7 @@ namespace Ryujinx.Cpu.Jit
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
- /// <inheritdoc/>
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ private void SignalMemoryTrackingImpl(ulong va, ulong size, bool write, bool guest, bool precise = false, int? exemptId = null)
{
AssertValidAddressAndSize(va, size);
@@ -583,31 +625,47 @@ namespace Ryujinx.Cpu.Jit
return;
}
- // We emulate guard pages for software memory access. This makes for an easy transition to
- // tracking using host guard pages in future, but also supporting platforms where this is not possible.
-
- // Write tag includes read protection, since we don't have any read actions that aren't performed before write too.
- long tag = (write ? 3L : 1L) << PointerTagBit;
+ // If the memory tracking is coming from the guest, use the tag bits in the page table entry.
+ // Otherwise, use the managed page flags.
- int pages = GetPagesCount(va, (uint)size, out _);
- ulong pageStart = va >> PageBits;
-
- for (int page = 0; page < pages; page++)
+ if (guest)
{
- ref long pageRef = ref _pageTable.GetRef<long>(pageStart * PteSize);
+ // We emulate guard pages for software memory access. This makes for an easy transition to
+ // tracking using host guard pages in future, but also supporting platforms where this is not possible.
- long pte;
+ // Write tag includes read protection, since we don't have any read actions that aren't performed before write too.
+ long tag = (write ? 3L : 1L) << PointerTagBit;
- pte = Volatile.Read(ref pageRef);
+ int pages = GetPagesCount(va, (uint)size, out _);
+ ulong pageStart = va >> PageBits;
- if ((pte & tag) != 0)
+ for (int page = 0; page < pages; page++)
{
- Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId);
- break;
- }
+ ref long pageRef = ref _pageTable.GetRef<long>(pageStart * PteSize);
+
+ long pte;
+
+ pte = Volatile.Read(ref pageRef);
- pageStart++;
+ if ((pte & tag) != 0)
+ {
+ Tracking.VirtualMemoryEvent(va, size, write, precise: false, exemptId, true);
+ break;
+ }
+
+ pageStart++;
+ }
}
+ else
+ {
+ _pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
+ }
+ }
+
+ /// <inheritdoc/>
+ public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ {
+ SignalMemoryTrackingImpl(va, size, write, false, precise, exemptId);
}
private ulong PaToPte(ulong pa)