From cee712105850ac3385cd0091a923438167433f9f Mon Sep 17 00:00:00 2001 From: TSR Berry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 8 Apr 2023 01:22:00 +0200 Subject: Move solution and projects to src --- src/ARMeilleure/Memory/IJitMemoryAllocator.cs | 10 +++ src/ARMeilleure/Memory/IJitMemoryBlock.cs | 14 +++++ src/ARMeilleure/Memory/IMemoryManager.cs | 77 ++++++++++++++++++++++++ src/ARMeilleure/Memory/InvalidAccessException.cs | 23 +++++++ src/ARMeilleure/Memory/MemoryManagerType.cs | 41 +++++++++++++ src/ARMeilleure/Memory/ReservedRegion.cs | 58 ++++++++++++++++++ 6 files changed, 223 insertions(+) create mode 100644 src/ARMeilleure/Memory/IJitMemoryAllocator.cs create mode 100644 src/ARMeilleure/Memory/IJitMemoryBlock.cs create mode 100644 src/ARMeilleure/Memory/IMemoryManager.cs create mode 100644 src/ARMeilleure/Memory/InvalidAccessException.cs create mode 100644 src/ARMeilleure/Memory/MemoryManagerType.cs create mode 100644 src/ARMeilleure/Memory/ReservedRegion.cs (limited to 'src/ARMeilleure/Memory') 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 UnmapEvent; + + /// + /// Reads data from CPU mapped memory. + /// + /// Type of the data being read + /// Virtual address of the data in memory + /// The data + T Read(ulong va) where T : unmanaged; + + /// + /// Reads data from CPU mapped memory, with read tracking + /// + /// Type of the data being read + /// Virtual address of the data in memory + /// The data + T ReadTracked(ulong va) where T : unmanaged; + + /// + /// Writes data to CPU mapped memory. + /// + /// Type of the data being written + /// Virtual address to write the data into + /// Data to be written + void Write(ulong va, T value) where T : unmanaged; + + /// + /// Gets a read-only span of data from CPU mapped memory. + /// + /// Virtual address of the data + /// Size of the data + /// True if read tracking is triggered on the span + /// A read-only span of the data + ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false); + + /// + /// Gets a reference for the given type at the specified virtual memory address. + /// + /// + /// The data must be located at a contiguous memory region. + /// + /// Type of the data to get the reference + /// Virtual address of the data + /// A reference to the data in memory + ref T GetRef(ulong va) where T : unmanaged; + + /// + /// Checks if the page at a given CPU virtual address is mapped. + /// + /// Virtual address to check + /// True if the address is mapped, false otherwise + bool IsMapped(ulong va); + + /// + /// Alerts the memory tracking that a given region has been read from or written to. + /// This should be called before read/write is performed. + /// + /// Virtual address of the region + /// Size of the region + /// True if the region was written, false if read + /// True if the access is precise, false otherwise + /// Optional ID of the handles that should not be signalled + 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 +{ + /// + /// 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. + /// + public enum MemoryManagerType + { + /// + /// Complete software MMU implementation, the read/write methods are always called, + /// without any attempt to perform faster memory access. + /// + SoftwareMmu, + + /// + /// 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. + /// + SoftwarePageTable, + + /// + /// 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. + /// + HostMapped, + + /// + /// 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. + /// + 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(); + } + } +} -- cgit v1.2.3