Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add QEMU detection check #104125

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;
using System.Threading;

internal static partial class Interop
{
internal static partial class Sys
{
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_IsQemuDetected")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool IsQemuDetectedImpl();

private static int s_isQemuDetected;

internal static bool IsQemuDetected()
{
int isQemuDetected = Interlocked.CompareExchange(ref s_isQemuDetected, 0, 0);
if (isQemuDetected == 0)
{
isQemuDetected = IsQemuDetectedImpl() ? 1 : 2;
int oldValue = Interlocked.CompareExchange(ref s_isQemuDetected, isQemuDetected, 0);
if (oldValue != 0) // a different thread has managed to update the value
{
isQemuDetected = oldValue;
}
}

return isQemuDetected == 1;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ public static partial class PlatformDetection
public static bool IsMonoLinuxArm64 => IsMonoRuntime && IsLinux && IsArm64Process;
public static bool IsNotMonoLinuxArm64 => !IsMonoLinuxArm64;

public static bool IsQemuDetected =>
#if TARGET_WINDOWS || TARGET_BROWSER || TARGET_WASI
false;
#else
Interop.Sys.IsQemuDetected();
#endif

// OSX family
public static bool IsApplePlatform => IsOSX || IsiOS || IstvOS || IsMacCatalyst;
public static bool IsOSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
Expand Down
2 changes: 2 additions & 0 deletions src/libraries/Common/tests/TestUtilities/TestUtilities.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
Link="Common\Interop\Unix\System.Security.Cryptography.Native\Interop.OpenSslGetProtocolSupport.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetEUid.cs"
Link="Common\Interop\Unix\Interop.GetEUid.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.IsQemuDetected.cs"
Link="Common\Interop\Unix\Interop.IsQemuDetected.cs" />
</ItemGroup>
<!-- Android imports -->
<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions src/native/libs/System.Native/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ if (NOT CLR_CMAKE_TARGET_WASI)
pal_signal.c
pal_threading.c
pal_uid.c
"${CLR_SRC_NATIVE_DIR}/minipal/cpufeatures.c"
)
else()
list (APPEND NATIVE_SOURCES
Expand Down
1 change: 1 addition & 0 deletions src/native/libs/System.Native/entrypoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ static const Entry s_sysNative[] =
DllImportEntry(SystemNative_GetUnixRelease)
DllImportEntry(SystemNative_GetUnixVersion)
DllImportEntry(SystemNative_GetOSArchitecture)
DllImportEntry(SystemNative_IsQemuDetected)
DllImportEntry(SystemNative_SearchPath)
DllImportEntry(SystemNative_SearchPath_TempDirectory)
DllImportEntry(SystemNative_RegisterForSigChld)
Expand Down
10 changes: 10 additions & 0 deletions src/native/libs/System.Native/pal_runtimeinformation.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include <minipal/cpufeatures.h>
#if defined(TARGET_ANDROID)
#include <sys/system_properties.h>
#elif defined(TARGET_OSX)
Expand Down Expand Up @@ -151,3 +152,12 @@ int32_t SystemNative_GetOSArchitecture(void)
return result;
#endif
}

int32_t SystemNative_IsQemuDetected(void)
{
#ifdef TARGET_WASI
return 0;
#else
return minipal_detect_qemu() ? 1 : 0;
#endif
}
2 changes: 2 additions & 0 deletions src/native/libs/System.Native/pal_runtimeinformation.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ PALEXPORT char* SystemNative_GetUnixRelease(void);
PALEXPORT int32_t SystemNative_GetUnixVersion(char* version, int* capacity);

PALEXPORT int32_t SystemNative_GetOSArchitecture(void);

PALEXPORT int32_t SystemNative_IsQemuDetected(void);
112 changes: 88 additions & 24 deletions src/native/minipal/cpufeatures.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include "cpufeatures.h"
#include "cpuid.h"

#if HOST_WINDOWS
#ifdef HOST_WINDOWS

#include <Windows.h>

Expand All @@ -28,6 +28,11 @@
#include <sys/auxv.h>
#include <asm/hwcap.h>

#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-macros"
#endif

// Light-up for hardware capabilities that are not present in older headers used by the portable build.
#ifndef HWCAP_ASIMDRDM
#define HWCAP_ASIMDRDM (1 << 12)
Expand All @@ -45,6 +50,14 @@
#define HWCAP_SVE (1 << 22)
#endif

#ifndef XSTATE_MASK_AVX512
#define XSTATE_MASK_AVX512 (0xE0) /* 0b1110_0000 */
#endif // XSTATE_MASK_AVX512

#ifdef __clang__
#pragma clang diagnostic pop
#endif

#endif

#if HAVE_SYSCTLBYNAME
Expand All @@ -56,7 +69,7 @@
#if defined(HOST_UNIX)
#if defined(HOST_X86) || defined(HOST_AMD64)

static uint32_t xmmYmmStateSupport()
static uint32_t xmmYmmStateSupport(void)
{
uint32_t eax;
__asm(" xgetbv\n" \
Expand All @@ -68,11 +81,7 @@ static uint32_t xmmYmmStateSupport()
return ((eax & 0x06) == 0x06) ? 1 : 0;
}

#ifndef XSTATE_MASK_AVX512
#define XSTATE_MASK_AVX512 (0xE0) /* 0b1110_0000 */
#endif // XSTATE_MASK_AVX512

static uint32_t avx512StateSupport()
static uint32_t avx512StateSupport(void)
{
#if defined(HOST_APPLE)
// MacOS has specialized behavior where it reports AVX512 support but doesnt
Expand All @@ -99,12 +108,12 @@ static uint32_t avx512StateSupport()
#endif
}

static bool IsAvxEnabled()
static bool IsAvxEnabled(void)
{
return true;
}

static bool IsAvx512Enabled()
static bool IsAvx512Enabled(void)
{
return true;
}
Expand Down Expand Up @@ -222,11 +231,11 @@ int minipal_getcpufeatures(void)

if (IsAvx512Enabled() && (avx512StateSupport() == 1)) // XGETBV XRC0[7:5] == 111
{
if (((cpuidInfo[CPUID_EBX] & (1 << 16)) != 0) && // AVX512F
((cpuidInfo[CPUID_EBX] & (1 << 30)) != 0) && // AVX512BW
((cpuidInfo[CPUID_EBX] & (1 << 28)) != 0) && // AVX512CD
((cpuidInfo[CPUID_EBX] & (1 << 17)) != 0) && // AVX512DQ
((cpuidInfo[CPUID_EBX] & (1 << 31)) != 0)) // AVX512VL
if ((((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 16)) != 0) && // AVX512F
(((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 30)) != 0) && // AVX512BW
(((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 28)) != 0) && // AVX512CD
(((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 17)) != 0) && // AVX512DQ
(((uint32_t)cpuidInfo[CPUID_EBX] & ((uint32_t)1 << 31)) != 0)) // AVX512VL
{
// While the AVX-512 ISAs can be individually lit-up, they really
// need F, BW, CD, DQ, and VL to be fully functional without adding
Expand Down Expand Up @@ -301,12 +310,12 @@ int minipal_getcpufeatures(void)
}
}

__cpuid(cpuidInfo, 0x80000000);
__cpuid(cpuidInfo, (int)0x80000000);
uint32_t maxCpuIdEx = (uint32_t)cpuidInfo[CPUID_EAX];

if (maxCpuIdEx >= 0x80000001)
{
__cpuid(cpuidInfo, 0x80000001);
__cpuid(cpuidInfo, (int)0x80000001);

if ((cpuidInfo[CPUID_ECX] & (1 << 5)) != 0) // LZCNT
{
Expand Down Expand Up @@ -441,35 +450,90 @@ int minipal_getcpufeatures(void)
return result;
}

// Detect if the current process is running under the Apple Rosetta x64 emulator
bool minipal_detect_rosetta(void)
static bool GetCpuBrand(char* brand, size_t bufferSize)
{
#if defined(HOST_AMD64) || defined(HOST_X86)
// Check for CPU brand indicating emulation
int regs[4];
char brand[49];

// Get the maximum value for extended function CPUID info
__cpuid(regs, (int)0x80000000);
if ((unsigned int)regs[0] < 0x80000004)
{
return false; // Extended CPUID not supported
brand[0] = '\0'; // Extended CPUID not supported, return empty string or handle error
return false;
}

// Retrieve the CPU brand string
// Retrieve the CPU brand string directly into the caller-provided buffer
for (unsigned int i = 0x80000002; i <= 0x80000004; ++i)
{
__cpuid(regs, (int)i);
memcpy(brand + (i - 0x80000002) * sizeof(regs), regs, sizeof(regs));
}
brand[sizeof(brand) - 1] = '\0';

brand[bufferSize - 1] = '\0';

return true;
#else
(void)brand;
(void)bufferSize;
return false;
#endif // HOST_AMD64 || HOST_X86
}

// Detect if the current process is running under the Apple Rosetta x64 emulator
bool minipal_detect_rosetta(void)
{
char brand[49];

// Check if CPU brand indicates emulation
return GetCpuBrand(brand, sizeof(brand)) && (strstr(brand, "VirtualApple") != NULL);
}

#if !defined(HOST_WINDOWS)

// Detect if the current process is running under QEMU
bool minipal_detect_qemu(void)
{
char brand[49];

// Check if CPU brand indicates emulation
if (strstr(brand, "VirtualApple") != NULL)
if (GetCpuBrand(brand, sizeof(brand)) && strstr(brand, "QEMU") != NULL)
{
return true;
}
#endif // HOST_AMD64 || HOST_X86

// Check for process name of PID 1 indicating emulation
char cmdline[256];
FILE *cmdline_file = fopen("/proc/1/cmdline", "r");
if (cmdline_file != NULL)
{
fgets(cmdline, sizeof(cmdline), cmdline_file);
fclose(cmdline_file);

if (strstr(cmdline, "qemu-") != NULL)
{
return true;
}
}

// Check for process-level emulation using ps command
FILE *ps_output = popen("ps -e -o comm=", "r");
if (ps_output != NULL)
{
char process_name[256];
while (fgets(process_name, sizeof(process_name), ps_output) != NULL)
{
if (strstr(process_name, "qemu") != NULL)
{
pclose(ps_output);
return true;
}
}
pclose(ps_output);
}

return false;
am11 marked this conversation as resolved.
Show resolved Hide resolved
}

#endif // !HOST_WINDOWS
1 change: 1 addition & 0 deletions src/native/minipal/cpufeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ extern "C"

int minipal_getcpufeatures(void);
bool minipal_detect_rosetta(void);
bool minipal_detect_qemu(void);

#ifdef __cplusplus
}
Expand Down
Loading