aboutsummaryrefslogtreecommitdiff
path: root/src/ARMeilleure/Memory
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/ARMeilleure/Memory
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/ARMeilleure/Memory')
-rw-r--r--src/ARMeilleure/Memory/IJitMemoryAllocator.cs10
-rw-r--r--src/ARMeilleure/Memory/IJitMemoryBlock.cs14
-rw-r--r--src/ARMeilleure/Memory/IMemoryManager.cs77
-rw-r--r--src/ARMeilleure/Memory/InvalidAccessException.cs23
-rw-r--r--src/ARMeilleure/Memory/MemoryManagerType.cs41
-rw-r--r--src/ARMeilleure/Memory/ReservedRegion.cs58
6 files changed, 223 insertions, 0 deletions
diff --git a/src/ARMeilleure/Memory/IJitMemoryAllocator.cs b/src/ARMeilleure/Memory/IJitMemoryAllocator.cs
new file mode 100644
index 00000000..19b696b0
--- /dev/null
+++ b/src/ARMeilleure/Memory/IJitMemoryAllocator.cs
@@ -0,0 +1,10 @@
+namespace ARMeilleure.Memory
+{
+ public interface IJitMemoryAllocator
+ {
+ IJitMemoryBlock Allocate(ulong size);
+ IJitMemoryBlock Reserve(ulong size);
+
+ ulong GetPageSize();
+ }
+}
diff --git a/src/ARMeilleure/Memory/IJitMemoryBlock.cs b/src/ARMeilleure/Memory/IJitMemoryBlock.cs
new file mode 100644
index 00000000..670f2862
--- /dev/null
+++ b/src/ARMeilleure/Memory/IJitMemoryBlock.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace ARMeilleure.Memory
+{
+ public interface IJitMemoryBlock : IDisposable
+ {
+ IntPtr Pointer { get; }
+
+ bool Commit(ulong offset, ulong size);
+
+ void MapAsRx(ulong offset, ulong size);
+ void MapAsRwx(ulong offset, ulong size);
+ }
+}
diff --git a/src/ARMeilleure/Memory/IMemoryManager.cs b/src/ARMeilleure/Memory/IMemoryManager.cs
new file mode 100644
index 00000000..5eb1fadd
--- /dev/null
+++ b/src/ARMeilleure/Memory/IMemoryManager.cs
@@ -0,0 +1,77 @@
+using System;
+
+namespace ARMeilleure.Memory
+{
+ public interface IMemoryManager
+ {
+ int AddressSpaceBits { get; }
+
+ IntPtr PageTablePointer { get; }
+
+ MemoryManagerType Type { get; }
+
+ event Action<ulong, ulong> UnmapEvent;
+
+ /// <summary>
+ /// Reads data from CPU mapped memory.
+ /// </summary>
+ /// <typeparam name="T">Type of the data being read</typeparam>
+ /// <param name="va">Virtual address of the data in memory</param>
+ /// <returns>The data</returns>
+ T Read<T>(ulong va) where T : unmanaged;
+
+ /// <summary>
+ /// Reads data from CPU mapped memory, with read tracking
+ /// </summary>
+ /// <typeparam name="T">Type of the data being read</typeparam>
+ /// <param name="va">Virtual address of the data in memory</param>
+ /// <returns>The data</returns>
+ T ReadTracked<T>(ulong va) where T : unmanaged;
+
+ /// <summary>
+ /// Writes data to CPU mapped memory.
+ /// </summary>
+ /// <typeparam name="T">Type of the data being written</typeparam>
+ /// <param name="va">Virtual address to write the data into</param>
+ /// <param name="value">Data to be written</param>
+ void Write<T>(ulong va, T value) where T : unmanaged;
+
+ /// <summary>
+ /// Gets a read-only span of data from CPU mapped memory.
+ /// </summary>
+ /// <param name="va">Virtual address of the data</param>
+ /// <param name="size">Size of the data</param>
+ /// <param name="tracked">True if read tracking is triggered on the span</param>
+ /// <returns>A read-only span of the data</returns>
+ ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
+
+ /// <summary>
+ /// Gets a reference for the given type at the specified virtual memory address.
+ /// </summary>
+ /// <remarks>
+ /// The data must be located at a contiguous memory region.
+ /// </remarks>
+ /// <typeparam name="T">Type of the data to get the reference</typeparam>
+ /// <param name="va">Virtual address of the data</param>
+ /// <returns>A reference to the data in memory</returns>
+ ref T GetRef<T>(ulong va) where T : unmanaged;
+
+ /// <summary>
+ /// Checks if the page at a given CPU virtual address is mapped.
+ /// </summary>
+ /// <param name="va">Virtual address to check</param>
+ /// <returns>True if the address is mapped, false otherwise</returns>
+ bool IsMapped(ulong va);
+
+ /// <summary>
+ /// Alerts the memory tracking that a given region has been read from or written to.
+ /// This should be called before read/write is performed.
+ /// </summary>
+ /// <param name="va">Virtual address of the region</param>
+ /// <param name="size">Size of the region</param>
+ /// <param name="write">True if the region was written, false if read</param>
+ /// <param name="precise">True if the access is precise, false otherwise</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);
+ }
+} \ No newline at end of file
diff --git a/src/ARMeilleure/Memory/InvalidAccessException.cs b/src/ARMeilleure/Memory/InvalidAccessException.cs
new file mode 100644
index 00000000..ad540719
--- /dev/null
+++ b/src/ARMeilleure/Memory/InvalidAccessException.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace ARMeilleure.Memory
+{
+ class InvalidAccessException : Exception
+ {
+ public InvalidAccessException()
+ {
+ }
+
+ public InvalidAccessException(ulong address) : base($"Invalid memory access at virtual address 0x{address:X16}.")
+ {
+ }
+
+ public InvalidAccessException(string message) : base(message)
+ {
+ }
+
+ public InvalidAccessException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
diff --git a/src/ARMeilleure/Memory/MemoryManagerType.cs b/src/ARMeilleure/Memory/MemoryManagerType.cs
new file mode 100644
index 00000000..ce84ccaf
--- /dev/null
+++ b/src/ARMeilleure/Memory/MemoryManagerType.cs
@@ -0,0 +1,41 @@
+namespace ARMeilleure.Memory
+{
+ /// <summary>
+ /// Indicates the type of a memory manager and the method it uses for memory mapping
+ /// and address translation. This controls the code generated for memory accesses on the JIT.
+ /// </summary>
+ public enum MemoryManagerType
+ {
+ /// <summary>
+ /// Complete software MMU implementation, the read/write methods are always called,
+ /// without any attempt to perform faster memory access.
+ /// </summary>
+ SoftwareMmu,
+
+ /// <summary>
+ /// High level implementation using a software flat page table for address translation,
+ /// used to speed up address translation if possible without calling the read/write methods.
+ /// </summary>
+ SoftwarePageTable,
+
+ /// <summary>
+ /// High level implementation with mappings managed by the host OS, effectively using hardware
+ /// page tables. No address translation is performed in software and the memory is just accessed directly.
+ /// </summary>
+ HostMapped,
+
+ /// <summary>
+ /// Same as the host mapped memory manager type, but without masking the address within the address space.
+ /// Allows invalid access from JIT code to the rest of the program, but is faster.
+ /// </summary>
+ HostMappedUnsafe
+ }
+
+ static class MemoryManagerTypeExtensions
+ {
+ public static bool IsHostMapped(this MemoryManagerType type)
+ {
+ return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
+ }
+ }
+}
diff --git a/src/ARMeilleure/Memory/ReservedRegion.cs b/src/ARMeilleure/Memory/ReservedRegion.cs
new file mode 100644
index 00000000..2197afad
--- /dev/null
+++ b/src/ARMeilleure/Memory/ReservedRegion.cs
@@ -0,0 +1,58 @@
+using System;
+
+namespace ARMeilleure.Memory
+{
+ class ReservedRegion
+ {
+ public const int DefaultGranularity = 65536; // Mapping granularity in Windows.
+
+ public IJitMemoryBlock Block { get; }
+
+ public IntPtr Pointer => Block.Pointer;
+
+ private readonly ulong _maxSize;
+ private readonly ulong _sizeGranularity;
+ private ulong _currentSize;
+
+ public ReservedRegion(IJitMemoryAllocator allocator, ulong maxSize, ulong granularity = 0)
+ {
+ if (granularity == 0)
+ {
+ granularity = DefaultGranularity;
+ }
+
+ Block = allocator.Reserve(maxSize);
+ _maxSize = maxSize;
+ _sizeGranularity = granularity;
+ _currentSize = 0;
+ }
+
+ public void ExpandIfNeeded(ulong desiredSize)
+ {
+ if (desiredSize > _maxSize)
+ {
+ throw new OutOfMemoryException();
+ }
+
+ if (desiredSize > _currentSize)
+ {
+ // Lock, and then check again. We only want to commit once.
+ lock (this)
+ {
+ if (desiredSize >= _currentSize)
+ {
+ ulong overflowBytes = desiredSize - _currentSize;
+ ulong moreToCommit = (((_sizeGranularity - 1) + overflowBytes) / _sizeGranularity) * _sizeGranularity; // Round up.
+ Block.Commit(_currentSize, moreToCommit);
+ _currentSize += moreToCommit;
+ }
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ Block.Dispose();
+ }
+ }
+}