Introduce basic hardware instruction wrappers and helper functions for
interacting with x86 Protection Keys for Userspace (PKRU / MPK) in QEMU.

Signed-off-by: Jacky Li <[email protected]>
---
 MAINTAINERS |  1 +
 util/pkey.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 97 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b51f5c3e60..5a31c45963 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -156,6 +156,7 @@ F: target/i386/meson.build
 F: tools/i386/
 F: tests/functional/i386/
 F: tests/functional/x86_64/
+F: util/pkey.c
 
 X86 VM file descriptor change on reset test
 M: Ani Sinha <[email protected]>
diff --git a/util/pkey.c b/util/pkey.c
new file mode 100644
index 0000000000..4f14a72151
--- /dev/null
+++ b/util/pkey.c
@@ -0,0 +1,96 @@
+/*
+ * QEMU guest memory protection key helpers
+ *
+ * Copyright (c) 2026 Google LLC
+ *
+ * Author:
+ *  Jacky Li <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "exec/cpu-common.h"
+#include "qemu/error-report.h"
+
+#if defined(HOST_X86_64) && defined(CONFIG_LINUX)
+#include <asm/unistd.h>
+#include <cpuid.h>
+#include <immintrin.h>
+#include <linux/kvm.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/syscall.h>
+
+#include "qemu/atomic.h"
+
+#ifndef PR_SET_SPECULATION_CTRL
+#define PR_SET_SPECULATION_CTRL 53
+#endif
+#ifndef PR_SPEC_INDIRECT_BRANCH
+#define PR_SPEC_INDIRECT_BRANCH 1
+#endif
+#ifndef PR_SPEC_DISABLE
+#define PR_SPEC_DISABLE (1UL << 2)
+#endif
+
+#ifndef PKEY_DISABLE_ACCESS
+#define PKEY_DISABLE_ACCESS 0x1
+#endif
+
+/* Each protection key occupies exactly 2 bits in the PKRU register. */
+#define BITS_PER_KEY 2
+/* The mask used to extract/write the 2-bit permission flags. */
+#define KEY_MASK 3U
+/* Max protection keys supported on x86_64 */
+#define KEY_COUNT 16
+
+static inline __attribute__((target("pku"), always_inline))
+uint32_t rdpkru(void)
+{
+    return _rdpkru_u32();
+}
+
+static inline __attribute__((target("pku"), always_inline)) void wrpkru(
+        uint32_t pkru)
+{
+    _wrpkru(pkru);
+}
+
+static inline __attribute__((target("pku"), always_inline)) int 
inline_pkey_get(
+        int pkey)
+{
+    uint32_t pkru = rdpkru();
+    return (pkru >> (pkey * BITS_PER_KEY)) & KEY_MASK;
+}
+
+static inline __attribute__((target("pku"), always_inline)) void
+inline_pkey_set(int pkey, unsigned int access_rights)
+{
+    uint32_t pkru = rdpkru();
+    pkru &= ~(KEY_MASK << (pkey * BITS_PER_KEY));
+    pkru |= ((access_rights & KEY_MASK) << (pkey * BITS_PER_KEY));
+    wrpkru(pkru);
+}
+
+static inline __attribute__((always_inline)) intptr_t local_syscall3(
+        intptr_t num, intptr_t arg1, intptr_t arg2, intptr_t arg3)
+{
+    intptr_t ret = num;
+    asm volatile("syscall\n"
+               : "+a"(ret)
+               : "D"(arg1), "S"(arg2), "d"(arg3)
+               : "rcx", "r11", "memory");
+    return ret;
+}
+
+#else
+/* Dummy implementations for all other configurations (non-x86_64 Linux, */
+/* Windows, macOS, etc.) */
+#if defined(CONFIG_LINUX)
+#include <linux/kvm.h>
+#include <sys/ioctl.h>
+#endif
+#endif

-- 
2.55.0.737.g08866a6d13-goog


Reply via email to