aboutsummaryrefslogtreecommitdiff
path: root/ChocolArm64/Memory/CompareExchange128.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2019-02-18 20:52:06 -0300
committerjduncanator <1518948+jduncanator@users.noreply.github.com>2019-02-19 10:52:06 +1100
commit932224f05112180aa5f52162cbbc3a17c339075f (patch)
treeeed53f37bfd78db97d6f8301c021b7de9dd3becf /ChocolArm64/Memory/CompareExchange128.cs
parentdd00a4b62d48b7d55a6e66a69a83f09267d34143 (diff)
ARM exclusive monitor and multicore fixes (#589)
* Implement ARM exclusive load/store with compare exchange insts, and enable multicore by default * Fix comment typo * Support Linux and OSX on MemoryAlloc and CompareExchange128, some cleanup * Use intel syntax on assembly code * Adjust identation * Add CPUID check and fix exclusive reservation granule size * Update schema multicore scheduling default value * Make the cpu id check code lower case aswell
Diffstat (limited to 'ChocolArm64/Memory/CompareExchange128.cs')
-rw-r--r--ChocolArm64/Memory/CompareExchange128.cs151
1 files changed, 151 insertions, 0 deletions
diff --git a/ChocolArm64/Memory/CompareExchange128.cs b/ChocolArm64/Memory/CompareExchange128.cs
new file mode 100644
index 00000000..0fbe10f2
--- /dev/null
+++ b/ChocolArm64/Memory/CompareExchange128.cs
@@ -0,0 +1,151 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ChocolArm64.Memory
+{
+ static class CompareExchange128
+ {
+ private struct Int128
+ {
+ public ulong Low { get; }
+ public ulong High { get; }
+
+ public Int128(ulong low, ulong high)
+ {
+ Low = low;
+ High = high;
+ }
+ }
+
+ private delegate Int128 InterlockedCompareExchange(IntPtr address, Int128 expected, Int128 desired);
+
+ private delegate int GetCpuId();
+
+ private static InterlockedCompareExchange _interlockedCompareExchange;
+
+ static CompareExchange128()
+ {
+ if (RuntimeInformation.OSArchitecture != Architecture.X64 || !IsCmpxchg16bSupported())
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ byte[] interlockedCompareExchange128Code;
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ interlockedCompareExchange128Code = new byte[]
+ {
+ 0x53, // push rbx
+ 0x49, 0x8b, 0x00, // mov rax, [r8]
+ 0x49, 0x8b, 0x19, // mov rbx, [r9]
+ 0x49, 0x89, 0xca, // mov r10, rcx
+ 0x49, 0x89, 0xd3, // mov r11, rdx
+ 0x49, 0x8b, 0x49, 0x08, // mov rcx, [r9+8]
+ 0x49, 0x8b, 0x50, 0x08, // mov rdx, [r8+8]
+ 0xf0, 0x49, 0x0f, 0xc7, 0x0b, // lock cmpxchg16b [r11]
+ 0x49, 0x89, 0x02, // mov [r10], rax
+ 0x4c, 0x89, 0xd0, // mov rax, r10
+ 0x49, 0x89, 0x52, 0x08, // mov [r10+8], rdx
+ 0x5b, // pop rbx
+ 0xc3 // ret
+ };
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ||
+ RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ interlockedCompareExchange128Code = new byte[]
+ {
+ 0x53, // push rbx
+ 0x49, 0x89, 0xd1, // mov r9, rdx
+ 0x48, 0x89, 0xcb, // mov rbx, rcx
+ 0x48, 0x89, 0xf0, // mov rax, rsi
+ 0x4c, 0x89, 0xca, // mov rdx, r9
+ 0x4c, 0x89, 0xc1, // mov rcx, r8
+ 0xf0, 0x48, 0x0f, 0xc7, 0x0f, // lock cmpxchg16b [rdi]
+ 0x5b, // pop rbx
+ 0xc3 // ret
+ };
+ }
+ else
+ {
+ throw new PlatformNotSupportedException();
+ }
+
+ IntPtr funcPtr = MapCodeAsExecutable(interlockedCompareExchange128Code);
+
+ _interlockedCompareExchange = Marshal.GetDelegateForFunctionPointer<InterlockedCompareExchange>(funcPtr);
+ }
+
+ private static bool IsCmpxchg16bSupported()
+ {
+ byte[] getCpuIdCode = new byte[]
+ {
+ 0x53, // push rbx
+ 0xb8, 0x01, 0x00, 0x00, 0x00, // mov eax, 0x1
+ 0x0f, 0xa2, // cpuid
+ 0x89, 0xc8, // mov eax, ecx
+ 0x5b, // pop rbx
+ 0xc3 // ret
+ };
+
+ IntPtr funcPtr = MapCodeAsExecutable(getCpuIdCode);
+
+ GetCpuId getCpuId = Marshal.GetDelegateForFunctionPointer<GetCpuId>(funcPtr);
+
+ int cpuId = getCpuId();
+
+ MemoryAlloc.Free(funcPtr);
+
+ return (cpuId & (1 << 13)) != 0;
+ }
+
+ private static IntPtr MapCodeAsExecutable(byte[] code)
+ {
+ ulong codeLength = (ulong)code.Length;
+
+ IntPtr funcPtr = MemoryAlloc.Allocate(codeLength);
+
+ unsafe
+ {
+ fixed (byte* codePtr = code)
+ {
+ byte* dest = (byte*)funcPtr;
+
+ long size = (long)codeLength;
+
+ Buffer.MemoryCopy(codePtr, dest, size, size);
+ }
+ }
+
+ MemoryAlloc.Reprotect(funcPtr, codeLength, MemoryProtection.Execute);
+
+ return funcPtr;
+ }
+
+ public static bool InterlockedCompareExchange128(
+ IntPtr address,
+ ulong expectedLow,
+ ulong expectedHigh,
+ ulong desiredLow,
+ ulong desiredHigh)
+ {
+ Int128 expected = new Int128(expectedLow, expectedHigh);
+ Int128 desired = new Int128(desiredLow, desiredHigh);
+
+ Int128 old = _interlockedCompareExchange(address, expected, desired);
+
+ return old.Low == expected.Low && old.High == expected.High;
+ }
+
+ public static void InterlockedRead128(IntPtr address, out ulong low, out ulong high)
+ {
+ Int128 zero = new Int128(0, 0);
+
+ Int128 old = _interlockedCompareExchange(address, zero, zero);
+
+ low = old.Low;
+ high = old.High;
+ }
+ }
+} \ No newline at end of file