Hi Corinna, This patch adds ARM64 support for CPU information and cache detection used by /proc/cpuinfo and sysconf cache queries. Thanks & regards, K Chandru In-lined patch:
---
winsup/cygwin/fhandler/proc.cc | 242 +++++++++++++++++++++++++++
winsup/cygwin/local_includes/cpuid.h | 21 ++-
winsup/cygwin/sysconf.cc | 148 ++++++++++++++++
3 files changed, 408 insertions(+), 3 deletions(-)
diff --git a/winsup/cygwin/fhandler/proc.cc b/winsup/cygwin/fhandler/proc.cc
index f1cd468fc..ddc2f028d 100644
--- a/winsup/cygwin/fhandler/proc.cc
+++ b/winsup/cygwin/fhandler/proc.cc
@@ -643,6 +643,247 @@ mask_bits (uint32_t in)
static off_t
format_proc_cpuinfo (void *, char *&destbuf)
{
+#if defined(__aarch64__)
+ WCHAR cpu_key[128], *cpu_num_p;
+ int cpu_number;
+ size_t buf_size = 16384;
+ size_t buf_used = 0;
+ char *buf = (char *) malloc (buf_size);
+ if (!buf)
+ return 0;
+ char *bufptr = buf;
+ GROUP_AFFINITY orig_group_affinity;
+ bool affinity_set = false;
+ WORD num_cpu_per_group = __get_cpus_per_group ();
+
+ cpu_num_p = wcpcpy (cpu_key,
+ L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\");
+
+ for (cpu_number = 0; ; cpu_number++)
+ {
+ __small_swprintf (cpu_num_p, L"%d", cpu_number);
+ if (!NT_SUCCESS (RtlCheckRegistryKey (RTL_REGISTRY_ABSOLUTE, cpu_key)))
+ break;
+ buf_used = bufptr - buf;
+ if (buf_used + 1024 > buf_size)
+ {
+ buf_size *= 2;
+ char *newbuf = (char *) realloc (buf, buf_size);
+ if (!newbuf)
+ {
+ if (affinity_set)
+ SetThreadGroupAffinity (GetCurrentThread (),
+ &orig_group_affinity, NULL);
+ free (buf);
+ return 0;
+ }
+ buf = newbuf;
+ bufptr = buf + buf_used;
+ }
+
+ WORD cpu_group = cpu_number / num_cpu_per_group;
+ KAFFINITY cpu_mask = 1L << (cpu_number % num_cpu_per_group);
+
+ GROUP_AFFINITY affinity;
+ memset (&affinity, 0, sizeof affinity);
+ affinity.Mask = cpu_mask;
+ affinity.Group = cpu_group;
+
+ affinity_set = false;
+
+ if (SetThreadGroupAffinity (GetCurrentThread (), &affinity,
+ &orig_group_affinity))
+ {
+ affinity_set = true;
+ yield ();
+ }
+
+ DWORD cpu_mhz = 0;
+ RTL_QUERY_REGISTRY_TABLE tab[2] =
+ {
+ { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_NOSTRING,
+ L"~Mhz", &cpu_mhz, REG_NONE, NULL, 0 },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 }
+ };
+ RtlQueryRegistryValues (RTL_REGISTRY_ABSOLUTE, cpu_key, tab, NULL, NULL);
+
+ if (cpu_mhz > 0)
+ cpu_mhz = ((cpu_mhz - 1) / 10 + 1) * 10;
+ DWORD bogomips = cpu_mhz * 2;
+
+ /* Read string fields: VendorIdentifier, ProcessorNameString, Identifier.
+ RTL_QUERY_REGISTRY_DIRECT requires a UNICODE_STRING target for REG_SZ
+ values; using a DWORD target silently truncates the data. */
+ WCHAR vendor_buf[256] = {0};
+ WCHAR name_buf[256] = {0};
+ WCHAR ident_buf[256] = {0};
+
+ UNICODE_STRING vendor_us = { 0, sizeof vendor_buf, vendor_buf };
+ UNICODE_STRING name_us = { 0, sizeof name_buf, name_buf };
+ UNICODE_STRING ident_us = { 0, sizeof ident_buf, ident_buf };
+
+ RTL_QUERY_REGISTRY_TABLE str_tab[4] =
+ {
+ { NULL, RTL_QUERY_REGISTRY_DIRECT,
+ L"VendorIdentifier", &vendor_us, REG_SZ, NULL, 0 },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT,
+ L"ProcessorNameString", &name_us, REG_SZ, NULL, 0 },
+ { NULL, RTL_QUERY_REGISTRY_DIRECT,
+ L"Identifier", &ident_us, REG_SZ, NULL, 0 },
+ { NULL, 0, NULL, NULL, 0, NULL, 0 }
+ };
+ RtlQueryRegistryValues (RTL_REGISTRY_ABSOLUTE, cpu_key, str_tab,
+ NULL, NULL);
+
+ int id_family = 0, id_model = 0;
+ for (WCHAR *p = ident_buf; *p; p++)
+ {
+ if (!wcsncmp (p, L"Family ", 7))
+ id_family = (int) wcstol (p + 7, NULL, 10);
+ else if (!wcsncmp (p, L"Model ", 6))
+ id_model = (int) wcstol (p + 6, NULL, 10);
+ }
+
+ uint32_t cpu_implementer = 0x00;
+ uint32_t cpu_part = 0x000;
+ uint32_t cpu_variant = 0x0;
+ uint32_t cpu_revision = 0x0;
+ uint32_t cpu_arch = 8;
+
+ {
+ HANDLE hk = NULL;
+ UNICODE_STRING key_us;
+ OBJECT_ATTRIBUTES oa;
+ RtlInitUnicodeString (&key_us, cpu_key);
+ InitializeObjectAttributes (&oa, &key_us, OBJ_CASE_INSENSITIVE,
+ NULL, NULL);
+ if (NT_SUCCESS (NtOpenKey (&hk, KEY_READ, &oa)))
+ {
+ UNICODE_STRING val_us;
+ /* CP 4000 appears to contain the ARM MIDR value, but this
+ registry value is undocumented and has reportedly changed
+ across Windows updates. Treat it as best-effort only. */
+ RtlInitUnicodeString (&val_us, L"CP 4000");
+ BYTE kbuf[sizeof (KEY_VALUE_PARTIAL_INFORMATION) + 8] = {0};
+ PKEY_VALUE_PARTIAL_INFORMATION kvi =
+ (PKEY_VALUE_PARTIAL_INFORMATION) kbuf;
+ ULONG res_len;
+ if (NT_SUCCESS (NtQueryValueKey (hk, &val_us,
+ KeyValuePartialInformation,
+ kvi, sizeof kbuf, &res_len))
+ && kvi->DataLength >= 4)
+ {
+ uint32_t midr = 0;
+ memcpy (&midr, kvi->Data, 4);
+ /* Some implementations store the value in the upper 32 bits.
*/
+ if (midr == 0 && kvi->DataLength >= 8)
+ memcpy (&midr, kvi->Data + 4, 4);
+ if (midr != 0)
+ {
+ cpu_implementer = (midr >> 24) & 0xFF;
+ cpu_variant = (midr >> 20) & 0xF;
+ cpu_arch = (midr >> 16) & 0xF;
+ cpu_part = (midr >> 4) & 0xFFF;
+ cpu_revision = midr & 0xF;
+ /* Architecture field 0xF means "ARMv8+ with CPUID
+ scheme"; report as 8 (ARMv8) since we cannot read
+ ID_AA64ISAR* registers from userspace on Windows. */
+ if (cpu_arch == 0xF)
+ cpu_arch = 8;
+ }
+ }
+ NtClose (hk);
+ }
+ }
+
+ bufptr += __small_sprintf (bufptr, "processor\t: %d\n", cpu_number);
+
+ if (vendor_buf[0])
+ bufptr += __small_sprintf (bufptr, "vendor_id\t: %W\n", vendor_buf);
+
+ if (ident_buf[0])
+ {
+ bufptr += __small_sprintf (bufptr, "cpu family\t: %d\n", id_family);
+ bufptr += __small_sprintf (bufptr, "model\t\t: %d\n", id_model);
+ }
+ bufptr += __small_sprintf (bufptr, "stepping\t: %d\n", cpu_revision);
+
+ if (name_buf[0])
+ bufptr += __small_sprintf (bufptr, "model name\t: %W\n", name_buf);
+
+ bufptr += __small_sprintf (bufptr, "BogoMIPS\t: %d.00\n", bogomips);
+
+ /* Emit "cache size" line for /proc/cpuinfo feature parity with the
+ x86 branch.Values come from
+ GetLogicalProcessorInformationEx(RelationCache)
+ via get_cpu_cache_arm64() in sysconf.cc */
+ {
+ extern long get_cpu_cache_arm64 (int);
+ long cs = get_cpu_cache_arm64 (_SC_LEVEL3_CACHE_SIZE);
+ if (cs <= 0)
+ cs = get_cpu_cache_arm64 (_SC_LEVEL2_CACHE_SIZE);
+ if (cs <= 0)
+ {
+ long l1i = get_cpu_cache_arm64 (_SC_LEVEL1_ICACHE_SIZE);
+ long l1d = get_cpu_cache_arm64 (_SC_LEVEL1_DCACHE_SIZE);
+ if (l1i > 0 || l1d > 0)
+ cs = (l1i > 0 ? l1i : 0) + (l1d > 0 ? l1d : 0);
+ }
+ if (cs > 0)
+ bufptr += __small_sprintf (bufptr, "cache size\t: %d KB\n",
+ (int) (cs >> 10));
+ }
+
+ print ("Features\t:");
+ if (IsProcessorFeaturePresent (PF_ARM_V8_INSTRUCTIONS_AVAILABLE))
+ {
+ ftuprint ("fp");
+ ftuprint ("asimd");
+ ftuprint ("evtstrm");
+ }
+ if (IsProcessorFeaturePresent (PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE))
+ {
+ ftuprint ("aes");
+ ftuprint ("pmull");
+ ftuprint ("sha1");
+ ftuprint ("sha2");
+ }
+ if (IsProcessorFeaturePresent (PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE))
+ ftuprint ("crc32");
+ if (IsProcessorFeaturePresent (PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE))
+ ftuprint ("atomics");
+ if (IsProcessorFeaturePresent (PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE))
+ {
+ ftuprint ("asimdhp");
+ ftuprint ("asimddp");
+ ftuprint ("fphp");
+ }
+ if (IsProcessorFeaturePresent (PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE))
+ ftuprint ("jscvt");
+ if (IsProcessorFeaturePresent (PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE))
+ ftuprint ("lrcpc");
+ print ("\n");
+
+ if (cpu_implementer != 0 || cpu_part != 0)
+ bufptr += __small_sprintf (bufptr,
+ "CPU implementer\t: 0x%02x\n"
+ "CPU architecture: %d\n"
+ "CPU variant\t: 0x%x\n"
+ "CPU part\t: 0x%03x\n"
+ "CPU revision\t: %d\n",
+ cpu_implementer, cpu_arch, cpu_variant, cpu_part, cpu_revision);
+ bufptr += __small_sprintf (bufptr, "\n");
+
+ if (affinity_set)
+ SetThreadGroupAffinity (GetCurrentThread (), &orig_group_affinity,
NULL);
+ }
+
+ off_t len = bufptr - buf;
+ destbuf = (char *) crealloc_abort (destbuf, len);
+ memcpy (destbuf, buf, len);
+ free (buf);
+ return len;
+#else
WCHAR cpu_key[128], *cpu_num_p;
DWORD orig_affinity_mask = 0;
GROUP_AFFINITY orig_group_affinity;
@@ -1767,6 +2008,7 @@ format_proc_cpuinfo (void *, char *&destbuf)
destbuf = (char *) crealloc_abort (destbuf, bufptr - buf);
memcpy (destbuf, buf, bufptr - buf);
return bufptr - buf;
+#endif
}
static off_t
diff --git a/winsup/cygwin/local_includes/cpuid.h
b/winsup/cygwin/local_includes/cpuid.h
index 6dbb1bddf..674069a7e 100644
--- a/winsup/cygwin/local_includes/cpuid.h
+++ b/winsup/cygwin/local_includes/cpuid.h
@@ -13,17 +13,32 @@ static inline void __attribute ((always_inline))
cpuid (uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t ain,
uint32_t cin = 0)
{
+#if defined(__x86_64__)
asm volatile ("cpuid"
: "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
: "a" (ain), "c" (cin));
+#elif defined(__aarch64__)
+ /* AArch64 has no CPUID instruction. Identify the target as Windows
+ ARM64 on leaf 0, and return zeros for other leaves. */
+ *a = *b = *c = *d = 0;
+ if (ain == 0)
+ {
+ *a = 0;
+ *b = 0x63724141; /* "AArc" */
+ *d = 0x57343668; /* "h64W" */
+ *c = 0x00006e69; /* "in\0\0" */
+ }
+ (void) cin;
+#else
+#error unimplemented for this target
+#endif
}
-#ifdef __x86_64__
+#if defined(__x86_64__)
static inline bool __attribute ((always_inline))
can_set_flag (uint32_t long flag)
{
uint32_t long r1, r2;
-
asm volatile ("pushfq\n"
"popq %0\n"
"movq %0, %1\n"
@@ -39,7 +54,7 @@ can_set_flag (uint32_t long flag)
);
return ((r1 ^ r2) & flag) != 0;
}
-#else
+#elif !defined(__aarch64__)
#error unimplemented for this target
#endif
diff --git a/winsup/cygwin/sysconf.cc b/winsup/cygwin/sysconf.cc
index 6529731a5..59646affd 100644
--- a/winsup/cygwin/sysconf.cc
+++ b/winsup/cygwin/sysconf.cc
@@ -98,6 +98,11 @@ get_avphys (int in)
/ (wincap.allocation_granularity () / wincap.page_size ());
}
+/* Cache detection for Intel and AMD uses CPUID, which are only
+ available on x86_64. On AArch64, EL1 system registers fault from
+ userspace. Use GetLogicalProcessorInformationEx instead. */
+#if !defined(__aarch64__)
+
enum cache_level
{
LevelNone,
@@ -454,9 +459,151 @@ get_cpu_cache_amd (int in, uint32_t maxe)
return ret;
}
+#endif /* !__aarch64__ */
+
+/* On ARM64, GetLogicalProcessorInformationEx returns one cache
+ descriptor per core, unlike Intel CPUID leaf 4 which returns
+ one descriptor per cache level. We filter to CPU 0's descriptors
+ and return the first match. */
+
+#if defined(__aarch64__)
+long
+get_cpu_cache_arm64 (int in)
+{
+ DWORD len = 0;
+
+ GetLogicalProcessorInformationEx (RelationCache, NULL, &len);
+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER || len == 0)
+ return 0;
+
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buf =
+ (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) malloc (len);
+ if (!buf)
+ return 0;
+
+ if (!GetLogicalProcessorInformationEx (RelationCache, buf, &len))
+ {
+ free (buf);
+ return 0;
+ }
+
+ long ret = 0;
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX p = buf;
+ DWORD remaining = len;
+
+ GROUP_AFFINITY cpu0_affinity = {};
+ cpu0_affinity.Group = 0;
+ cpu0_affinity.Mask = 1;
+
+ while (remaining > 0)
+ {
+ if (p->Size == 0)
+ break;
+ if (p->Relationship == RelationCache)
+ {
+ CACHE_RELATIONSHIP *cr = &p->Cache;
+ bool covers_cpu0 =
+ (cr->GroupMask.Group == cpu0_affinity.Group) &&
+ (cr->GroupMask.Mask & cpu0_affinity.Mask);
+
+ if (!covers_cpu0)
+ {
+ remaining -= p->Size;
+ p = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ ((BYTE *) p + p->Size);
+ continue;
+ }
+
+ uint8_t level = cr->Level;
+ uint8_t type = cr->Type;
+ uint32_t size = cr->CacheSize;
+ uint32_t asc = cr->Associativity;
+ uint32_t lsize = cr->LineSize;
+ bool match = false;
+
+ switch (in)
+ {
+ case _SC_LEVEL1_ICACHE_SIZE:
+ if (level == 1 && type == CacheInstruction)
+ { ret = size; match = true; }
+ break;
+ case _SC_LEVEL1_ICACHE_ASSOC:
+ if (level == 1 && type == CacheInstruction)
+ { ret = (asc == 0xFF) ? 0x8000 : asc; match = true; }
+ break;
+ case _SC_LEVEL1_ICACHE_LINESIZE:
+ if (level == 1 && type == CacheInstruction)
+ { ret = lsize; match = true; }
+ break;
+ case _SC_LEVEL1_DCACHE_SIZE:
+ if (level == 1 && type == CacheData)
+ { ret = size; match = true; }
+ break;
+ case _SC_LEVEL1_DCACHE_ASSOC:
+ if (level == 1 && type == CacheData)
+ { ret = (asc == 0xFF) ? 0x8000 : asc; match = true; }
+ break;
+ case _SC_LEVEL1_DCACHE_LINESIZE:
+ if (level == 1 && type == CacheData)
+ { ret = lsize; match = true; }
+ break;
+ case _SC_LEVEL2_CACHE_SIZE:
+ if (level == 2)
+ { ret = size; match = true; }
+ break;
+ case _SC_LEVEL2_CACHE_ASSOC:
+ if (level == 2)
+ { ret = (asc == 0xFF) ? 0x8000 : asc; match = true; }
+ break;
+ case _SC_LEVEL2_CACHE_LINESIZE:
+ if (level == 2)
+ { ret = lsize; match = true; }
+ break;
+ case _SC_LEVEL3_CACHE_SIZE:
+ if (level == 3)
+ { ret = size; match = true; }
+ break;
+ case _SC_LEVEL3_CACHE_ASSOC:
+ if (level == 3)
+ { ret = (asc == 0xFF) ? 0x8000 : asc; match = true; }
+ break;
+ case _SC_LEVEL3_CACHE_LINESIZE:
+ if (level == 3)
+ { ret = lsize; match = true; }
+ break;
+ case _SC_LEVEL4_CACHE_SIZE:
+ if (level == 4)
+ { ret = size; match = true; }
+ break;
+ case _SC_LEVEL4_CACHE_ASSOC:
+ if (level == 4)
+ { ret = (asc == 0xFF) ? 0x8000 : asc; match = true; }
+ break;
+ case _SC_LEVEL4_CACHE_LINESIZE:
+ if (level == 4)
+ { ret = lsize; match = true; }
+ break;
+ default:
+ break;
+ }
+ if (match)
+ break;
+ }
+ remaining -= p->Size;
+ p = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX) ((BYTE *) p + p->Size);
+ }
+
+ free (buf);
+ return ret;
+}
+#endif /* __aarch64__ */
+
static long
get_cpu_cache (int in)
{
+#if defined(__aarch64__)
+ return get_cpu_cache_arm64 (in);
+#else
uint32_t maxf, vendor_id[4];
cpuid (&maxf, &vendor_id[0], &vendor_id[2], &vendor_id[1], 0x00000000);
@@ -471,6 +618,7 @@ get_cpu_cache (int in)
return get_cpu_cache_amd (in, maxe);
}
return 0;
+#endif
}
enum sc_type { cons, func };
--
2.49.0.windows.1
Cygwin-cpuid-add-AArch64-build-stubs.patch
Description: Cygwin-cpuid-add-AArch64-build-stubs.patch
