mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-25 06:06:00 +00:00
Force all exclusive memory accesses to be ordered on AppleHv (#5898)
* Implement reprotect method on virtual memory manager (currently stubbed) * Force all exclusive memory accesses to be ordered on AppleHv * Format whitespace * Fix test build * Fix comment copy/paste * Fix wrong bit for isLoad * Update src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs Co-authored-by: riperiperi <rhy3756547@hotmail.com> --------- Co-authored-by: riperiperi <rhy3756547@hotmail.com>
This commit is contained in:
parent
623604c391
commit
815819767c
11 changed files with 171 additions and 59 deletions
62
src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs
Normal file
62
src/Ryujinx.Cpu/AppleHv/HvCodePatcher.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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/>
|
/// <inheritdoc/>
|
||||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||||
{
|
{
|
||||||
|
@ -736,6 +721,24 @@ namespace Ryujinx.Cpu.AppleHv
|
||||||
return (int)(vaSpan / PageSize);
|
return (int)(vaSpan / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
{
|
{
|
||||||
|
|
|
@ -575,24 +575,17 @@ namespace Ryujinx.Cpu.Jit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable IDE0051 // Remove unused private member
|
|
||||||
private ulong GetPhysicalAddress(ulong va)
|
|
||||||
{
|
|
||||||
// We return -1L if the virtual address is invalid or unmapped.
|
|
||||||
if (!ValidateAddress(va) || !IsMapped(va))
|
|
||||||
{
|
|
||||||
return ulong.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetPhysicalAddressInternal(va);
|
|
||||||
}
|
|
||||||
#pragma warning restore IDE0051
|
|
||||||
|
|
||||||
private ulong GetPhysicalAddressInternal(ulong va)
|
private ulong GetPhysicalAddressInternal(ulong va)
|
||||||
{
|
{
|
||||||
return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
|
return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
{
|
{
|
||||||
|
@ -698,9 +691,5 @@ namespace Ryujinx.Cpu.Jit
|
||||||
/// Disposes of resources used by the memory manager.
|
/// Disposes of resources used by the memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected override void Destroy() => _pageTable.Dispose();
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -615,6 +615,12 @@ namespace Ryujinx.Cpu.Jit
|
||||||
return (int)(vaSpan / PageSize);
|
return (int)(vaSpan / PageSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
{
|
{
|
||||||
|
|
46
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs
Normal file
46
src/Ryujinx.HLE/HOS/Kernel/Memory/KMemoryPermission.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -203,15 +203,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
|
protected override Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
// TODO.
|
_cpuMemory.Reprotect(address, pagesCount * PageSize, permission.Convert());
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission)
|
protected override Result ReprotectAndFlush(ulong address, ulong pagesCount, KMemoryPermission permission)
|
||||||
{
|
{
|
||||||
// TODO.
|
// TODO: Flush JIT cache.
|
||||||
return Result.Success;
|
|
||||||
|
return Reprotect(address, pagesCount, permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
@ -1255,7 +1255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
|
|
||||||
if ((oldPermission & KMemoryPermission.Execute) != 0)
|
if ((oldPermission & KMemoryPermission.Execute) != 0)
|
||||||
{
|
{
|
||||||
result = ReprotectWithAttributes(address, pagesCount, permission);
|
result = ReprotectAndFlush(address, pagesCount, permission);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3036,13 +3036,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||||
protected abstract Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission);
|
protected abstract Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission);
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
/// <param name="address">Virtual address of the region to have the permission changes</param>
|
/// <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="pagesCount">Number of pages to have their permissions changed</param>
|
||||||
/// <param name="permission">New permission</param>
|
/// <param name="permission">New permission</param>
|
||||||
/// <returns>Result of the permission change operation</returns>
|
/// <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>
|
/// <summary>
|
||||||
/// Alerts the memory tracking that a given region has been read from or written to.
|
/// Alerts the memory tracking that a given region has been read from or written to.
|
||||||
|
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -455,6 +455,11 @@ namespace Ryujinx.Memory
|
||||||
return _pageTable.Read(va) + (nuint)(va & PageMask);
|
return _pageTable.Read(va) + (nuint)(va & PageMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,6 +104,12 @@ namespace Ryujinx.Memory
|
||||||
/// <returns>True if the data was changed, false otherwise</returns>
|
/// <returns>True if the data was changed, false otherwise</returns>
|
||||||
bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data);
|
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)
|
void Fill(ulong va, ulong size, byte value)
|
||||||
{
|
{
|
||||||
const int MaxChunkSize = 1 << 24;
|
const int MaxChunkSize = 1 << 24;
|
||||||
|
@ -194,6 +200,14 @@ namespace Ryujinx.Memory
|
||||||
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
|
||||||
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
|
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>
|
/// <summary>
|
||||||
/// Reprotect a region of virtual memory for tracking.
|
/// Reprotect a region of virtual memory for tracking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -102,6 +102,11 @@ namespace Ryujinx.Tests.Memory
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
|
||||||
{
|
{
|
||||||
OnProtect?.Invoke(va, size, protection);
|
OnProtect?.Invoke(va, size, protection);
|
||||||
|
|
Loading…
Reference in a new issue