aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs62
-rw-r--r--src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs33
-rw-r--r--src/Ryujinx.Cpu/Jit/MemoryManager.cs21
-rw-r--r--src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs6
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs46
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs10
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs6
-rw-r--r--src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs20
-rw-r--r--src/Ryujinx.Memory/AddressSpaceManager.cs5
-rw-r--r--src/Ryujinx.Memory/IVirtualMemoryManager.cs14
-rw-r--r--src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs5
11 files changed, 170 insertions, 58 deletions
diff --git a/src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs b/src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs
new file mode 100644
index 00000000..876597b7
--- /dev/null
+++ b/src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
+
+namespace Ryujinx.Cpu.AppleHv
+{
+ static class HvCodePatcher
+ {
+ private const uint XMask = 0x3f808000u;
+ private const uint XValue = 0x8000000u;
+
+ private const uint ZrIndex = 31u;
+
+ public static void RewriteUnorderedExclusiveInstructions(Span<byte> code)
+ {
+ Span<uint> codeUint = MemoryMarshal.Cast<byte, uint>(code);
+ Span<Vector128<uint>> codeVector = MemoryMarshal.Cast<byte, Vector128<uint>>(code);
+
+ Vector128<uint> mask = Vector128.Create(XMask);
+ Vector128<uint> value = Vector128.Create(XValue);
+
+ for (int index = 0; index < codeVector.Length; index++)
+ {
+ Vector128<uint> v = codeVector[index];
+
+ if (Vector128.EqualsAny(Vector128.BitwiseAnd(v, mask), value))
+ {
+ int baseIndex = index * 4;
+
+ for (int instIndex = baseIndex; instIndex < baseIndex + 4; instIndex++)
+ {
+ ref uint inst = ref codeUint[instIndex];
+
+ if ((inst & XMask) != XValue)
+ {
+ continue;
+ }
+
+ bool isPair = (inst & (1u << 21)) != 0;
+ bool isLoad = (inst & (1u << 22)) != 0;
+
+ uint rt2 = (inst >> 10) & 0x1fu;
+ uint rs = (inst >> 16) & 0x1fu;
+
+ if (isLoad && rs != ZrIndex)
+ {
+ continue;
+ }
+
+ if (!isPair && rt2 != ZrIndex)
+ {
+ continue;
+ }
+
+ // Set the ordered flag.
+ inst |= 1u << 15;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
index d5ce817a..947c3710 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
@@ -128,21 +128,6 @@ namespace Ryujinx.Cpu.AppleHv
}
}
-#pragma warning disable IDE0051 // Remove unused private member
- /// <summary>
- /// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
- /// </summary>
- /// <param name="va">Virtual address of the range</param>
- /// <param name="size">Size of the range in bytes</param>
- private void AssertMapped(ulong va, ulong size)
- {
- if (!ValidateAddressAndSize(va, size) || !IsRangeMappedImpl(va, size))
- {
- throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
- }
- }
-#pragma warning restore IDE0051
-
/// <inheritdoc/>
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
{
@@ -737,6 +722,24 @@ namespace Ryujinx.Cpu.AppleHv
}
/// <inheritdoc/>
+ public void Reprotect(ulong va, ulong size, MemoryPermission protection)
+ {
+ if (protection.HasFlag(MemoryPermission.Execute))
+ {
+ // Some applications use unordered exclusive memory access instructions
+ // where it is not valid to do so, leading to memory re-ordering that
+ // makes the code behave incorrectly on some CPUs.
+ // To work around this, we force all such accesses to be ordered.
+
+ using WritableRegion writableRegion = GetWritableRegion(va, (int)size);
+
+ HvCodePatcher.RewriteUnorderedExclusiveInstructions(writableRegion.Memory.Span);
+ }
+
+ // TODO
+ }
+
+ /// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
// Protection is inverted on software pages, since the default value is 0.
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
index 1c27e97f..912e3f7e 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
@@ -575,22 +575,15 @@ namespace Ryujinx.Cpu.Jit
}
}
-#pragma warning disable IDE0051 // Remove unused private member
- private ulong GetPhysicalAddress(ulong va)
+ private ulong GetPhysicalAddressInternal(ulong va)
{
- // We return -1L if the virtual address is invalid or unmapped.
- if (!ValidateAddress(va) || !IsMapped(va))
- {
- return ulong.MaxValue;
- }
-
- return GetPhysicalAddressInternal(va);
+ return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
}
-#pragma warning restore IDE0051
- private ulong GetPhysicalAddressInternal(ulong va)
+ /// <inheritdoc/>
+ public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
- return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
+ // TODO
}
/// <inheritdoc/>
@@ -698,9 +691,5 @@ namespace Ryujinx.Cpu.Jit
/// Disposes of resources used by the memory manager.
/// </summary>
protected override void Destroy() => _pageTable.Dispose();
-
-#pragma warning disable IDE0051 // Remove unused private member
- private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
-#pragma warning restore IDE0051
}
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
index 010a0bc2..6d32787a 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
@@ -616,6 +616,12 @@ namespace Ryujinx.Cpu.Jit
}
/// <inheritdoc/>
+ public void Reprotect(ulong va, ulong size, MemoryPermission protection)
+ {
+ // TODO
+ }
+
+ /// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
// Protection is inverted on software pages, since the default value is 0.
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs
new file mode 100644
index 00000000..32734574
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs
@@ -0,0 +1,46 @@
+using Ryujinx.Memory;
+using System;
+
+namespace Ryujinx.HLE.HOS.Kernel.Memory
+{
+ [Flags]
+ enum KMemoryPermission : uint
+ {
+ None = 0,
+ UserMask = Read | Write | Execute,
+ Mask = uint.MaxValue,
+
+ Read = 1 << 0,
+ Write = 1 << 1,
+ Execute = 1 << 2,
+ DontCare = 1 << 28,
+
+ ReadAndWrite = Read | Write,
+ ReadAndExecute = Read | Execute,
+ }
+
+ static class KMemoryPermissionExtensions
+ {
+ public static MemoryPermission Convert(this KMemoryPermission permission)
+ {
+ MemoryPermission output = MemoryPermission.None;
+
+ if (permission.HasFlag(KMemoryPermission.Read))
+ {
+ output = MemoryPermission.Read;
+ }
+
+ if (permission.HasFlag(KMemoryPermission.Write))
+ {
+ output |= MemoryPermission.Write;
+ }
+
+ if (permission.HasFlag(KMemoryPermission.Execute))
+ {
+ output |= MemoryPermission.Execute;
+ }
+
+ return output;
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs
index dcfc8f4f..4cd3e6fd 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTable.cs
@@ -203,15 +203,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// <inheritdoc/>
protected override Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
{
- // TODO.
+ _cpuMemory.Reprotect(address, pagesCount * PageSize, permission.Convert());
+
return Result.Success;
}
/// <inheritdoc/>
- protected override Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission)
+ protected override Result ReprotectAndFlush(ulong address, ulong pagesCount, KMemoryPermission permission)
{
- // TODO.
- return Result.Success;
+ // TODO: Flush JIT cache.
+
+ return Reprotect(address, pagesCount, permission);
}
/// <inheritdoc/>
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
index 2b00f802..2b6d4e4e 100644
--- a/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
+++ b/src/Ryujinx.HLE/HOS/Kernel/Memory/KPageTableBase.cs
@@ -1255,7 +1255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if ((oldPermission & KMemoryPermission.Execute) != 0)
{
- result = ReprotectWithAttributes(address, pagesCount, permission);
+ result = ReprotectAndFlush(address, pagesCount, permission);
}
else
{
@@ -3036,13 +3036,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
protected abstract Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission);
/// <summary>
- /// Changes the permissions of a given virtual memory region.
+ /// Changes the permissions of a given virtual memory region, while also flushing the cache.
/// </summary>
/// <param name="address">Virtual address of the region to have the permission changes</param>
/// <param name="pagesCount">Number of pages to have their permissions changed</param>
/// <param name="permission">New permission</param>
/// <returns>Result of the permission change operation</returns>
- protected abstract Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission);
+ protected abstract Result ReprotectAndFlush(ulong address, ulong pagesCount, KMemoryPermission permission);
/// <summary>
/// Alerts the memory tracking that a given region has been read from or written to.
diff --git a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs b/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
deleted file mode 100644
index 068cdbb8..00000000
--- a/src/Ryujinx.HLE/HOS/Kernel/Memory/MemoryPermission.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-
-namespace Ryujinx.HLE.HOS.Kernel.Memory
-{
- [Flags]
- enum KMemoryPermission : uint
- {
- None = 0,
- UserMask = Read | Write | Execute,
- Mask = uint.MaxValue,
-
- Read = 1 << 0,
- Write = 1 << 1,
- Execute = 1 << 2,
- DontCare = 1 << 28,
-
- ReadAndWrite = Read | Write,
- ReadAndExecute = Read | Execute,
- }
-}
diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs
index 65b4d48f..b8d48365 100644
--- a/src/Ryujinx.Memory/AddressSpaceManager.cs
+++ b/src/Ryujinx.Memory/AddressSpaceManager.cs
@@ -456,6 +456,11 @@ namespace Ryujinx.Memory
}
/// <inheritdoc/>
+ public void Reprotect(ulong va, ulong size, MemoryPermission protection)
+ {
+ }
+
+ /// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
throw new NotImplementedException();
diff --git a/src/Ryujinx.Memory/IVirtualMemoryManager.cs b/src/Ryujinx.Memory/IVirtualMemoryManager.cs
index edbfc885..8c9ca168 100644
--- a/src/Ryujinx.Memory/IVirtualMemoryManager.cs
+++ b/src/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -104,6 +104,12 @@ namespace Ryujinx.Memory
/// <returns>True if the data was changed, false otherwise</returns>
bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data);
+ /// <summary>
+ /// Fills the specified memory region with the value specified in <paramref name="value"/>.
+ /// </summary>
+ /// <param name="va">Virtual address to fill the value into</param>
+ /// <param name="size">Size of the memory region to fill</param>
+ /// <param name="value">Value to fill with</param>
void Fill(ulong va, ulong size, byte value)
{
const int MaxChunkSize = 1 << 24;
@@ -195,6 +201,14 @@ namespace Ryujinx.Memory
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
/// <summary>
+ /// Reprotect a region of virtual memory for guest access.
+ /// </summary>
+ /// <param name="va">Virtual address base</param>
+ /// <param name="size">Size of the region to protect</param>
+ /// <param name="protection">Memory protection to set</param>
+ void Reprotect(ulong va, ulong size, MemoryPermission protection);
+
+ /// <summary>
/// Reprotect a region of virtual memory for tracking.
/// </summary>
/// <param name="va">Virtual address base</param>
diff --git a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
index 59dc1a52..435bb35a 100644
--- a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
+++ b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs
@@ -102,6 +102,11 @@ namespace Ryujinx.Tests.Memory
throw new NotImplementedException();
}
+ public void Reprotect(ulong va, ulong size, MemoryPermission protection)
+ {
+ throw new NotImplementedException();
+ }
+
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
OnProtect?.Invoke(va, size, protection);