diff --git a/src/port/pg_crc32c_armv8_choose.c b/src/port/pg_crc32c_armv8_choose.c
index 306500154e..f035ac9855 100644
--- a/src/port/pg_crc32c_armv8_choose.c
+++ b/src/port/pg_crc32c_armv8_choose.c
@@ -31,6 +31,11 @@
 #endif
 #endif
 
+#if defined(__NetBSD__) && defined(__aarch64__)
+#include <sys/sysctl.h>
+#include <aarch64/armreg.h>
+#endif
+
 #include "port/pg_crc32c.h"
 
 static bool
@@ -52,6 +57,42 @@ pg_crc32c_armv8_available(void)
 #else
 	return (getauxval(AT_HWCAP2) & HWCAP2_CRC32) != 0;
 #endif
+#elif defined(__NetBSD__) && defined(__aarch64__)
+	/*
+	 * On NetBSD we can read AArch64 Instruction Set Attribute Register 0 via
+	 * sysctl.  We could probably do something similar on arm32 as well, but
+	 * it's undocumented.  Note we assume cpu0 is representative of all the
+	 * machine's CPUs.
+	 */
+	const char *path = "machdep.cpu0.cpu_id";
+	size_t		len;
+#define SYSCTL_CPU_ID_MAXSIZE	64
+	uint64		sysctlbuf[SYSCTL_CPU_ID_MAXSIZE];
+	struct aarch64_sysctl_cpu_id *id =
+		(struct aarch64_sysctl_cpu_id *) sysctlbuf;
+	uint64		reg;
+	uint64		fld;
+
+	len = sizeof(sysctlbuf);
+	memset(sysctlbuf, 0, len);
+	if (sysctlbyname(path, id, &len, NULL, 0) != 0)
+		return false;
+	if (len != sizeof(struct aarch64_sysctl_cpu_id))
+		return false;			/* kernel incompatibility? */
+
+#define ISAR0_CRC32_BITPOS 16
+#define ISAR0_CRC32_BITWIDTH 4
+#define WIDTHMASK(w)	((((uint64) 1) << (w)) - 1)
+
+	reg = id->ac_aa64isar0;
+	fld = (reg >> ISAR0_CRC32_BITPOS) & WIDTHMASK(ISAR0_CRC32_BITWIDTH);
+
+	/*
+	 * Available 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
