Support runtime CRC feature probing on NetBSD/ARM using sysctl().
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Nov 2024 16:53:26 +0000 (11:53 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 25 Nov 2024 16:53:26 +0000 (11:53 -0500)
Commit aac831caf left this as a to-do; here's code to do it.
Like the previous patch, this is HEAD-only for now.

Discussion: https://postgr.es/m/4496616.iHFcN1HehY@portable-bastien

src/port/pg_crc32c_armv8_choose.c

index 306500154ee62f51e9adf6d0ec499cfea48e0867..45a52c7724752b10867c2e73077aa5b641b73d9e 100644 (file)
 #endif
 #endif
 
+#if defined(__NetBSD__)
+#include <sys/sysctl.h>
+#if defined(__aarch64__)
+#include <aarch64/armreg.h>
+#endif
+#endif
+
 #include "port/pg_crc32c.h"
 
 static bool
@@ -52,6 +59,49 @@ pg_crc32c_armv8_available(void)
 #else
        return (getauxval(AT_HWCAP2) & HWCAP2_CRC32) != 0;
 #endif
+#elif defined(__NetBSD__)
+       /*
+        * On NetBSD we can read the Instruction Set Attribute Registers via
+        * sysctl.  For doubtless-historical reasons the sysctl interface is
+        * completely different on 64-bit than 32-bit, but the underlying
+        * registers contain the same fields.
+        */
+#define ISAR0_CRC32_BITPOS 16
+#define ISAR0_CRC32_BITWIDTH 4
+#define WIDTHMASK(w)   ((1 << (w)) - 1)
+#define SYSCTL_CPU_ID_MAXSIZE 64
+
+       size_t          len;
+       uint64          sysctlbuf[SYSCTL_CPU_ID_MAXSIZE];
+#if defined(__aarch64__)
+       /* We assume cpu0 is representative of all the machine's CPUs. */
+       const char *path = "machdep.cpu0.cpu_id";
+       size_t          expected_len = sizeof(struct aarch64_sysctl_cpu_id);
+#define ISAR0 ((struct aarch64_sysctl_cpu_id *) sysctlbuf)->ac_aa64isar0
+#else
+       const char *path = "machdep.id_isar";
+       size_t          expected_len = 6 * sizeof(int);
+#define ISAR0 ((int *) sysctlbuf)[5]
+#endif
+       uint64          fld;
+
+       /* Fetch the appropriate set of register values. */
+       len = sizeof(sysctlbuf);
+       memset(sysctlbuf, 0, len);
+       if (sysctlbyname(path, sysctlbuf, &len, NULL, 0) != 0)
+               return false;                   /* perhaps kernel is 64-bit and we aren't? */
+       if (len != expected_len)
+               return false;                   /* kernel API change? */
+
+       /* Fetch the CRC32 field from ISAR0. */
+       fld = (ISAR0 >> ISAR0_CRC32_BITPOS) & WIDTHMASK(ISAR0_CRC32_BITWIDTH);
+
+       /*
+        * Current documentation defines only the field values 0 (No CRC32) and 1
+        * (CRC32B/CRC32H/CRC32W/CRC32X/CRC32CB/CRC32CH/CRC32CW/CRC32CX).  Assume
+        * that any future nonzero value will be a superset of 1.
+        */
+       return (fld != 0);
 #else
        return false;
 #endif