diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2019-02-18 20:52:06 -0300 |
|---|---|---|
| committer | jduncanator <1518948+jduncanator@users.noreply.github.com> | 2019-02-19 10:52:06 +1100 |
| commit | 932224f05112180aa5f52162cbbc3a17c339075f (patch) | |
| tree | eed53f37bfd78db97d6f8301c021b7de9dd3becf /ChocolArm64/Memory/CompareExchange128.cs | |
| parent | dd00a4b62d48b7d55a6e66a69a83f09267d34143 (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.cs | 151 |
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 |
