diff options
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/amd64/amd64/sys_machdep.c | 85 | ||||
| -rw-r--r-- | sys/amd64/linux/linux_emul_md.h | 35 | ||||
| -rw-r--r-- | sys/amd64/linux/linux_pkru.c | 226 | ||||
| -rw-r--r-- | sys/amd64/linux/linux_sysvec.c | 4 | ||||
| -rw-r--r-- | sys/amd64/linux32/linux32_sysvec.c | 4 | ||||
| -rw-r--r-- | sys/arm64/linux/linux_emul_md.c | 52 | ||||
| -rw-r--r-- | sys/arm64/linux/linux_emul_md.h | 18 | ||||
| -rw-r--r-- | sys/compat/linux/linux_dummy.c | 3 | ||||
| -rw-r--r-- | sys/compat/linux/linux_emul.c | 2 | ||||
| -rw-r--r-- | sys/compat/linux/linux_emul.h | 12 | ||||
| -rw-r--r-- | sys/compat/linux/linux_misc.c | 22 | ||||
| -rw-r--r-- | sys/compat/linux/linux_mmap.c | 38 | ||||
| -rw-r--r-- | sys/compat/linux/linux_mmap.h | 21 | ||||
| -rw-r--r-- | sys/i386/linux/linux_emul_md.c | 52 | ||||
| -rw-r--r-- | sys/i386/linux/linux_emul_md.h | 18 | ||||
| -rw-r--r-- | sys/modules/linux/Makefile | 1 | ||||
| -rw-r--r-- | sys/modules/linux_common/Makefile | 10 | ||||
| -rw-r--r-- | sys/x86/include/sysarch.h | 1 |
18 files changed, 554 insertions, 50 deletions
diff --git a/sys/amd64/amd64/sys_machdep.c b/sys/amd64/amd64/sys_machdep.c index 1df73a25c05e..2c6387d2a0d2 100644 --- a/sys/amd64/amd64/sys_machdep.c +++ b/sys/amd64/amd64/sys_machdep.c @@ -167,11 +167,44 @@ update_gdt_fsbase(struct thread *td, uint32_t base) 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 @@ sysarch(struct thread *td, struct sysarch_args *uap) 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 index 000000000000..a5ea9c20e20a --- /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 index 000000000000..159f8492ff35 --- /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 index 890cf01c46a0..ecb497c61a1a 100644 --- 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 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp, * 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 index 735ebb151017..c4de547b2bf5 100644 --- 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 @@ linux_exec_setregs(struct thread *td, struct image_params *imgp, 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.c b/sys/arm64/linux/linux_emul_md.c new file mode 100644 index 000000000000..9dd507ad4f49 --- /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/arm64/linux/linux_emul_md.h b/sys/arm64/linux/linux_emul_md.h new file mode 100644 index 000000000000..27424f600f5e --- /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/compat/linux/linux_dummy.c b/sys/compat/linux/linux_dummy.c index 971e070e90aa..dfb1c90e9abf 100644 --- a/sys/compat/linux/linux_dummy.c +++ b/sys/compat/linux/linux_dummy.c @@ -110,9 +110,6 @@ DUMMY(mlock2); 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.c b/sys/compat/linux/linux_emul.c index e5ab51802468..8ee17e3e484b 100644 --- a/sys/compat/linux/linux_emul.c +++ b/sys/compat/linux/linux_emul.c @@ -157,6 +157,7 @@ linux_proc_init(struct thread *td, struct thread *newtd, bool init_thread) 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 @@ linux_proc_init(struct thread *td, struct thread *newtd, bool init_thread) 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_emul.h b/sys/compat/linux/linux_emul.h index 6dfa31f6edf7..685cbcaa45b4 100644 --- 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 @@ struct linux_pemuldata { 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 { 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_misc.c b/sys/compat/linux/linux_misc.c index 3aecc0106aaa..96c0ab81beef 100644 --- a/sys/compat/linux/linux_misc.c +++ b/sys/compat/linux/linux_misc.c @@ -360,6 +360,28 @@ linux_mprotect(struct thread *td, struct linux_mprotect_args *uap) } 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.c b/sys/compat/linux/linux_mmap.c index 9fecb6ebb2ad..4fe0df4d5d07 100644 --- a/sys/compat/linux/linux_mmap.c +++ b/sys/compat/linux/linux_mmap.c @@ -248,6 +248,44 @@ linux_mprotect_common(struct thread *td, uintptr_t addr, size_t len, int prot) } /* + * 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/compat/linux/linux_mmap.h b/sys/compat/linux/linux_mmap.h index 043dec9d40b7..75af79ae1316 100644 --- 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/i386/linux/linux_emul_md.c b/sys/i386/linux/linux_emul_md.c new file mode 100644 index 000000000000..9dd507ad4f49 --- /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/i386/linux/linux_emul_md.h b/sys/i386/linux/linux_emul_md.h new file mode 100644 index 000000000000..ddf5cf183acd --- /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/modules/linux/Makefile b/sys/modules/linux/Makefile index 8904e8005416..69b2af142712 100644 --- a/sys/modules/linux/Makefile +++ b/sys/modules/linux/Makefile @@ -63,6 +63,7 @@ SRCS+= imgact_linux.c \ 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 index 1fa740b8b823..280d8f8d3ec3 100644 --- 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 @@ SRCS= linux_common.c linux_mib.c linux_mmap.c linux_util.c linux_emul.c \ 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 index cf7ee9ee5518..9a8c95d9ae75 100644 --- a/sys/x86/include/sysarch.h +++ b/sys/x86/include/sysarch.h @@ -164,6 +164,7 @@ int amd64_set_ldt(struct thread *, struct i386_ldt_args *, 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_ */ |
