Play musical names with MemoryBlock
The refactoring of platform abstractions should never have changed the name of the public interface. No one cares about MemoryBlockWindows or MemoryBlockUnix - those names are impl details.
This commit is contained in:
parent
7bda234fe1
commit
09f75c281c
|
@ -132,14 +132,14 @@ namespace BizHawk.BizInvoke
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly MemoryBlockBase _memory;
|
private readonly MemoryBlock _memory;
|
||||||
private readonly object _sync = new object();
|
private readonly object _sync = new object();
|
||||||
private readonly WeakReference[] _refs;
|
private readonly WeakReference[] _refs;
|
||||||
|
|
||||||
public SysVHostMsGuest()
|
public SysVHostMsGuest()
|
||||||
{
|
{
|
||||||
int size = 4 * 1024 * 1024;
|
int size = 4 * 1024 * 1024;
|
||||||
_memory = MemoryBlockBase.CallPlatformCtor((ulong)size);
|
_memory = MemoryBlock.Create((ulong)size);
|
||||||
_memory.Activate();
|
_memory.Activate();
|
||||||
_refs = new WeakReference[size / BlockSize];
|
_refs = new WeakReference[size / BlockSize];
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ namespace BizHawk.BizInvoke
|
||||||
|
|
||||||
private void WriteThunk(byte[] data, int placeholderIndex, IntPtr p, int index)
|
private void WriteThunk(byte[] data, int placeholderIndex, IntPtr p, int index)
|
||||||
{
|
{
|
||||||
_memory.Protect(_memory.Start, _memory.Size, MemoryBlockBase.Protection.RW);
|
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RW);
|
||||||
var ss = _memory.GetStream(_memory.Start + (ulong)index * BlockSize, BlockSize, true);
|
var ss = _memory.GetStream(_memory.Start + (ulong)index * BlockSize, BlockSize, true);
|
||||||
ss.Write(data, 0, data.Length);
|
ss.Write(data, 0, data.Length);
|
||||||
for (int i = data.Length; i < BlockSize; i++)
|
for (int i = data.Length; i < BlockSize; i++)
|
||||||
|
@ -193,7 +193,7 @@ namespace BizHawk.BizInvoke
|
||||||
ss.Position = placeholderIndex;
|
ss.Position = placeholderIndex;
|
||||||
var bw = new BinaryWriter(ss);
|
var bw = new BinaryWriter(ss);
|
||||||
bw.Write((long)p);
|
bw.Write((long)p);
|
||||||
_memory.Protect(_memory.Start, _memory.Size, MemoryBlockBase.Protection.RX);
|
_memory.Protect(_memory.Start, _memory.Size, MemoryBlock.Protection.RX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr GetThunkAddress(int index)
|
private IntPtr GetThunkAddress(int index)
|
||||||
|
|
|
@ -1,164 +1,102 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using BizHawk.Common;
|
||||||
|
|
||||||
namespace BizHawk.BizInvoke
|
namespace BizHawk.BizInvoke
|
||||||
{
|
{
|
||||||
public sealed class MemoryBlock : MemoryBlockBase
|
public abstract class MemoryBlock : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>allocate <paramref name="size"/> bytes starting at a particular address <paramref name="start"/></summary>
|
||||||
/// handle returned by CreateFileMapping
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> is not aligned or <paramref name="size"/> is <c>0</c></exception>
|
||||||
/// </summary>
|
protected MemoryBlock(ulong start, ulong size)
|
||||||
private IntPtr _handle;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="MemoryBlockBase(ulong,ulong)"/>
|
|
||||||
/// <exception cref="InvalidOperationException">failed to create file mapping</exception>
|
|
||||||
public MemoryBlock(ulong start, ulong size) : base(start, size)
|
|
||||||
{
|
{
|
||||||
_handle = Kernel32.CreateFileMapping(
|
if (!WaterboxUtils.Aligned(start))
|
||||||
Kernel32.INVALID_HANDLE_VALUE,
|
throw new ArgumentOutOfRangeException(nameof(start), start, "start address must be aligned");
|
||||||
IntPtr.Zero,
|
if (size == 0)
|
||||||
Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit,
|
throw new ArgumentOutOfRangeException(nameof(size), size, "cannot create 0-length block");
|
||||||
(uint)(Size >> 32),
|
Start = start;
|
||||||
(uint)Size,
|
Size = WaterboxUtils.AlignUp(size);
|
||||||
null
|
EndExclusive = Start + Size;
|
||||||
);
|
_pageData = new Protection[GetPage(EndExclusive - 1) + 1];
|
||||||
if (_handle == IntPtr.Zero) throw new InvalidOperationException($"{nameof(Kernel32.CreateFileMapping)}() returned NULL");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="true"/> or failed to map file view</exception>
|
/// <summary>stores last set memory protection value for each page</summary>
|
||||||
public override void Activate()
|
protected readonly Protection[] _pageData;
|
||||||
|
|
||||||
|
/// <summary>end address of the memory block (not part of the block; class invariant: equal to <see cref="Start"/> + <see cref="Size"/>)</summary>
|
||||||
|
public readonly ulong EndExclusive;
|
||||||
|
|
||||||
|
/// <summary>total size of the memory block</summary>
|
||||||
|
public readonly ulong Size;
|
||||||
|
|
||||||
|
/// <summary>starting address of the memory block</summary>
|
||||||
|
public readonly ulong Start;
|
||||||
|
|
||||||
|
/// <summary>snapshot for XOR buffer</summary>
|
||||||
|
protected byte[] _snapshot;
|
||||||
|
|
||||||
|
/// <summary>true if this is currently swapped in</summary>
|
||||||
|
public bool Active { get; protected set; }
|
||||||
|
|
||||||
|
public byte[] XorHash { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>get a page index within the block</summary>
|
||||||
|
protected int GetPage(ulong addr)
|
||||||
{
|
{
|
||||||
if (Active)
|
if (addr < Start || EndExclusive <= addr) throw new ArgumentOutOfRangeException(nameof(addr), addr, "invalid address");
|
||||||
throw new InvalidOperationException("Already active");
|
return (int) ((addr - Start) >> WaterboxUtils.PageShift);
|
||||||
if (Kernel32.MapViewOfFileEx(
|
|
||||||
_handle,
|
|
||||||
Kernel32.FileMapAccessType.Read | Kernel32.FileMapAccessType.Write | Kernel32.FileMapAccessType.Execute,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
Z.UU(Size),
|
|
||||||
Z.US(Start)
|
|
||||||
) != Z.US(Start))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL");
|
|
||||||
}
|
|
||||||
ProtectAll();
|
|
||||||
Active = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="false"/> or failed to unmap file view</exception>
|
/// <summary>get a start address for a page index within the block</summary>
|
||||||
public override void Deactivate()
|
protected ulong GetStartAddr(int page) => ((ulong) page << WaterboxUtils.PageShift) + Start;
|
||||||
|
|
||||||
|
/// <summary>Get a stream that can be used to read or write from part of the block. Does not check for or change <see cref="Protect"/>!</summary>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/> - <c>1</c>) are outside [<see cref="Start"/>, <see cref="EndExclusive"/>), the range of the block</exception>
|
||||||
|
public Stream GetStream(ulong start, ulong length, bool writer)
|
||||||
{
|
{
|
||||||
if (!Active)
|
if (start < Start)
|
||||||
throw new InvalidOperationException("Not active");
|
throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address");
|
||||||
if (!Kernel32.UnmapViewOfFile(Z.US(Start)))
|
if (EndExclusive < start + length)
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
|
throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address");
|
||||||
Active = false;
|
return new MemoryViewStream(!writer, writer, (long) start, (long) length, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException">snapshot already taken, <see cref="MemoryBlockBase.Active"/> is <see langword="false"/>, or failed to make memory read-only</exception>
|
/// <summary>get a stream that can be used to read or write from part of the block. both reads and writes will be XORed against an earlier recorded snapshot</summary>
|
||||||
public override void SaveXorSnapshot()
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/> - <c>1</c>) are outside [<see cref="Start"/>, <see cref="EndExclusive"/>), the range of the block</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">no snapshot taken (haven't called <see cref="SaveXorSnapshot"/>)</exception>
|
||||||
|
public Stream GetXorStream(ulong start, ulong length, bool writer)
|
||||||
{
|
{
|
||||||
if (_snapshot != null)
|
if (start < Start) throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address");
|
||||||
throw new InvalidOperationException("Snapshot already taken");
|
if (EndExclusive < start + length) throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address");
|
||||||
if (!Active)
|
if (_snapshot == null) throw new InvalidOperationException("No snapshot taken!");
|
||||||
throw new InvalidOperationException("Not active");
|
return new MemoryViewXorStream(!writer, writer, (long) start, (long) length, this, _snapshot, (long) (start - Start));
|
||||||
|
|
||||||
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
|
|
||||||
// that to complicate things
|
|
||||||
Kernel32.MemoryProtection old;
|
|
||||||
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
|
||||||
|
|
||||||
_snapshot = new byte[Size];
|
|
||||||
var ds = new MemoryStream(_snapshot, true);
|
|
||||||
var ss = GetStream(Start, Size, false);
|
|
||||||
ss.CopyTo(ds);
|
|
||||||
XorHash = WaterboxUtils.Hash(_snapshot);
|
|
||||||
|
|
||||||
ProtectAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="false"/> or failed to make memory read-only</exception>
|
/// <summary>activate the memory block, swapping it in at the pre-specified address</summary>
|
||||||
public override byte[] FullHash()
|
public abstract void Activate();
|
||||||
{
|
|
||||||
if (!Active)
|
|
||||||
throw new InvalidOperationException("Not active");
|
|
||||||
// temporarily switch the entire block to `R`
|
|
||||||
Kernel32.MemoryProtection old;
|
|
||||||
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
|
||||||
var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
|
|
||||||
ProtectAll();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
|
/// <summary>deactivate the memory block, removing it from RAM but leaving it immediately available to swap back in</summary>
|
||||||
{
|
public abstract void Deactivate();
|
||||||
Kernel32.MemoryProtection p;
|
|
||||||
switch (prot)
|
|
||||||
{
|
|
||||||
case Protection.None: p = Kernel32.MemoryProtection.NOACCESS; break;
|
|
||||||
case Protection.R: p = Kernel32.MemoryProtection.READONLY; break;
|
|
||||||
case Protection.RW: p = Kernel32.MemoryProtection.READWRITE; break;
|
|
||||||
case Protection.RX: p = Kernel32.MemoryProtection.EXECUTE_READ; break;
|
|
||||||
default: throw new ArgumentOutOfRangeException(nameof(prot));
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ProtectAll()
|
/// <summary>take a hash of the current full contents of the block, including unreadable areas</summary>
|
||||||
{
|
public abstract byte[] FullHash();
|
||||||
int ps = 0;
|
|
||||||
for (int i = 0; i < _pageData.Length; i++)
|
|
||||||
{
|
|
||||||
if (i == _pageData.Length - 1 || _pageData[i] != _pageData[i + 1])
|
|
||||||
{
|
|
||||||
var p = GetKernelMemoryProtectionValue(_pageData[i]);
|
|
||||||
ulong zstart = GetStartAddr(ps);
|
|
||||||
ulong zend = GetStartAddr(i + 1);
|
|
||||||
Kernel32.MemoryProtection old;
|
|
||||||
if (!Kernel32.VirtualProtect(Z.UU(zstart), Z.UU(zend - zstart), p, out old))
|
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
|
||||||
ps = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException">failed to protect memory</exception>
|
/// <summary>set r/w/x protection on a portion of memory. rounded to encompassing pages</summary>
|
||||||
public override void Protect(ulong start, ulong length, Protection prot)
|
public abstract void Protect(ulong start, ulong length, Protection prot);
|
||||||
{
|
|
||||||
if (length == 0)
|
|
||||||
return;
|
|
||||||
int pstart = GetPage(start);
|
|
||||||
int pend = GetPage(start + length - 1);
|
|
||||||
|
|
||||||
var p = GetKernelMemoryProtectionValue(prot);
|
/// <summary>restore all recorded protections</summary>
|
||||||
for (int i = pstart; i <= pend; i++)
|
protected abstract void ProtectAll();
|
||||||
_pageData[i] = prot; // also store the value for later use
|
|
||||||
|
|
||||||
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
/// <summary>take a snapshot of the entire memory block's contents, for use in <see cref="GetXorStream"/></summary>
|
||||||
{
|
public abstract void SaveXorSnapshot();
|
||||||
var computedStart = WaterboxUtils.AlignDown(start);
|
|
||||||
var computedEnd = WaterboxUtils.AlignUp(start + length);
|
|
||||||
var computedLength = computedEnd - computedStart;
|
|
||||||
|
|
||||||
Kernel32.MemoryProtection old;
|
public abstract void Dispose(bool disposing);
|
||||||
if (!Kernel32.VirtualProtect(Z.UU(computedStart),
|
|
||||||
Z.UU(computedLength), p, out old))
|
|
||||||
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Dispose(bool disposing)
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_handle != IntPtr.Zero)
|
Dispose(true);
|
||||||
{
|
GC.SuppressFinalize(this);
|
||||||
if (Active)
|
|
||||||
Deactivate();
|
|
||||||
Kernel32.CloseHandle(_handle);
|
|
||||||
_handle = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~MemoryBlock()
|
~MemoryBlock()
|
||||||
|
@ -166,73 +104,152 @@ namespace BizHawk.BizInvoke
|
||||||
Dispose(false);
|
Dispose(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Kernel32
|
/// <summary>allocate <paramref name="size"/> bytes starting at a particular address <paramref name="start"/></summary>
|
||||||
{
|
public static MemoryBlock Create(ulong start, ulong size) => OSTailoredCode.IsUnixHost
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
? (MemoryBlock) new MemoryBlockUnix(start, size)
|
||||||
public static extern bool VirtualProtect(UIntPtr lpAddress, UIntPtr dwSize,
|
: new MemoryBlockWindows(start, size);
|
||||||
MemoryProtection flNewProtect, out MemoryProtection lpflOldProtect);
|
|
||||||
|
|
||||||
[Flags]
|
/// <summary>allocate <paramref name="size"/> bytes at any address</summary>
|
||||||
public enum MemoryProtection : uint
|
public static MemoryBlock Create(ulong size) => Create(0, size);
|
||||||
|
|
||||||
|
/// <summary>Memory protection constant</summary>
|
||||||
|
public enum Protection : byte { None, R, RW, RX }
|
||||||
|
|
||||||
|
private class MemoryViewStream : Stream
|
||||||
{
|
{
|
||||||
EXECUTE = 0x10,
|
public MemoryViewStream(bool readable, bool writable, long ptr, long length, MemoryBlock owner)
|
||||||
EXECUTE_READ = 0x20,
|
{
|
||||||
EXECUTE_READWRITE = 0x40,
|
_readable = readable;
|
||||||
EXECUTE_WRITECOPY = 0x80,
|
_writable = writable;
|
||||||
NOACCESS = 0x01,
|
_ptr = ptr;
|
||||||
READONLY = 0x02,
|
_length = length;
|
||||||
READWRITE = 0x04,
|
_owner = owner;
|
||||||
WRITECOPY = 0x08,
|
_pos = 0;
|
||||||
GUARD_Modifierflag = 0x100,
|
|
||||||
NOCACHE_Modifierflag = 0x200,
|
|
||||||
WRITECOMBINE_Modifierflag = 0x400
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
private readonly long _length;
|
||||||
public static extern IntPtr CreateFileMapping(
|
private readonly MemoryBlock _owner;
|
||||||
IntPtr hFile,
|
private readonly long _ptr;
|
||||||
IntPtr lpFileMappingAttributes,
|
private readonly bool _readable;
|
||||||
FileMapProtection flProtect,
|
private readonly bool _writable;
|
||||||
uint dwMaximumSizeHigh,
|
|
||||||
uint dwMaximumSizeLow,
|
|
||||||
string lpName);
|
|
||||||
|
|
||||||
[Flags]
|
private long _pos;
|
||||||
public enum FileMapProtection : uint
|
|
||||||
|
public override bool CanRead => _readable;
|
||||||
|
public override bool CanSeek => true;
|
||||||
|
public override bool CanWrite => _writable;
|
||||||
|
public override long Length => _length;
|
||||||
|
public override long Position
|
||||||
{
|
{
|
||||||
PageReadonly = 0x02,
|
get => _pos;
|
||||||
PageReadWrite = 0x04,
|
set
|
||||||
PageWriteCopy = 0x08,
|
{
|
||||||
PageExecuteRead = 0x20,
|
if (value < 0 || _length < value) throw new ArgumentOutOfRangeException();
|
||||||
PageExecuteReadWrite = 0x40,
|
_pos = value;
|
||||||
SectionCommit = 0x8000000,
|
}
|
||||||
SectionImage = 0x1000000,
|
|
||||||
SectionNoCache = 0x10000000,
|
|
||||||
SectionReserve = 0x4000000,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
private void EnsureNotDisposed()
|
||||||
public static extern bool CloseHandle(IntPtr hObject);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
|
||||||
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
public static extern IntPtr MapViewOfFileEx(IntPtr hFileMappingObject,
|
|
||||||
FileMapAccessType dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
|
|
||||||
UIntPtr dwNumberOfBytesToMap, IntPtr lpBaseAddress);
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum FileMapAccessType : uint
|
|
||||||
{
|
{
|
||||||
Copy = 0x01,
|
if (_owner.Start == 0)
|
||||||
Write = 0x02,
|
throw new ObjectDisposedException(nameof(MemoryBlock));
|
||||||
Read = 0x04,
|
|
||||||
AllAccess = 0x08,
|
|
||||||
Execute = 0x20,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly IntPtr INVALID_HANDLE_VALUE = Z.US(0xffffffffffffffff);
|
public override void Flush() {}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (!_readable) throw new InvalidOperationException();
|
||||||
|
if (count < 0 || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
||||||
|
EnsureNotDisposed();
|
||||||
|
|
||||||
|
count = (int) Math.Min(count, _length - _pos);
|
||||||
|
Marshal.Copy(Z.SS(_ptr + _pos), buffer, offset, count);
|
||||||
|
_pos += count;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
long newpos;
|
||||||
|
switch (origin)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case SeekOrigin.Begin:
|
||||||
|
newpos = offset;
|
||||||
|
break;
|
||||||
|
case SeekOrigin.Current:
|
||||||
|
newpos = _pos + offset;
|
||||||
|
break;
|
||||||
|
case SeekOrigin.End:
|
||||||
|
newpos = _length + offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Position = newpos;
|
||||||
|
return newpos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (!_writable) throw new InvalidOperationException();
|
||||||
|
if (count < 0 || _length - _pos < count || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
||||||
|
EnsureNotDisposed();
|
||||||
|
|
||||||
|
Marshal.Copy(buffer, offset, Z.SS(_ptr + _pos), count);
|
||||||
|
_pos += count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MemoryViewXorStream : MemoryViewStream
|
||||||
|
{
|
||||||
|
public MemoryViewXorStream(bool readable, bool writable, long ptr, long length, MemoryBlock owner, byte[] initial, long offset)
|
||||||
|
: base(readable, writable, ptr, length, owner)
|
||||||
|
{
|
||||||
|
_initial = initial;
|
||||||
|
_offset = (int) offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>the initial data to XOR against for both reading and writing</summary>
|
||||||
|
private readonly byte[] _initial;
|
||||||
|
|
||||||
|
/// <summary>offset into the XOR data that this stream is representing</summary>
|
||||||
|
private readonly int _offset;
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var pos = (int) Position;
|
||||||
|
count = base.Read(buffer, offset, count);
|
||||||
|
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
var pos = (int) Position;
|
||||||
|
if (count < 0 || Length - pos < count || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
||||||
|
|
||||||
|
// is mutating the buffer passed to Stream.Write kosher?
|
||||||
|
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
||||||
|
base.Write(buffer, offset, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <remarks>bounds check already done by calling method i.e. in <see cref="MemoryViewStream.Read">base.Read</see> (for <see cref="Read"/>) or in <see cref="Write"/></remarks>
|
||||||
|
private static unsafe void XorTransform(byte[] source, int sourceOffset, byte[] dest, int destOffset, int length)
|
||||||
|
{
|
||||||
|
// TODO: C compilers can make this pretty snappy, but can the C# jitter? Or do we need intrinsics
|
||||||
|
fixed (byte* _s = source, _d = dest)
|
||||||
|
{
|
||||||
|
byte* s = _s + sourceOffset;
|
||||||
|
byte* d = _d + destOffset;
|
||||||
|
byte* sEnd = s + length;
|
||||||
|
while (s < sEnd) *d++ ^= *s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,255 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using BizHawk.Common;
|
|
||||||
|
|
||||||
namespace BizHawk.BizInvoke
|
|
||||||
{
|
|
||||||
public abstract class MemoryBlockBase : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>allocate <paramref name="size"/> bytes starting at a particular address <paramref name="start"/></summary>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> is not aligned or <paramref name="size"/> is <c>0</c></exception>
|
|
||||||
protected MemoryBlockBase(ulong start, ulong size)
|
|
||||||
{
|
|
||||||
if (!WaterboxUtils.Aligned(start))
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(start), start, "start address must be aligned");
|
|
||||||
if (size == 0)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(size), size, "cannot create 0-length block");
|
|
||||||
Start = start;
|
|
||||||
Size = WaterboxUtils.AlignUp(size);
|
|
||||||
EndExclusive = Start + Size;
|
|
||||||
_pageData = new Protection[GetPage(EndExclusive - 1) + 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>stores last set memory protection value for each page</summary>
|
|
||||||
protected readonly Protection[] _pageData;
|
|
||||||
|
|
||||||
/// <summary>end address of the memory block (not part of the block; class invariant: equal to <see cref="Start"/> + <see cref="Size"/>)</summary>
|
|
||||||
public readonly ulong EndExclusive;
|
|
||||||
|
|
||||||
/// <summary>total size of the memory block</summary>
|
|
||||||
public readonly ulong Size;
|
|
||||||
|
|
||||||
/// <summary>starting address of the memory block</summary>
|
|
||||||
public readonly ulong Start;
|
|
||||||
|
|
||||||
/// <summary>snapshot for XOR buffer</summary>
|
|
||||||
protected byte[] _snapshot;
|
|
||||||
|
|
||||||
/// <summary>true if this is currently swapped in</summary>
|
|
||||||
public bool Active { get; protected set; }
|
|
||||||
|
|
||||||
public byte[] XorHash { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>get a page index within the block</summary>
|
|
||||||
protected int GetPage(ulong addr)
|
|
||||||
{
|
|
||||||
if (addr < Start || EndExclusive <= addr) throw new ArgumentOutOfRangeException(nameof(addr), addr, "invalid address");
|
|
||||||
return (int) ((addr - Start) >> WaterboxUtils.PageShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>get a start address for a page index within the block</summary>
|
|
||||||
protected ulong GetStartAddr(int page) => ((ulong) page << WaterboxUtils.PageShift) + Start;
|
|
||||||
|
|
||||||
/// <summary>Get a stream that can be used to read or write from part of the block. Does not check for or change <see cref="Protect"/>!</summary>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/> - <c>1</c>) are outside [<see cref="Start"/>, <see cref="EndExclusive"/>), the range of the block</exception>
|
|
||||||
public Stream GetStream(ulong start, ulong length, bool writer)
|
|
||||||
{
|
|
||||||
if (start < Start)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address");
|
|
||||||
if (EndExclusive < start + length)
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address");
|
|
||||||
return new MemoryViewStream(!writer, writer, (long) start, (long) length, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>get a stream that can be used to read or write from part of the block. both reads and writes will be XORed against an earlier recorded snapshot</summary>
|
|
||||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="start"/> or end (= <paramref name="start"/> + <paramref name="length"/> - <c>1</c>) are outside [<see cref="Start"/>, <see cref="EndExclusive"/>), the range of the block</exception>
|
|
||||||
/// <exception cref="InvalidOperationException">no snapshot taken (haven't called <see cref="SaveXorSnapshot"/>)</exception>
|
|
||||||
public Stream GetXorStream(ulong start, ulong length, bool writer)
|
|
||||||
{
|
|
||||||
if (start < Start) throw new ArgumentOutOfRangeException(nameof(start), start, "invalid address");
|
|
||||||
if (EndExclusive < start + length) throw new ArgumentOutOfRangeException(nameof(length), length, "requested length implies invalid end address");
|
|
||||||
if (_snapshot == null) throw new InvalidOperationException("No snapshot taken!");
|
|
||||||
return new MemoryViewXorStream(!writer, writer, (long) start, (long) length, this, _snapshot, (long) (start - Start));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>activate the memory block, swapping it in at the pre-specified address</summary>
|
|
||||||
public abstract void Activate();
|
|
||||||
|
|
||||||
/// <summary>deactivate the memory block, removing it from RAM but leaving it immediately available to swap back in</summary>
|
|
||||||
public abstract void Deactivate();
|
|
||||||
|
|
||||||
/// <summary>take a hash of the current full contents of the block, including unreadable areas</summary>
|
|
||||||
public abstract byte[] FullHash();
|
|
||||||
|
|
||||||
/// <summary>set r/w/x protection on a portion of memory. rounded to encompassing pages</summary>
|
|
||||||
public abstract void Protect(ulong start, ulong length, Protection prot);
|
|
||||||
|
|
||||||
/// <summary>restore all recorded protections</summary>
|
|
||||||
protected abstract void ProtectAll();
|
|
||||||
|
|
||||||
/// <summary>take a snapshot of the entire memory block's contents, for use in <see cref="GetXorStream"/></summary>
|
|
||||||
public abstract void SaveXorSnapshot();
|
|
||||||
|
|
||||||
public abstract void Dispose(bool disposing);
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
~MemoryBlockBase()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>allocate <paramref name="size"/> bytes starting at a particular address <paramref name="start"/></summary>
|
|
||||||
public static MemoryBlockBase CallPlatformCtor(ulong start, ulong size) => OSTailoredCode.IsUnixHost
|
|
||||||
? (MemoryBlockBase) new MemoryBlockUnix(start, size)
|
|
||||||
: new MemoryBlock(start, size);
|
|
||||||
|
|
||||||
/// <summary>allocate <paramref name="size"/> bytes at any address</summary>
|
|
||||||
public static MemoryBlockBase CallPlatformCtor(ulong size) => CallPlatformCtor(0, size);
|
|
||||||
|
|
||||||
/// <summary>Memory protection constant</summary>
|
|
||||||
public enum Protection : byte { None, R, RW, RX }
|
|
||||||
|
|
||||||
private class MemoryViewStream : Stream
|
|
||||||
{
|
|
||||||
public MemoryViewStream(bool readable, bool writable, long ptr, long length, MemoryBlockBase owner)
|
|
||||||
{
|
|
||||||
_readable = readable;
|
|
||||||
_writable = writable;
|
|
||||||
_ptr = ptr;
|
|
||||||
_length = length;
|
|
||||||
_owner = owner;
|
|
||||||
_pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly long _length;
|
|
||||||
private readonly MemoryBlockBase _owner;
|
|
||||||
private readonly long _ptr;
|
|
||||||
private readonly bool _readable;
|
|
||||||
private readonly bool _writable;
|
|
||||||
|
|
||||||
private long _pos;
|
|
||||||
|
|
||||||
public override bool CanRead => _readable;
|
|
||||||
public override bool CanSeek => true;
|
|
||||||
public override bool CanWrite => _writable;
|
|
||||||
public override long Length => _length;
|
|
||||||
public override long Position
|
|
||||||
{
|
|
||||||
get => _pos;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value < 0 || _length < value) throw new ArgumentOutOfRangeException();
|
|
||||||
_pos = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EnsureNotDisposed()
|
|
||||||
{
|
|
||||||
if (_owner.Start == 0)
|
|
||||||
throw new ObjectDisposedException(nameof(MemoryBlockBase));
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Flush() {}
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (!_readable) throw new InvalidOperationException();
|
|
||||||
if (count < 0 || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
|
||||||
EnsureNotDisposed();
|
|
||||||
|
|
||||||
count = (int) Math.Min(count, _length - _pos);
|
|
||||||
Marshal.Copy(Z.SS(_ptr + _pos), buffer, offset, count);
|
|
||||||
_pos += count;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override long Seek(long offset, SeekOrigin origin)
|
|
||||||
{
|
|
||||||
long newpos;
|
|
||||||
switch (origin)
|
|
||||||
{
|
|
||||||
default:
|
|
||||||
case SeekOrigin.Begin:
|
|
||||||
newpos = offset;
|
|
||||||
break;
|
|
||||||
case SeekOrigin.Current:
|
|
||||||
newpos = _pos + offset;
|
|
||||||
break;
|
|
||||||
case SeekOrigin.End:
|
|
||||||
newpos = _length + offset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Position = newpos;
|
|
||||||
return newpos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetLength(long value)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
if (!_writable) throw new InvalidOperationException();
|
|
||||||
if (count < 0 || _length - _pos < count || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
|
||||||
EnsureNotDisposed();
|
|
||||||
|
|
||||||
Marshal.Copy(buffer, offset, Z.SS(_ptr + _pos), count);
|
|
||||||
_pos += count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MemoryViewXorStream : MemoryViewStream
|
|
||||||
{
|
|
||||||
public MemoryViewXorStream(bool readable, bool writable, long ptr, long length, MemoryBlockBase owner, byte[] initial, long offset)
|
|
||||||
: base(readable, writable, ptr, length, owner)
|
|
||||||
{
|
|
||||||
_initial = initial;
|
|
||||||
_offset = (int) offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>the initial data to XOR against for both reading and writing</summary>
|
|
||||||
private readonly byte[] _initial;
|
|
||||||
|
|
||||||
/// <summary>offset into the XOR data that this stream is representing</summary>
|
|
||||||
private readonly int _offset;
|
|
||||||
|
|
||||||
public override int Read(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var pos = (int) Position;
|
|
||||||
count = base.Read(buffer, offset, count);
|
|
||||||
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(byte[] buffer, int offset, int count)
|
|
||||||
{
|
|
||||||
var pos = (int) Position;
|
|
||||||
if (count < 0 || Length - pos < count || buffer.Length < count + offset) throw new ArgumentOutOfRangeException();
|
|
||||||
|
|
||||||
// is mutating the buffer passed to Stream.Write kosher?
|
|
||||||
XorTransform(_initial, _offset + pos, buffer, offset, count);
|
|
||||||
base.Write(buffer, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <remarks>bounds check already done by calling method i.e. in <see cref="MemoryViewStream.Read">base.Read</see> (for <see cref="Read"/>) or in <see cref="Write"/></remarks>
|
|
||||||
private static unsafe void XorTransform(byte[] source, int sourceOffset, byte[] dest, int destOffset, int length)
|
|
||||||
{
|
|
||||||
// TODO: C compilers can make this pretty snappy, but can the C# jitter? Or do we need intrinsics
|
|
||||||
fixed (byte* _s = source, _d = dest)
|
|
||||||
{
|
|
||||||
byte* s = _s + sourceOffset;
|
|
||||||
byte* d = _d + destOffset;
|
|
||||||
byte* sEnd = s + length;
|
|
||||||
while (s < sEnd) *d++ ^= *s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,12 +5,12 @@ using static BizHawk.BizInvoke.POSIXLibC;
|
||||||
|
|
||||||
namespace BizHawk.BizInvoke
|
namespace BizHawk.BizInvoke
|
||||||
{
|
{
|
||||||
public sealed class MemoryBlockUnix : MemoryBlockBase
|
public sealed class MemoryBlockUnix : MemoryBlock
|
||||||
{
|
{
|
||||||
/// <summary>handle returned by <see cref="memfd_create"/></summary>
|
/// <summary>handle returned by <see cref="memfd_create"/></summary>
|
||||||
private int _fd;
|
private int _fd;
|
||||||
|
|
||||||
/// <inheritdoc cref="MemoryBlockBase(ulong,ulong)"/>
|
/// <inheritdoc cref="MemoryBlock(ulong,ulong)"/>
|
||||||
/// <exception cref="InvalidOperationException">failed to get file descriptor (never thrown as <see cref="NotImplementedException"/> is thrown first)</exception>
|
/// <exception cref="InvalidOperationException">failed to get file descriptor (never thrown as <see cref="NotImplementedException"/> is thrown first)</exception>
|
||||||
/// <exception cref="NotImplementedException">always</exception>
|
/// <exception cref="NotImplementedException">always</exception>
|
||||||
public MemoryBlockUnix(ulong start, ulong size) : base(start, size)
|
public MemoryBlockUnix(ulong start, ulong size) : base(start, size)
|
||||||
|
@ -22,7 +22,7 @@ namespace BizHawk.BizInvoke
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="true"/> or failed to map memory</exception>
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="true"/> or failed to map memory</exception>
|
||||||
public override void Activate()
|
public override void Activate()
|
||||||
{
|
{
|
||||||
if (Active) throw new InvalidOperationException("Already active");
|
if (Active) throw new InvalidOperationException("Already active");
|
||||||
|
@ -34,7 +34,7 @@ namespace BizHawk.BizInvoke
|
||||||
Active = true;
|
Active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="false"/> or failed to unmap memory</exception>
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="false"/> or failed to unmap memory</exception>
|
||||||
public override void Deactivate()
|
public override void Deactivate()
|
||||||
{
|
{
|
||||||
if (!Active) throw new InvalidOperationException("Not active");
|
if (!Active) throw new InvalidOperationException("Not active");
|
||||||
|
@ -45,7 +45,7 @@ namespace BizHawk.BizInvoke
|
||||||
Active = false;
|
Active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException"><see cref="MemoryBlockBase.Active"/> is <see langword="false"/> or failed to make memory read-only</exception>
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="false"/> or failed to make memory read-only</exception>
|
||||||
public override byte[] FullHash()
|
public override byte[] FullHash()
|
||||||
{
|
{
|
||||||
if (!Active) throw new InvalidOperationException("Not active");
|
if (!Active) throw new InvalidOperationException("Not active");
|
||||||
|
@ -100,7 +100,7 @@ namespace BizHawk.BizInvoke
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <exception cref="InvalidOperationException">snapshot already taken, <see cref="MemoryBlockBase.Active"/> is <see langword="false"/>, or failed to make memory read-only</exception>
|
/// <exception cref="InvalidOperationException">snapshot already taken, <see cref="MemoryBlock.Active"/> is <see langword="false"/>, or failed to make memory read-only</exception>
|
||||||
public override void SaveXorSnapshot()
|
public override void SaveXorSnapshot()
|
||||||
{
|
{
|
||||||
if (_snapshot != null) throw new InvalidOperationException("Snapshot already taken");
|
if (_snapshot != null) throw new InvalidOperationException("Snapshot already taken");
|
||||||
|
|
|
@ -0,0 +1,238 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace BizHawk.BizInvoke
|
||||||
|
{
|
||||||
|
public sealed class MemoryBlockWindows : MemoryBlock
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// handle returned by CreateFileMapping
|
||||||
|
/// </summary>
|
||||||
|
private IntPtr _handle;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="MemoryBlock(ulong,ulong)"/>
|
||||||
|
/// <exception cref="InvalidOperationException">failed to create file mapping</exception>
|
||||||
|
public MemoryBlockWindows(ulong start, ulong size) : base(start, size)
|
||||||
|
{
|
||||||
|
_handle = Kernel32.CreateFileMapping(
|
||||||
|
Kernel32.INVALID_HANDLE_VALUE,
|
||||||
|
IntPtr.Zero,
|
||||||
|
Kernel32.FileMapProtection.PageExecuteReadWrite | Kernel32.FileMapProtection.SectionCommit,
|
||||||
|
(uint)(Size >> 32),
|
||||||
|
(uint)Size,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
if (_handle == IntPtr.Zero) throw new InvalidOperationException($"{nameof(Kernel32.CreateFileMapping)}() returned NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="true"/> or failed to map file view</exception>
|
||||||
|
public override void Activate()
|
||||||
|
{
|
||||||
|
if (Active)
|
||||||
|
throw new InvalidOperationException("Already active");
|
||||||
|
if (Kernel32.MapViewOfFileEx(
|
||||||
|
_handle,
|
||||||
|
Kernel32.FileMapAccessType.Read | Kernel32.FileMapAccessType.Write | Kernel32.FileMapAccessType.Execute,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Z.UU(Size),
|
||||||
|
Z.US(Start)
|
||||||
|
) != Z.US(Start))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.MapViewOfFileEx)}() returned NULL");
|
||||||
|
}
|
||||||
|
ProtectAll();
|
||||||
|
Active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="false"/> or failed to unmap file view</exception>
|
||||||
|
public override void Deactivate()
|
||||||
|
{
|
||||||
|
if (!Active)
|
||||||
|
throw new InvalidOperationException("Not active");
|
||||||
|
if (!Kernel32.UnmapViewOfFile(Z.US(Start)))
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.UnmapViewOfFile)}() returned NULL");
|
||||||
|
Active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="InvalidOperationException">snapshot already taken, <see cref="MemoryBlock.Active"/> is <see langword="false"/>, or failed to make memory read-only</exception>
|
||||||
|
public override void SaveXorSnapshot()
|
||||||
|
{
|
||||||
|
if (_snapshot != null)
|
||||||
|
throw new InvalidOperationException("Snapshot already taken");
|
||||||
|
if (!Active)
|
||||||
|
throw new InvalidOperationException("Not active");
|
||||||
|
|
||||||
|
// temporarily switch the entire block to `R`: in case some areas are unreadable, we don't want
|
||||||
|
// that to complicate things
|
||||||
|
Kernel32.MemoryProtection old;
|
||||||
|
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
||||||
|
|
||||||
|
_snapshot = new byte[Size];
|
||||||
|
var ds = new MemoryStream(_snapshot, true);
|
||||||
|
var ss = GetStream(Start, Size, false);
|
||||||
|
ss.CopyTo(ds);
|
||||||
|
XorHash = WaterboxUtils.Hash(_snapshot);
|
||||||
|
|
||||||
|
ProtectAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="InvalidOperationException"><see cref="MemoryBlock.Active"/> is <see langword="false"/> or failed to make memory read-only</exception>
|
||||||
|
public override byte[] FullHash()
|
||||||
|
{
|
||||||
|
if (!Active)
|
||||||
|
throw new InvalidOperationException("Not active");
|
||||||
|
// temporarily switch the entire block to `R`
|
||||||
|
Kernel32.MemoryProtection old;
|
||||||
|
if (!Kernel32.VirtualProtect(Z.UU(Start), Z.UU(Size), Kernel32.MemoryProtection.READONLY, out old))
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
||||||
|
var ret = WaterboxUtils.Hash(GetStream(Start, Size, false));
|
||||||
|
ProtectAll();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Kernel32.MemoryProtection GetKernelMemoryProtectionValue(Protection prot)
|
||||||
|
{
|
||||||
|
Kernel32.MemoryProtection p;
|
||||||
|
switch (prot)
|
||||||
|
{
|
||||||
|
case Protection.None: p = Kernel32.MemoryProtection.NOACCESS; break;
|
||||||
|
case Protection.R: p = Kernel32.MemoryProtection.READONLY; break;
|
||||||
|
case Protection.RW: p = Kernel32.MemoryProtection.READWRITE; break;
|
||||||
|
case Protection.RX: p = Kernel32.MemoryProtection.EXECUTE_READ; break;
|
||||||
|
default: throw new ArgumentOutOfRangeException(nameof(prot));
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ProtectAll()
|
||||||
|
{
|
||||||
|
int ps = 0;
|
||||||
|
for (int i = 0; i < _pageData.Length; i++)
|
||||||
|
{
|
||||||
|
if (i == _pageData.Length - 1 || _pageData[i] != _pageData[i + 1])
|
||||||
|
{
|
||||||
|
var p = GetKernelMemoryProtectionValue(_pageData[i]);
|
||||||
|
ulong zstart = GetStartAddr(ps);
|
||||||
|
ulong zend = GetStartAddr(i + 1);
|
||||||
|
Kernel32.MemoryProtection old;
|
||||||
|
if (!Kernel32.VirtualProtect(Z.UU(zstart), Z.UU(zend - zstart), p, out old))
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
||||||
|
ps = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <exception cref="InvalidOperationException">failed to protect memory</exception>
|
||||||
|
public override void Protect(ulong start, ulong length, Protection prot)
|
||||||
|
{
|
||||||
|
if (length == 0)
|
||||||
|
return;
|
||||||
|
int pstart = GetPage(start);
|
||||||
|
int pend = GetPage(start + length - 1);
|
||||||
|
|
||||||
|
var p = GetKernelMemoryProtectionValue(prot);
|
||||||
|
for (int i = pstart; i <= pend; i++)
|
||||||
|
_pageData[i] = prot; // also store the value for later use
|
||||||
|
|
||||||
|
if (Active) // it's legal to Protect() if we're not active; the information is just saved for the next activation
|
||||||
|
{
|
||||||
|
var computedStart = WaterboxUtils.AlignDown(start);
|
||||||
|
var computedEnd = WaterboxUtils.AlignUp(start + length);
|
||||||
|
var computedLength = computedEnd - computedStart;
|
||||||
|
|
||||||
|
Kernel32.MemoryProtection old;
|
||||||
|
if (!Kernel32.VirtualProtect(Z.UU(computedStart),
|
||||||
|
Z.UU(computedLength), p, out old))
|
||||||
|
throw new InvalidOperationException($"{nameof(Kernel32.VirtualProtect)}() returned FALSE!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_handle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (Active)
|
||||||
|
Deactivate();
|
||||||
|
Kernel32.CloseHandle(_handle);
|
||||||
|
_handle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~MemoryBlockWindows()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Kernel32
|
||||||
|
{
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool VirtualProtect(UIntPtr lpAddress, UIntPtr dwSize,
|
||||||
|
MemoryProtection flNewProtect, out MemoryProtection lpflOldProtect);
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum MemoryProtection : uint
|
||||||
|
{
|
||||||
|
EXECUTE = 0x10,
|
||||||
|
EXECUTE_READ = 0x20,
|
||||||
|
EXECUTE_READWRITE = 0x40,
|
||||||
|
EXECUTE_WRITECOPY = 0x80,
|
||||||
|
NOACCESS = 0x01,
|
||||||
|
READONLY = 0x02,
|
||||||
|
READWRITE = 0x04,
|
||||||
|
WRITECOPY = 0x08,
|
||||||
|
GUARD_Modifierflag = 0x100,
|
||||||
|
NOCACHE_Modifierflag = 0x200,
|
||||||
|
WRITECOMBINE_Modifierflag = 0x400
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern IntPtr CreateFileMapping(
|
||||||
|
IntPtr hFile,
|
||||||
|
IntPtr lpFileMappingAttributes,
|
||||||
|
FileMapProtection flProtect,
|
||||||
|
uint dwMaximumSizeHigh,
|
||||||
|
uint dwMaximumSizeLow,
|
||||||
|
string lpName);
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum FileMapProtection : uint
|
||||||
|
{
|
||||||
|
PageReadonly = 0x02,
|
||||||
|
PageReadWrite = 0x04,
|
||||||
|
PageWriteCopy = 0x08,
|
||||||
|
PageExecuteRead = 0x20,
|
||||||
|
PageExecuteReadWrite = 0x40,
|
||||||
|
SectionCommit = 0x8000000,
|
||||||
|
SectionImage = 0x1000000,
|
||||||
|
SectionNoCache = 0x10000000,
|
||||||
|
SectionReserve = 0x4000000,
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool CloseHandle(IntPtr hObject);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
public static extern IntPtr MapViewOfFileEx(IntPtr hFileMappingObject,
|
||||||
|
FileMapAccessType dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
|
||||||
|
UIntPtr dwNumberOfBytesToMap, IntPtr lpBaseAddress);
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum FileMapAccessType : uint
|
||||||
|
{
|
||||||
|
Copy = 0x01,
|
||||||
|
Write = 0x02,
|
||||||
|
Read = 0x04,
|
||||||
|
AllAccess = 0x08,
|
||||||
|
Execute = 0x20,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly IntPtr INVALID_HANDLE_VALUE = Z.US(0xffffffffffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using static BizHawk.BizInvoke.MemoryBlockBase;
|
using static BizHawk.BizInvoke.MemoryBlock;
|
||||||
|
|
||||||
namespace BizHawk.BizInvoke
|
namespace BizHawk.BizInvoke
|
||||||
{
|
{
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
private bool _everythingSealed;
|
private bool _everythingSealed;
|
||||||
|
|
||||||
public MemoryBlockBase Memory { get; private set; }
|
public MemoryBlock Memory { get; private set; }
|
||||||
|
|
||||||
public string ModuleName { get; }
|
public string ModuleName { get; }
|
||||||
|
|
||||||
|
@ -94,9 +94,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
|
||||||
Memory = MemoryBlock.CallPlatformCtor(start, size);
|
Memory = MemoryBlock.Create(start, size);
|
||||||
Memory.Activate();
|
Memory.Activate();
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
|
||||||
|
|
||||||
foreach (var seg in loadsegs)
|
foreach (var seg in loadsegs)
|
||||||
{
|
{
|
||||||
|
@ -170,23 +170,23 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void Protect()
|
private void Protect()
|
||||||
{
|
{
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.R);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.R);
|
||||||
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
|
foreach (var sec in _elf.Sections.Where(s => (s.Flags & SectionFlags.Allocatable) != 0))
|
||||||
{
|
{
|
||||||
if (_everythingSealed && IsSpecialReadonlySection(sec))
|
if (_everythingSealed && IsSpecialReadonlySection(sec))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((sec.Flags & SectionFlags.Executable) != 0)
|
if ((sec.Flags & SectionFlags.Executable) != 0)
|
||||||
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlockBase.Protection.RX);
|
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlock.Protection.RX);
|
||||||
else if ((sec.Flags & SectionFlags.Writable) != 0)
|
else if ((sec.Flags & SectionFlags.Writable) != 0)
|
||||||
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlockBase.Protection.RW);
|
Memory.Protect(sec.LoadAddress, sec.Size, MemoryBlock.Protection.RW);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect all of the .wbxsyscall stuff
|
// connect all of the .wbxsyscall stuff
|
||||||
public void ConnectSyscalls(IImportResolver syscalls)
|
public void ConnectSyscalls(IImportResolver syscalls)
|
||||||
{
|
{
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
|
||||||
|
|
||||||
var tmp = new IntPtr[1];
|
var tmp = new IntPtr[1];
|
||||||
var ptrSize = (ulong)IntPtr.Size;
|
var ptrSize = (ulong)IntPtr.Size;
|
||||||
|
@ -343,7 +343,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
throw new InvalidOperationException("Memory consistency check failed. Is this savestate from different SyncSettings?");
|
throw new InvalidOperationException("Memory consistency check failed. Is this savestate from different SyncSettings?");
|
||||||
}
|
}
|
||||||
|
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
|
||||||
|
|
||||||
foreach (var s in _savedSections)
|
foreach (var s in _savedSections)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class Heap : IBinaryStateable, IDisposable
|
internal sealed class Heap : IBinaryStateable, IDisposable
|
||||||
{
|
{
|
||||||
public MemoryBlockBase Memory { get; private set; }
|
public MemoryBlock Memory { get; private set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// name, used in identifying errors
|
/// name, used in identifying errors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
|
|
||||||
public Heap(ulong start, ulong size, string name)
|
public Heap(ulong start, ulong size, string name)
|
||||||
{
|
{
|
||||||
Memory = MemoryBlockBase.CallPlatformCtor(start, size);
|
Memory = MemoryBlock.Create(start, size);
|
||||||
Used = 0;
|
Used = 0;
|
||||||
Name = name;
|
Name = name;
|
||||||
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
Console.WriteLine("Created heap `{1}` at {0:x16}:{2:x16}", start, name, start + size);
|
||||||
|
@ -62,7 +62,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
throw new InvalidOperationException($"Failed to allocate {size} bytes from heap {Name}");
|
throw new InvalidOperationException($"Failed to allocate {size} bytes from heap {Name}");
|
||||||
}
|
}
|
||||||
ulong ret = Memory.Start + allocstart;
|
ulong ret = Memory.Start + allocstart;
|
||||||
Memory.Protect(Memory.Start + Used, newused - Used, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start + Used, newused - Used, MemoryBlock.Protection.RW);
|
||||||
Used = newused;
|
Used = newused;
|
||||||
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
Console.WriteLine($"Allocated {size} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -72,7 +72,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
if (!Sealed)
|
if (!Sealed)
|
||||||
{
|
{
|
||||||
Memory.Protect(Memory.Start, Used, MemoryBlockBase.Protection.R);
|
Memory.Protect(Memory.Start, Used, MemoryBlock.Protection.R);
|
||||||
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
|
_hash = WaterboxUtils.Hash(Memory.GetStream(Memory.Start, Used, false));
|
||||||
Sealed = true;
|
Sealed = true;
|
||||||
}
|
}
|
||||||
|
@ -116,8 +116,8 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
}
|
}
|
||||||
var usedAligned = WaterboxUtils.AlignUp(used);
|
var usedAligned = WaterboxUtils.AlignUp(used);
|
||||||
|
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.None);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.None);
|
||||||
Memory.Protect(Memory.Start, used, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start, used, MemoryBlock.Protection.RW);
|
||||||
var ms = Memory.GetXorStream(Memory.Start, usedAligned, true);
|
var ms = Memory.GetXorStream(Memory.Start, usedAligned, true);
|
||||||
WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned);
|
WaterboxUtils.CopySome(br.BaseStream, ms, (long)usedAligned);
|
||||||
Used = used;
|
Used = used;
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class MapHeap : IBinaryStateable, IDisposable
|
internal sealed class MapHeap : IBinaryStateable, IDisposable
|
||||||
{
|
{
|
||||||
public MemoryBlockBase Memory { get; private set; }
|
public MemoryBlock Memory { get; private set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// name, used in identifying errors
|
/// name, used in identifying errors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -40,18 +40,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
return ((ulong)page << WaterboxUtils.PageShift) + Memory.Start;
|
return ((ulong)page << WaterboxUtils.PageShift) + Memory.Start;
|
||||||
}
|
}
|
||||||
|
|
||||||
private const MemoryBlockBase.Protection FREE = (MemoryBlockBase.Protection)255;
|
private const MemoryBlock.Protection FREE = (MemoryBlock.Protection)255;
|
||||||
|
|
||||||
private readonly MemoryBlockBase.Protection[] _pages;
|
private readonly MemoryBlock.Protection[] _pages;
|
||||||
private readonly byte[] _pagesAsBytes;
|
private readonly byte[] _pagesAsBytes;
|
||||||
|
|
||||||
public MapHeap(ulong start, ulong size, string name)
|
public MapHeap(ulong start, ulong size, string name)
|
||||||
{
|
{
|
||||||
size = WaterboxUtils.AlignUp(size);
|
size = WaterboxUtils.AlignUp(size);
|
||||||
Memory = MemoryBlockBase.CallPlatformCtor(start, size);
|
Memory = MemoryBlock.Create(start, size);
|
||||||
Name = name;
|
Name = name;
|
||||||
_pagesAsBytes = new byte[size >> WaterboxUtils.PageShift];
|
_pagesAsBytes = new byte[size >> WaterboxUtils.PageShift];
|
||||||
_pages = (MemoryBlockBase.Protection[])(object)_pagesAsBytes;
|
_pages = (MemoryBlock.Protection[])(object)_pagesAsBytes;
|
||||||
for (var i = 0; i < _pages.Length; i++)
|
for (var i = 0; i < _pages.Length; i++)
|
||||||
_pages[i] = FREE;
|
_pages[i] = FREE;
|
||||||
Console.WriteLine($"Created {nameof(MapHeap)} `{name}` at {start:x16}:{start + size:x16}");
|
Console.WriteLine($"Created {nameof(MapHeap)} `{name}` at {start:x16}:{start + size:x16}");
|
||||||
|
@ -102,7 +102,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProtectInternal(int startPage, int numPages, MemoryBlockBase.Protection prot, bool wasUsed)
|
private void ProtectInternal(int startPage, int numPages, MemoryBlock.Protection prot, bool wasUsed)
|
||||||
{
|
{
|
||||||
for (var i = startPage; i < startPage + numPages; i++)
|
for (var i = startPage; i < startPage + numPages; i++)
|
||||||
_pages[i] = prot;
|
_pages[i] = prot;
|
||||||
|
@ -111,9 +111,9 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
ulong length = ((ulong)numPages) << WaterboxUtils.PageShift;
|
ulong length = ((ulong)numPages) << WaterboxUtils.PageShift;
|
||||||
if (prot == FREE)
|
if (prot == FREE)
|
||||||
{
|
{
|
||||||
Memory.Protect(start, length, MemoryBlockBase.Protection.RW);
|
Memory.Protect(start, length, MemoryBlock.Protection.RW);
|
||||||
WaterboxUtils.ZeroMemory(Z.US(start), (long)length);
|
WaterboxUtils.ZeroMemory(Z.US(start), (long)length);
|
||||||
Memory.Protect(start, length, MemoryBlockBase.Protection.None);
|
Memory.Protect(start, length, MemoryBlock.Protection.None);
|
||||||
Used -= length;
|
Used -= length;
|
||||||
Console.WriteLine($"Freed {length} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
Console.WriteLine($"Freed {length} bytes on {Name}, utilization {Used}/{Memory.Size} ({100.0 * Used / Memory.Size:0.#}%)");
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
var p = _pages[i];
|
var p = _pages[i];
|
||||||
ulong zstart = GetStartAddr(ps);
|
ulong zstart = GetStartAddr(ps);
|
||||||
ulong zlength = (ulong)(i - ps + 1) << WaterboxUtils.PageShift;
|
ulong zlength = (ulong)(i - ps + 1) << WaterboxUtils.PageShift;
|
||||||
Memory.Protect(zstart, zlength, p == FREE ? MemoryBlockBase.Protection.None : p);
|
Memory.Protect(zstart, zlength, p == FREE ? MemoryBlock.Protection.None : p);
|
||||||
ps = i + 1;
|
ps = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ulong Map(ulong size, MemoryBlockBase.Protection prot)
|
public ulong Map(ulong size, MemoryBlock.Protection prot)
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -216,14 +216,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
var copyPageLen = Math.Min(oldNumPages, newNumPages);
|
var copyPageLen = Math.Min(oldNumPages, newNumPages);
|
||||||
|
|
||||||
var data = new byte[copyDataLen];
|
var data = new byte[copyDataLen];
|
||||||
Memory.Protect(start, copyDataLen, MemoryBlockBase.Protection.RW);
|
Memory.Protect(start, copyDataLen, MemoryBlock.Protection.RW);
|
||||||
Marshal.Copy(Z.US(start), data, 0, (int)copyDataLen);
|
Marshal.Copy(Z.US(start), data, 0, (int)copyDataLen);
|
||||||
|
|
||||||
var pages = new MemoryBlockBase.Protection[copyPageLen];
|
var pages = new MemoryBlock.Protection[copyPageLen];
|
||||||
Array.Copy(_pages, oldStartPage, pages, 0, copyPageLen);
|
Array.Copy(_pages, oldStartPage, pages, 0, copyPageLen);
|
||||||
|
|
||||||
ProtectInternal(oldStartPage, oldNumPages, FREE, true);
|
ProtectInternal(oldStartPage, oldNumPages, FREE, true);
|
||||||
ProtectInternal(newStartPage, newNumPages, MemoryBlockBase.Protection.RW, false);
|
ProtectInternal(newStartPage, newNumPages, MemoryBlock.Protection.RW, false);
|
||||||
|
|
||||||
var ret = GetStartAddr(newStartPage);
|
var ret = GetStartAddr(newStartPage);
|
||||||
Marshal.Copy(data, 0, Z.US(ret), (int)copyDataLen);
|
Marshal.Copy(data, 0, Z.US(ret), (int)copyDataLen);
|
||||||
|
@ -241,7 +241,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
return Protect(start, size, FREE);
|
return Protect(start, size, FREE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Protect(ulong start, ulong size, MemoryBlockBase.Protection prot)
|
public bool Protect(ulong start, ulong size, MemoryBlock.Protection prot)
|
||||||
{
|
{
|
||||||
if (start < Memory.Start || start + size > Memory.EndExclusive || size == 0)
|
if (start < Memory.Start || start + size > Memory.EndExclusive || size == 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -274,7 +274,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
bw.Write(Memory.XorHash);
|
bw.Write(Memory.XorHash);
|
||||||
bw.Write(_pagesAsBytes);
|
bw.Write(_pagesAsBytes);
|
||||||
|
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.R);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.R);
|
||||||
var srcs = Memory.GetXorStream(Memory.Start, Memory.Size, false);
|
var srcs = Memory.GetXorStream(Memory.Start, Memory.Size, false);
|
||||||
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
|
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
|
||||||
{
|
{
|
||||||
|
@ -305,7 +305,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
throw new InvalidOperationException("Unexpected error reading!");
|
throw new InvalidOperationException("Unexpected error reading!");
|
||||||
|
|
||||||
Used = 0;
|
Used = 0;
|
||||||
Memory.Protect(Memory.Start, Memory.Size, MemoryBlockBase.Protection.RW);
|
Memory.Protect(Memory.Start, Memory.Size, MemoryBlock.Protection.RW);
|
||||||
var dsts = Memory.GetXorStream(Memory.Start, Memory.Size, true);
|
var dsts = Memory.GetXorStream(Memory.Start, Memory.Size, true);
|
||||||
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
|
for (int i = 0, addr = 0; i < _pages.Length; i++, addr += WaterboxUtils.PageSize)
|
||||||
{
|
{
|
||||||
|
@ -333,7 +333,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
ulong siz = (ulong)(rnd.Next(256 * 1024) + 384 * 1024);
|
ulong siz = (ulong)(rnd.Next(256 * 1024) + 384 * 1024);
|
||||||
siz = siz / 4096 * 4096;
|
siz = siz / 4096 * 4096;
|
||||||
var ptr = mmo.Map(siz, MemoryBlockBase.Protection.RW);
|
var ptr = mmo.Map(siz, MemoryBlock.Protection.RW);
|
||||||
allocs.Add(ptr, siz);
|
allocs.Add(ptr, siz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,7 +349,7 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
ulong siz = (ulong)(rnd.Next(256 * 1024) + 384 * 1024);
|
ulong siz = (ulong)(rnd.Next(256 * 1024) + 384 * 1024);
|
||||||
siz = siz / 4096 * 4096;
|
siz = siz / 4096 * 4096;
|
||||||
var ptr = mmo.Map(siz, MemoryBlockBase.Protection.RW);
|
var ptr = mmo.Map(siz, MemoryBlock.Protection.RW);
|
||||||
allocs.Add(ptr, siz);
|
allocs.Add(ptr, siz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,14 +28,14 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// everything to swap in for context switches
|
/// everything to swap in for context switches
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<MemoryBlockBase> _memoryBlocks = new List<MemoryBlockBase>();
|
private List<MemoryBlock> _memoryBlocks = new List<MemoryBlock>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// an informative name for each memory block: used for debugging purposes
|
/// an informative name for each memory block: used for debugging purposes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<string> _memoryBlockNames = new List<string>();
|
private List<string> _memoryBlockNames = new List<string>();
|
||||||
|
|
||||||
protected void AddMemoryBlock(MemoryBlockBase block, string name)
|
protected void AddMemoryBlock(MemoryBlock block, string name)
|
||||||
{
|
{
|
||||||
_memoryBlocks.Add(block);
|
_memoryBlocks.Add(block);
|
||||||
_memoryBlockNames.Add(name);
|
_memoryBlockNames.Add(name);
|
||||||
|
|
|
@ -399,18 +399,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
{
|
{
|
||||||
if (address != IntPtr.Zero)
|
if (address != IntPtr.Zero)
|
||||||
return Z.SS(-1);
|
return Z.SS(-1);
|
||||||
MemoryBlockBase.Protection mprot;
|
MemoryBlock.Protection mprot;
|
||||||
switch (prot)
|
switch (prot)
|
||||||
{
|
{
|
||||||
case 0: mprot = MemoryBlockBase.Protection.None; break;
|
case 0: mprot = MemoryBlock.Protection.None; break;
|
||||||
default:
|
default:
|
||||||
case 6: // W^X
|
case 6: // W^X
|
||||||
case 7: // W^X
|
case 7: // W^X
|
||||||
case 4: // exec only????
|
case 4: // exec only????
|
||||||
case 2: return Z.SS(-1); // write only????
|
case 2: return Z.SS(-1); // write only????
|
||||||
case 3: mprot = MemoryBlockBase.Protection.RW; break;
|
case 3: mprot = MemoryBlock.Protection.RW; break;
|
||||||
case 1: mprot = MemoryBlockBase.Protection.R; break;
|
case 1: mprot = MemoryBlock.Protection.R; break;
|
||||||
case 5: mprot = MemoryBlockBase.Protection.RX; break;
|
case 5: mprot = MemoryBlock.Protection.RX; break;
|
||||||
}
|
}
|
||||||
if ((flags & 0x20) == 0)
|
if ((flags & 0x20) == 0)
|
||||||
{
|
{
|
||||||
|
@ -448,18 +448,18 @@ namespace BizHawk.Emulation.Cores.Waterbox
|
||||||
[BizExport(CallingConvention.Cdecl, EntryPoint = "__wsyscalltab[10]")]
|
[BizExport(CallingConvention.Cdecl, EntryPoint = "__wsyscalltab[10]")]
|
||||||
public int MProtect(UIntPtr address, UIntPtr size, int prot)
|
public int MProtect(UIntPtr address, UIntPtr size, int prot)
|
||||||
{
|
{
|
||||||
MemoryBlockBase.Protection mprot;
|
MemoryBlock.Protection mprot;
|
||||||
switch (prot)
|
switch (prot)
|
||||||
{
|
{
|
||||||
case 0: mprot = MemoryBlockBase.Protection.None; break;
|
case 0: mprot = MemoryBlock.Protection.None; break;
|
||||||
default:
|
default:
|
||||||
case 6: // W^X
|
case 6: // W^X
|
||||||
case 7: // W^X
|
case 7: // W^X
|
||||||
case 4: // exec only????
|
case 4: // exec only????
|
||||||
case 2: return -1; // write only????
|
case 2: return -1; // write only????
|
||||||
case 3: mprot = MemoryBlockBase.Protection.RW; break;
|
case 3: mprot = MemoryBlock.Protection.RW; break;
|
||||||
case 1: mprot = MemoryBlockBase.Protection.R; break;
|
case 1: mprot = MemoryBlock.Protection.R; break;
|
||||||
case 5: mprot = MemoryBlockBase.Protection.RX; break;
|
case 5: mprot = MemoryBlock.Protection.RX; break;
|
||||||
}
|
}
|
||||||
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : -1;
|
return _parent._mmapheap.Protect((ulong)address, (ulong)size, mprot) ? 0 : -1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue