120 lines
5.9 KiB
C#
120 lines
5.9 KiB
C#
// Copyright (C) 2026 SharpEmu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
using SharpEmu.Core.Memory;
|
|
using SharpEmu.HLE;
|
|
using SharpEmu.HLE.GpuMemory;
|
|
using SharpEmu.HLE.Host;
|
|
using Xunit;
|
|
using static SharpEmu.Libs.Tests.Memory.HostViews.HostViewTestSupport;
|
|
|
|
namespace SharpEmu.Libs.Tests.Memory.GuestMemory;
|
|
|
|
[Collection(GuestMemoryStateCollection.Name)]
|
|
public sealed class ReleasedWriteFaultTests
|
|
{
|
|
private const GuestPageProtection ReadWrite = GuestPageProtection.Read | GuestPageProtection.Write;
|
|
|
|
[NativePageProtectionFact]
|
|
public void ReleasedWriteWatchPermitsOneRetryPerPageAndRestoration()
|
|
{
|
|
if (!Supported) return;
|
|
using var memory = new PhysicalVirtualMemory(viewHost: HostViewMemory.Create(), backingBytes: BackingSize);
|
|
using var manager = new GuestGpuMemory(memory);
|
|
var address = Map(memory, manager);
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
manager.Pages.AddWatch(address, 0x2000, blockReads: false);
|
|
manager.Pages.RemoveWatch(address, 0x2000, blockReads: false);
|
|
|
|
Assert.True(manager.TryResolveFault(FaultKind.Write, address));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address + 8));
|
|
Assert.True(manager.TryResolveFault(FaultKind.Write, address + 0x1000));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address + 0x1008));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address + 0x2000));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Read, address));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Execute, address));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Unknown, address));
|
|
|
|
manager.Pages.AddWatch(address, 0x1000, blockReads: false);
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
manager.Pages.RemoveWatch(address, 0x1000, blockReads: false);
|
|
Assert.True(manager.TryResolveFault(FaultKind.Write, address));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
}
|
|
|
|
[NativePageProtectionFact]
|
|
public void RemainingWatchAndHostProtectionPreventRetry()
|
|
{
|
|
if (!Supported) return;
|
|
using var memory = new PhysicalVirtualMemory(viewHost: HostViewMemory.Create(), backingBytes: BackingSize);
|
|
using var manager = new GuestGpuMemory(memory);
|
|
var address = Map(memory, manager);
|
|
manager.Pages.AddWatch(address, 0x1000, blockReads: false);
|
|
manager.Pages.AddWatch(address, 0x1000, blockReads: true);
|
|
manager.Pages.RemoveWatch(address, 0x1000, blockReads: false);
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
manager.Pages.RemoveWatch(address, 0x1000, blockReads: true);
|
|
|
|
Assert.True(memory.TryProtect(address, 0x1000, GuestPageProtection.None));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
Assert.True(memory.TryProtect(address, 0x1000, GuestPageProtection.Read));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
if (OperatingSystem.IsWindows())
|
|
{
|
|
Assert.True(PlatformMemory.ProtectRaw(address, 0x1000, 0x104, out _));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
}
|
|
|
|
Assert.True(manager.NoteProtected(address, 0x1000, GuestPageProtection.Read));
|
|
Assert.True(memory.TryProtect(address, 0x1000, ReadWrite));
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
Assert.True(manager.NoteProtected(address, 0x1000, ReadWrite));
|
|
Assert.True(manager.TryResolveFault(FaultKind.Write, address));
|
|
}
|
|
|
|
[NativePageProtectionFact]
|
|
public void NewMappingCannotUseThePreviousMappingsRestoration()
|
|
{
|
|
if (!Supported) return;
|
|
using var memory = new PhysicalVirtualMemory(viewHost: HostViewMemory.Create(), backingBytes: BackingSize);
|
|
using var manager = new GuestGpuMemory(memory);
|
|
var address = Map(memory, manager);
|
|
manager.Pages.AddWatch(address, 0x1000, blockReads: false);
|
|
manager.Pages.RemoveWatch(address, 0x1000, blockReads: false);
|
|
manager.Unregister(address, 4 * Segment);
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
Assert.True(memory.TryUnmapBacked(address, 4 * Segment));
|
|
Assert.True(memory.TryMapBacked(address, 4 * Segment, 0, ReadWrite, out _));
|
|
manager.Register(address, 4 * Segment, ReadWrite);
|
|
Assert.False(manager.TryResolveFault(FaultKind.Write, address));
|
|
}
|
|
|
|
[NativePageProtectionFact]
|
|
public void EachFaultingThreadCanRetryAReleasedWatch()
|
|
{
|
|
if (!Supported) return;
|
|
using var memory = new PhysicalVirtualMemory(viewHost: HostViewMemory.Create(), backingBytes: BackingSize);
|
|
using var manager = new GuestGpuMemory(memory);
|
|
var address = Map(memory, manager);
|
|
manager.Pages.AddWatch(address, 0x1000, blockReads: false);
|
|
manager.Pages.RemoveWatch(address, 0x1000, blockReads: false);
|
|
var results = new bool[4];
|
|
var threads = Enumerable.Range(0, 2).Select(index => new Thread(() =>
|
|
{
|
|
results[index * 2] = manager.TryResolveFault(FaultKind.Write, address);
|
|
results[index * 2 + 1] = manager.TryResolveFault(FaultKind.Write, address);
|
|
})).ToArray();
|
|
foreach (var thread in threads) thread.Start();
|
|
foreach (var thread in threads) Assert.True(thread.Join(TimeSpan.FromSeconds(10)));
|
|
Assert.Equal(new[] { true, false, true, false }, results);
|
|
}
|
|
|
|
private static ulong Map(PhysicalVirtualMemory memory, GuestGpuMemory manager)
|
|
{
|
|
Assert.True(memory.TryHoldRangeAtOrAbove(0x2_0000_0000, 4 * Segment, 4 * Segment, out var address));
|
|
Assert.True(memory.TryMapBacked(address, 4 * Segment, 0, ReadWrite, out _));
|
|
manager.Register(address, 4 * Segment, ReadWrite);
|
|
return address;
|
|
}
|
|
}
|