Page MenuHomeFreeBSD

D58782.id184025.diff
No OneTemporary

D58782.id184025.diff

diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c
--- a/sys/amd64/amd64/sys_machdep.c
+++ b/sys/amd64/amd64/sys_machdep.c
@@ -167,11 +167,44 @@
critical_exit();
}
+/*
+ * Tag (or untag, when 'clear' is true) a range of the calling
+ * process's address space with a protection key. The map read lock
+ * synchronizes with a parallel pmap_vmspace_copy() on fork.
+ *
+ * Shared between sysarch(2) and the Linuxulator's pkey_mprotect().
+ */
+int
+amd64_pkru_update(struct thread *td, uintptr_t addr, size_t len, u_int keyidx,
+ int flags, bool clear)
+{
+ struct vm_map *map;
+ vm_offset_t start, end;
+ int error;
+
+ MPASS(td == curthread);
+
+ map = &td->td_proc->p_vmspace->vm_map;
+ vm_map_lock_read(map);
+ if (len == 0 || !vm_map_check_boundary(map, addr, addr + len)) {
+ vm_map_unlock_read(map);
+ return (EINVAL);
+ }
+ start = trunc_page(addr);
+ end = round_page(addr + len);
+ if (clear)
+ error = pmap_pkru_clear(PCPU_GET(curpmap), start, end);
+ else
+ error = pmap_pkru_set(PCPU_GET(curpmap), start, end, keyidx,
+ flags);
+ vm_map_unlock_read(map);
+ return (error);
+}
+
int
sysarch(struct thread *td, struct sysarch_args *uap)
{
struct pcb *pcb;
- struct vm_map *map;
uint32_t i386base;
uint64_t a64base;
struct i386_ioperm_args iargs;
@@ -368,58 +401,20 @@
break;
case I386_SET_PKRU:
- case AMD64_SET_PKRU: {
- vm_offset_t addr, start, end;
- vm_size_t len;
-
- addr = (uintptr_t)a64pkru.addr;
- len = a64pkru.len;
-
- /*
- * Read-lock the map to synchronize with parallel
- * pmap_vmspace_copy() on fork.
- */
- map = &td->td_proc->p_vmspace->vm_map;
- vm_map_lock_read(map);
- if (len == 0 || !vm_map_check_boundary(map, addr, addr + len)) {
- vm_map_unlock_read(map);
- error = EINVAL;
- break;
- }
- start = trunc_page(addr);
- end = round_page(addr + len);
- error = pmap_pkru_set(PCPU_GET(curpmap), start, end,
- a64pkru.keyidx, a64pkru.flags);
- vm_map_unlock_read(map);
+ case AMD64_SET_PKRU:
+ error = amd64_pkru_update(td, (uintptr_t)a64pkru.addr,
+ a64pkru.len, a64pkru.keyidx, a64pkru.flags, false);
break;
- }
case I386_CLEAR_PKRU:
- case AMD64_CLEAR_PKRU: {
- vm_offset_t addr, start, end;
- vm_size_t len;
-
+ case AMD64_CLEAR_PKRU:
if (a64pkru.flags != 0 || a64pkru.keyidx != 0) {
error = EINVAL;
break;
}
-
- addr = (uintptr_t)a64pkru.addr;
- len = a64pkru.len;
-
- map = &td->td_proc->p_vmspace->vm_map;
- vm_map_lock_read(map);
- if (len == 0 || !vm_map_check_boundary(map, addr, addr + len)) {
- vm_map_unlock_read(map);
- error = EINVAL;
- break;
- }
- start = trunc_page(addr);
- end = round_page(addr + len);
- error = pmap_pkru_clear(PCPU_GET(curpmap), start, end);
- vm_map_unlock_read(map);
+ error = amd64_pkru_update(td, (uintptr_t)a64pkru.addr,
+ a64pkru.len, 0, 0, true);
break;
- }
case AMD64_DISABLE_TLSBASE:
clear_pcb_flags(pcb, PCB_TLSBASE);
diff --git a/sys/amd64/linux/linux_emul_md.h b/sys/amd64/linux/linux_emul_md.h
new file mode 100644
--- /dev/null
+++ b/sys/amd64/linux/linux_emul_md.h
@@ -0,0 +1,35 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+#ifndef _AMD64_LINUX_EMUL_MD_H_
+#define _AMD64_LINUX_EMUL_MD_H_
+
+/*
+ * Machine-dependent part of the Linux process emuldata, embedded in
+ * struct linux_pemuldata as pem_md.
+ */
+struct linux_pemuldata_md {
+ uint32_t md_pkey_allocation_map; /* x86 protection keys */
+};
+
+/*
+ * Initial protection key allocation map: key 0 is the default key,
+ * implicitly allocated on Linux (mm_pkey_allocation_map is initialized
+ * to 0x1). Inherited on fork, reset on exec.
+ */
+#define LINUX_PKEY_INITIAL_MAP 0x1
+
+/*
+ * Initial PKRU at exec: access disabled for keys 1..15, key 0 open;
+ * the Linux init_pkru default.
+ */
+#define LINUX_PKRU_INIT 0x55555554
+
+struct thread;
+
+void linux_pkru_exec_init(struct thread *);
+
+#endif /* !_AMD64_LINUX_EMUL_MD_H_ */
diff --git a/sys/amd64/linux/linux_pkru.c b/sys/amd64/linux/linux_pkru.c
new file mode 100644
--- /dev/null
+++ b/sys/amd64/linux/linux_pkru.c
@@ -0,0 +1,226 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+/*
+ * x86 memory protection keys (PKU) for the Linuxulator, serving both
+ * the 64-bit and 32-bit Linux ABIs.
+ *
+ * The PKRU register is directly user-visible: Linux programs read and
+ * write it with RDPKRU/WRPKRU, which execute natively. The kernel's
+ * part is key allocation bookkeeping (per address space: inherited on
+ * fork, reset on exec, as with Linux mm->context.pkey_allocation_map),
+ * tagging pages (pkey_mprotect), and applying the initial access
+ * rights of pkey_alloc() to the calling thread's PKRU.
+ */
+
+#include <sys/systm.h>
+#include <sys/imgact.h>
+#include <sys/lock.h>
+#include <sys/pcpu.h>
+#include <sys/proc.h>
+#include <sys/sx.h>
+
+#include <machine/cpufunc.h>
+#include <machine/fpu.h>
+#include <machine/md_var.h>
+#include <machine/pcb.h>
+#include <machine/specialreg.h>
+#include <machine/sysarch.h>
+#include <x86/x86_var.h>
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_mmap.h>
+
+static bool
+linux_pkey_supported(void)
+{
+
+ return ((cpu_stdext_feature2 & CPUID_STDEXT2_OSPKE) != 0);
+}
+
+/*
+ * Update the calling thread's PKRU: new value is (PKRU & keep) | set.
+ */
+static void
+linux_pkru_write(struct thread *td, uint32_t keep, uint32_t set)
+{
+ struct pcb *pcb;
+ struct xstate_hdr *hdr;
+ char *sa;
+ uint32_t *pkru;
+
+ MPASS(td == curthread);
+ pcb = td->td_pcb;
+
+ /*
+ * The critical section is held across the save area update to
+ * exclude preemption: a context switch could otherwise load the
+ * xsave area back into the CPU after fpugetregs(), and the
+ * stores below would then be lost to the next save.
+ */
+ critical_enter();
+ if ((pcb->pcb_flags & PCB_USERFPUINITDONE) != 0 &&
+ td == PCPU_GET(fpcurthread) && PCB_USER_FPU(pcb)) {
+ wrpkru((rdpkru() & keep) | set);
+ critical_exit();
+ return;
+ }
+
+ /*
+ * The user FPU state is in the PCB save area, or is not yet
+ * initialized, in which case fpugetregs() installs the initial
+ * state there.
+ */
+ (void)fpugetregs(td);
+ sa = (char *)get_pcb_user_save_td(td);
+ hdr = (struct xstate_hdr *)(sa + xsave_area_hdr_offset());
+ pkru = (uint32_t *)(sa + xsave_area_offset(xsave_mask,
+ XFEATURE_ENABLED_PKRU, false, false));
+ if ((hdr->xstate_bv & XFEATURE_ENABLED_PKRU) == 0) {
+ hdr->xstate_bv |= XFEATURE_ENABLED_PKRU;
+ *pkru = 0;
+ }
+ *pkru = (*pkru & keep) | set;
+ critical_exit();
+}
+
+/*
+ * Set the calling thread's PKRU access rights for the given key.
+ */
+static void
+linux_pkru_set_perm(struct thread *td, u_int keyidx, uint32_t rights)
+{
+
+ linux_pkru_write(td, ~(LINUX_PKEY_ACCESS_MASK << (keyidx * 2)),
+ rights << (keyidx * 2));
+}
+
+/*
+ * Called from the Linux sysvecs' exec_setregs. Linux initializes
+ * PKRU at exec to deny access to all keys but key 0
+ * (arch/x86/mm/pkeys.c init_pkru_value), so memory tagged with a not
+ * yet allocated key is inaccessible; FreeBSD's initial PKRU is 0.
+ * This initializes the user FPU state slightly earlier than the lazy
+ * first-use path; the state would be initialized moments later in
+ * rtld/libc startup regardless.
+ */
+void
+linux_pkru_exec_init(struct thread *td)
+{
+
+ if (!linux_pkey_supported())
+ return;
+ linux_pkru_write(td, 0, LINUX_PKRU_INIT);
+}
+
+/*
+ * Protection keys are a property of the address space: inherit the
+ * allocation map on fork, as Linux does. When a FreeBSD process is
+ * switching to the Linux ABI there is no parent emuldata; start from
+ * the initial map. The unlocked read is atomic on the aligned word;
+ * a pkey_alloc() racing the fork in another thread yields a valid
+ * serialization either way.
+ */
+void
+linux_pemuldata_init_md(struct thread *td, struct linux_pemuldata *pem)
+{
+ struct linux_pemuldata *ppem;
+
+ ppem = pem_find(td->td_proc);
+ if (ppem != NULL)
+ pem->pem_md.md_pkey_allocation_map =
+ ppem->pem_md.md_pkey_allocation_map;
+ else
+ pem->pem_md.md_pkey_allocation_map = LINUX_PKEY_INITIAL_MAP;
+}
+
+void
+linux_pemuldata_exec_md(struct linux_pemuldata *pem)
+{
+
+ pem->pem_md.md_pkey_allocation_map = LINUX_PKEY_INITIAL_MAP;
+}
+
+int
+linux_pkey_alloc_machdep(struct thread *td, uint64_t init_val)
+{
+ struct linux_pemuldata *pem;
+ uint32_t free_keys;
+ int key;
+
+ if (!linux_pkey_supported())
+ return (ENOSPC);
+
+ pem = pem_find(td->td_proc);
+ LINUX_PEM_XLOCK(pem);
+ free_keys = ~pem->pem_md.md_pkey_allocation_map &
+ ((1u << LINUX_PKEY_MAX) - 1) & ~LINUX_PKEY_INITIAL_MAP;
+ if (free_keys == 0) {
+ LINUX_PEM_XUNLOCK(pem);
+ return (ENOSPC);
+ }
+ key = ffs(free_keys) - 1;
+ pem->pem_md.md_pkey_allocation_map |= 1u << key;
+ LINUX_PEM_XUNLOCK(pem);
+
+ linux_pkru_set_perm(td, key, init_val);
+ td->td_retval[0] = key;
+ return (0);
+}
+
+int
+linux_pkey_free_machdep(struct thread *td, int pkey)
+{
+ struct linux_pemuldata *pem;
+
+ if (!linux_pkey_supported())
+ return (EINVAL);
+
+ pem = pem_find(td->td_proc);
+ LINUX_PEM_XLOCK(pem);
+ if ((pem->pem_md.md_pkey_allocation_map & (1u << pkey)) == 0) {
+ LINUX_PEM_XUNLOCK(pem);
+ return (EINVAL);
+ }
+ pem->pem_md.md_pkey_allocation_map &= ~(1u << pkey);
+ LINUX_PEM_XUNLOCK(pem);
+
+ /*
+ * As on Linux, freeing a key neither untags pages nor updates
+ * PKRU; that is the application's responsibility.
+ */
+ return (0);
+}
+
+int
+linux_pkey_mprotect_machdep(struct thread *td, uintptr_t addr, size_t len,
+ int prot, int pkey)
+{
+ struct linux_pemuldata *pem;
+ int error;
+
+ if (!linux_pkey_supported())
+ return (EINVAL);
+
+ pem = pem_find(td->td_proc);
+ LINUX_PEM_SLOCK(pem);
+ if ((pem->pem_md.md_pkey_allocation_map & (1u << pkey)) == 0) {
+ LINUX_PEM_SUNLOCK(pem);
+ return (EINVAL);
+ }
+ LINUX_PEM_SUNLOCK(pem);
+
+ error = linux_mprotect_common(td, addr, len, prot);
+ if (error != 0 || len == 0)
+ return (error);
+
+ /*
+ * Tag the range; a pkey of 0 untags it. The tag is not
+ * persistent: it dies with the mapping, matching Linux VMA
+ * semantics.
+ */
+ return (amd64_pkru_update(td, addr, len, pkey, 0, pkey == 0));
+}
diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c
--- a/sys/amd64/linux/linux_sysvec.c
+++ b/sys/amd64/linux/linux_sysvec.c
@@ -57,6 +57,7 @@
#include <x86/linux/linux_x86.h>
#include <amd64/linux/linux.h>
+#include <amd64/linux/linux_emul_md.h>
#include <amd64/linux/linux_proto.h>
#include <compat/linux/linux_elf.h>
#include <compat/linux/linux_emul.h>
@@ -271,6 +272,9 @@
* clean FP state if it uses the FPU again.
*/
fpstate_drop(td);
+
+ /* Linux processes start with PKRU denying unallocated keys. */
+ linux_pkru_exec_init(td);
}
static int
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -61,6 +61,7 @@
#include <machine/trap.h>
#include <x86/linux/linux_x86.h>
+#include <amd64/linux/linux_emul_md.h>
#include <amd64/linux32/linux.h>
#include <amd64/linux32/linux32_proto.h>
#include <compat/linux/linux_elf.h>
@@ -609,6 +610,9 @@
x86_clear_dbregs(pcb);
fpstate_drop(td);
+
+ /* Linux processes start with PKRU denying unallocated keys. */
+ linux_pkru_exec_init(td);
}
/*
diff --git a/sys/arm64/linux/linux_emul_md.h b/sys/arm64/linux/linux_emul_md.h
new file mode 100644
--- /dev/null
+++ b/sys/arm64/linux/linux_emul_md.h
@@ -0,0 +1,18 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+#ifndef _ARM64_LINUX_EMUL_MD_H_
+#define _ARM64_LINUX_EMUL_MD_H_
+
+/*
+ * Machine-dependent part of the Linux process emuldata, embedded in
+ * struct linux_pemuldata as pem_md.
+ */
+struct linux_pemuldata_md {
+ int md_dummy; /* no machine-dependent state yet */
+};
+
+#endif /* !_ARM64_LINUX_EMUL_MD_H_ */
diff --git a/sys/arm64/linux/linux_emul_md.c b/sys/arm64/linux/linux_emul_md.c
new file mode 100644
--- /dev/null
+++ b/sys/arm64/linux/linux_emul_md.c
@@ -0,0 +1,52 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_mmap.h>
+
+/* No machine-dependent emuldata state yet. */
+
+void
+linux_pemuldata_init_md(struct thread *td, struct linux_pemuldata *pem)
+{
+}
+
+void
+linux_pemuldata_exec_md(struct linux_pemuldata *pem)
+{
+}
+
+/*
+ * Protection key back ends: behave as Linux does on hardware without
+ * protection keys. pkey_alloc() reports no free keys and only the
+ * default key semantics remain.
+ */
+
+int
+linux_pkey_alloc_machdep(struct thread *td, uint64_t init_val)
+{
+
+ return (ENOSPC);
+}
+
+int
+linux_pkey_free_machdep(struct thread *td, int pkey)
+{
+
+ return (EINVAL);
+}
+
+int
+linux_pkey_mprotect_machdep(struct thread *td, uintptr_t addr, size_t len,
+ int prot, int pkey)
+{
+
+ return (EINVAL);
+}
diff --git a/sys/compat/linux/linux_dummy.c b/sys/compat/linux/linux_dummy.c
--- a/sys/compat/linux/linux_dummy.c
+++ b/sys/compat/linux/linux_dummy.c
@@ -110,9 +110,6 @@
DUMMY(preadv2);
DUMMY(pwritev2);
/* Linux 4.8: */
-DUMMY(pkey_mprotect);
-DUMMY(pkey_alloc);
-DUMMY(pkey_free);
/* Linux 4.18: */
DUMMY(io_pgetevents);
/* Linux 5.1: */
diff --git a/sys/compat/linux/linux_emul.h b/sys/compat/linux/linux_emul.h
--- a/sys/compat/linux/linux_emul.h
+++ b/sys/compat/linux/linux_emul.h
@@ -30,6 +30,8 @@
#ifndef _LINUX_EMUL_H_
#define _LINUX_EMUL_H_
+#include <machine/../linux/linux_emul_md.h>
+
struct image_params;
/*
@@ -69,6 +71,7 @@
uint32_t oom_score_adj; /* /proc/self/oom_score_adj */
uint32_t so_timestamp; /* requested timeval */
uint32_t so_timestampns; /* requested timespec */
+ struct linux_pemuldata_md pem_md; /* machine-dependent state */
};
#define LINUX_PEM_XLOCK(p) sx_xlock(&(p)->pem_sx)
@@ -78,4 +81,13 @@
struct linux_pemuldata *pem_find(struct proc *);
+/*
+ * Machine-dependent hooks for struct linux_pemuldata lifecycle
+ * events, implemented per-arch: initialization of pem_md when the
+ * emuldata is created (fork, or a process switching to the Linux
+ * ABI) and reset at exec.
+ */
+void linux_pemuldata_init_md(struct thread *, struct linux_pemuldata *);
+void linux_pemuldata_exec_md(struct linux_pemuldata *);
+
#endif /* !_LINUX_EMUL_H_ */
diff --git a/sys/compat/linux/linux_emul.c b/sys/compat/linux/linux_emul.c
--- a/sys/compat/linux/linux_emul.c
+++ b/sys/compat/linux/linux_emul.c
@@ -157,6 +157,7 @@
pem = malloc(sizeof(*pem), M_LINUX, M_WAITOK | M_ZERO);
sx_init(&pem->pem_sx, "lpemlk");
+ linux_pemuldata_init_md(td, pem);
p->p_emuldata = pem;
}
newtd->td_emuldata = em;
@@ -183,6 +184,7 @@
KASSERT(pem != NULL, ("proc_init: proc emuldata not found.\n"));
pem->persona = 0;
pem->oom_score_adj = 0;
+ linux_pemuldata_exec_md(pem);
}
}
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -359,6 +359,28 @@
uap->prot));
}
+int
+linux_pkey_mprotect(struct thread *td, struct linux_pkey_mprotect_args *uap)
+{
+
+ return (linux_pkey_mprotect_common(td, uap->start, uap->len,
+ uap->prot, uap->pkey));
+}
+
+int
+linux_pkey_alloc(struct thread *td, struct linux_pkey_alloc_args *uap)
+{
+
+ return (linux_pkey_alloc_common(td, uap->flags, uap->init_val));
+}
+
+int
+linux_pkey_free(struct thread *td, struct linux_pkey_free_args *uap)
+{
+
+ return (linux_pkey_free_common(td, uap->pkey));
+}
+
int
linux_madvise(struct thread *td, struct linux_madvise_args *uap)
{
diff --git a/sys/compat/linux/linux_mmap.h b/sys/compat/linux/linux_mmap.h
--- a/sys/compat/linux/linux_mmap.h
+++ b/sys/compat/linux/linux_mmap.h
@@ -66,6 +66,27 @@
int linux_mmap_common(struct thread *, uintptr_t, size_t, int, int,
int, off_t);
int linux_mprotect_common(struct thread *, uintptr_t, size_t, int);
+int linux_pkey_alloc_common(struct thread *, uint64_t, uint64_t);
+int linux_pkey_free_common(struct thread *, int);
+int linux_pkey_mprotect_common(struct thread *, uintptr_t, size_t, int, int);
+
+/*
+ * Machine-dependent protection key back ends: the real implementation
+ * on amd64 (sys/amd64/linux/linux_pkru.c), stubs behaving as Linux
+ * does on hardware without protection keys elsewhere (each arch's
+ * linux_emul_md.c).
+ */
+int linux_pkey_alloc_machdep(struct thread *, uint64_t);
+int linux_pkey_free_machdep(struct thread *, int);
+int linux_pkey_mprotect_machdep(struct thread *, uintptr_t, size_t, int, int);
+
+/* x86 memory protection keys (pkey_alloc(2) access rights) */
+#define LINUX_PKEY_DISABLE_ACCESS 0x1
+#define LINUX_PKEY_DISABLE_WRITE 0x2
+#define LINUX_PKEY_ACCESS_MASK (LINUX_PKEY_DISABLE_ACCESS | \
+ LINUX_PKEY_DISABLE_WRITE)
+#define LINUX_PKEY_MAX 16 /* keys 0..15; 0 is default */
+
int linux_madvise_common(struct thread *, uintptr_t, size_t, int);
#endif /* _LINUX_MMAP_H_ */
diff --git a/sys/compat/linux/linux_mmap.c b/sys/compat/linux/linux_mmap.c
--- a/sys/compat/linux/linux_mmap.c
+++ b/sys/compat/linux/linux_mmap.c
@@ -247,6 +247,44 @@
return (kern_mprotect(td, addr, len, prot, flags));
}
+/*
+ * x86 memory protection keys. The common entry points perform the
+ * parameter validation Linux applies regardless of hardware support,
+ * then defer to the machine-dependent back end.
+ */
+
+int
+linux_pkey_alloc_common(struct thread *td, uint64_t flags, uint64_t init_val)
+{
+
+ if (flags != 0)
+ return (EINVAL);
+ if ((init_val & ~(uint64_t)LINUX_PKEY_ACCESS_MASK) != 0)
+ return (EINVAL);
+ return (linux_pkey_alloc_machdep(td, init_val));
+}
+
+int
+linux_pkey_free_common(struct thread *td, int pkey)
+{
+
+ if (pkey < 0 || pkey >= LINUX_PKEY_MAX)
+ return (EINVAL);
+ return (linux_pkey_free_machdep(td, pkey));
+}
+
+int
+linux_pkey_mprotect_common(struct thread *td, uintptr_t addr, size_t len,
+ int prot, int pkey)
+{
+
+ if (pkey < -1 || pkey >= LINUX_PKEY_MAX)
+ return (EINVAL);
+ if (pkey == -1)
+ return (linux_mprotect_common(td, addr, len, prot));
+ return (linux_pkey_mprotect_machdep(td, addr, len, prot, pkey));
+}
+
/*
* Implement Linux madvise(MADV_DONTNEED), which has unusual semantics: for
* anonymous memory, pages in the range are immediately discarded.
diff --git a/sys/i386/linux/linux_emul_md.h b/sys/i386/linux/linux_emul_md.h
new file mode 100644
--- /dev/null
+++ b/sys/i386/linux/linux_emul_md.h
@@ -0,0 +1,18 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+#ifndef _I386_LINUX_EMUL_MD_H_
+#define _I386_LINUX_EMUL_MD_H_
+
+/*
+ * Machine-dependent part of the Linux process emuldata, embedded in
+ * struct linux_pemuldata as pem_md.
+ */
+struct linux_pemuldata_md {
+ int md_dummy; /* no machine-dependent state yet */
+};
+
+#endif /* !_I386_LINUX_EMUL_MD_H_ */
diff --git a/sys/i386/linux/linux_emul_md.c b/sys/i386/linux/linux_emul_md.c
new file mode 100644
--- /dev/null
+++ b/sys/i386/linux/linux_emul_md.c
@@ -0,0 +1,52 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+
+#include <compat/linux/linux_emul.h>
+#include <compat/linux/linux_mmap.h>
+
+/* No machine-dependent emuldata state yet. */
+
+void
+linux_pemuldata_init_md(struct thread *td, struct linux_pemuldata *pem)
+{
+}
+
+void
+linux_pemuldata_exec_md(struct linux_pemuldata *pem)
+{
+}
+
+/*
+ * Protection key back ends: behave as Linux does on hardware without
+ * protection keys. pkey_alloc() reports no free keys and only the
+ * default key semantics remain.
+ */
+
+int
+linux_pkey_alloc_machdep(struct thread *td, uint64_t init_val)
+{
+
+ return (ENOSPC);
+}
+
+int
+linux_pkey_free_machdep(struct thread *td, int pkey)
+{
+
+ return (EINVAL);
+}
+
+int
+linux_pkey_mprotect_machdep(struct thread *td, uintptr_t addr, size_t len,
+ int prot, int pkey)
+{
+
+ return (EINVAL);
+}
diff --git a/sys/modules/linux/Makefile b/sys/modules/linux/Makefile
--- a/sys/modules/linux/Makefile
+++ b/sys/modules/linux/Makefile
@@ -63,6 +63,7 @@
linux.c \
linux_dummy.c \
linux_emul.c \
+ linux_emul_md.c \
linux_errno.c \
linux_mib.c \
linux_mmap.c \
diff --git a/sys/modules/linux_common/Makefile b/sys/modules/linux_common/Makefile
--- a/sys/modules/linux_common/Makefile
+++ b/sys/modules/linux_common/Makefile
@@ -1,6 +1,9 @@
.PATH: ${SRCTOP}/sys/compat/linux
.if ${MACHINE_CPUARCH} == "amd64"
-.PATH: ${SRCTOP}/sys/x86/linux
+.PATH: ${SRCTOP}/sys/amd64/linux ${SRCTOP}/sys/x86/linux
+.endif
+.if ${MACHINE_CPUARCH} == "aarch64"
+.PATH: ${SRCTOP}/sys/arm64/linux
.endif
KMOD= linux_common
@@ -9,7 +12,10 @@
linux.c device_if.h vnode_if.h bus_if.h opt_inet6.h opt_inet.h
.if ${MACHINE_CPUARCH} == "amd64"
-SRCS+= linux_x86.c linux_vdso_selector_x86.c
+SRCS+= linux_pkru.c linux_x86.c linux_vdso_selector_x86.c
+.endif
+.if ${MACHINE_CPUARCH} == "aarch64"
+SRCS+= linux_emul_md.c
.endif
EXPORT_SYMS=
diff --git a/sys/x86/include/sysarch.h b/sys/x86/include/sysarch.h
--- a/sys/x86/include/sysarch.h
+++ b/sys/x86/include/sysarch.h
@@ -164,6 +164,7 @@
struct user_segment_descriptor *);
int amd64_get_ioperm(struct thread *, struct i386_ioperm_args *);
int amd64_set_ioperm(struct thread *, struct i386_ioperm_args *);
+int amd64_pkru_update(struct thread *, uintptr_t, size_t, u_int, int, bool);
#endif
#endif /* !_MACHINE_SYSARCH_H_ */

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 17, 9:14 PM (40 m, 10 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
36872599
Default Alt Text
D58782.id184025.diff (21 KB)

Event Timeline