Replicate the existing arm64 vDSO and modify it so that it can be built for the purecap ABI. Therefore, under PCuABI, vDSOs will be built for both aarch64 (COMPAT64) and a purecap.
Note that this change only implements the build of the purecap vDSO. Mapping of this vDSO by the kernel into user address space will be done in a subsequent patch.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- arch/arm64/Makefile | 6 + arch/arm64/include/asm/vdso/gettimeofday.h | 92 ++++++++++++++ arch/arm64/kernel/vdso-purecap/.gitignore | 2 + arch/arm64/kernel/vdso-purecap/Makefile | 101 ++++++++++++++++ arch/arm64/kernel/vdso-purecap/note.S | 23 ++++ arch/arm64/kernel/vdso-purecap/sigreturn.S | 80 ++++++++++++ arch/arm64/kernel/vdso-purecap/vdso.lds.S | 114 ++++++++++++++++++ .../arm64/kernel/vdso-purecap/vgettimeofday.c | 25 ++++ include/linux/compiler.h | 9 ++ include/linux/user_ptr.h | 1 + 10 files changed, 453 insertions(+) create mode 100644 arch/arm64/kernel/vdso-purecap/.gitignore create mode 100644 arch/arm64/kernel/vdso-purecap/Makefile create mode 100644 arch/arm64/kernel/vdso-purecap/note.S create mode 100644 arch/arm64/kernel/vdso-purecap/sigreturn.S create mode 100644 arch/arm64/kernel/vdso-purecap/vdso.lds.S create mode 100644 arch/arm64/kernel/vdso-purecap/vgettimeofday.c
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f5ac0cb0147e..c623b359bb44 100644 --- a/arch/arm64/Makefile +++ b/arch/arm64/Makefile @@ -179,6 +179,8 @@ vdso_install: $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso $@ $(if $(CONFIG_COMPAT_VDSO), \ $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 $@) + $(if $(CONFIG_CHERI_PURECAP_UABI), \ + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap $@)
archprepare: $(Q)$(MAKE) $(build)=arch/arm64/tools kapi @@ -204,6 +206,10 @@ prepare: vdso_prepare vdso_prepare: prepare0 $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso \ include/generated/vdso-offsets.h arch/arm64/kernel/vdso/vdso.so +ifdef CONFIG_CHERI_PURECAP_UABI + $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso-purecap \ + include/generated/vdso-purecap-offsets.h arch/arm64/kernel/vdso-purecap/vdso.so +endif ifdef CONFIG_COMPAT_VDSO $(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso32 \ include/generated/vdso32-offsets.h arch/arm64/kernel/vdso32/vdso.so diff --git a/arch/arm64/include/asm/vdso/gettimeofday.h b/arch/arm64/include/asm/vdso/gettimeofday.h index 764d13e2916c..fd7dfea6eb74 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -14,6 +14,66 @@
#define VDSO_HAS_CLOCK_GETRES 1
+// Purecap Assembly Fallbacks +#if defined(PURECAP_VDSO) +static __always_inline +int gettimeofday_fallback(struct __kernel_old_timeval *_tv, + struct timezone *_tz) +{ + register struct timezone *tz asm("c1") = _tz; + register struct __kernel_old_timeval *tv asm("c0") = _tv; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_gettimeofday; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (tv), "C" (tz), "r" (nr) + : "memory"); + + return ret; + //return 0; +} + +static __always_inline +long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{ + register struct __kernel_timespec *ts asm("c1") = _ts; + register clockid_t clkid asm("c0") = _clkid; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_clock_gettime; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (clkid), "C" (ts), "r" (nr) + : "memory"); + + return ret; + + //return 0; +} + +static __always_inline +int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) +{ + register struct __kernel_timespec *ts asm("c1") = _ts; + register clockid_t clkid asm("c0") = _clkid; + register long ret asm ("x0"); + register long nr asm("x8") = __NR_clock_getres; + + asm volatile( + " svc #0\n" + : "=r" (ret) + : "C" (clkid), "C" (ts), "r" (nr) + : "memory"); + + return ret; + //return 0; +} + +// Regular Aarch64 Assembly Fallbacks +#else static __always_inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz) @@ -66,6 +126,8 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) return ret; }
+#endif /* PURECAP_VDSO */ + static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd) { @@ -99,6 +161,34 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return res; }
+#if defined(PURECAP_VDSO) +static __always_inline +const struct vdso_data *__arch_get_vdso_data(void) +{ + const struct vdso_data *vd; + asm(".weak _vdso_data\n\t" + ".hidden vdso_data\n\t" + "adrp %0, _vdso_data\n\t" + "add %0, %0, #:lo12:_vdso_data" + : "=C"(vd)); + return vd; +} + +#ifdef CONFIG_TIME_NS +static __always_inline +const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) +{ + const struct vdso_data *td; + asm(".weak _timens_data\n\t" + ".hidden _timens_data\n\t" + "adrp %0, _timens_data\n\t" + "add %0, %0, #:lo12:_timens_data" + : "=C"(td)); + return vd; +} +#endif +#else + static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { @@ -113,6 +203,8 @@ const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) } #endif
+#endif + #endif /* !__ASSEMBLY__ */
#endif /* __ASM_VDSO_GETTIMEOFDAY_H */ diff --git a/arch/arm64/kernel/vdso-purecap/.gitignore b/arch/arm64/kernel/vdso-purecap/.gitignore new file mode 100644 index 000000000000..652e31d82582 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/.gitignore @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +vdso.lds diff --git a/arch/arm64/kernel/vdso-purecap/Makefile b/arch/arm64/kernel/vdso-purecap/Makefile new file mode 100644 index 000000000000..99214eac9881 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/Makefile @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Building a vDSO image for Purecap Morello under PCuABI. +# +# Based on the standard AArch64 Makefile. +# + +# Include the generic Makefile to check the built vdso. +include $(srctree)/lib/vdso/Makefile + +obj-vdso := vgettimeofday.o note.o sigreturn.o + +# Build rules +targets := $(obj-vdso) vdso.so vdso.so.dbg +obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) + +btildflags-$(CONFIG_ARM64_BTI_KERNEL) += -z force-bti + +# -Bsymbolic has been added for consistency with arm, the compat vDSO and +# potential future proofing if we end up with internal calls to the exported +# routines, as x86 does (see 6f121e548f83 ("x86, vdso: Reimplement vdso.so +# preparation in build-time C")). +ldflags-y := -shared -soname=linux-vdso.so.1 --hash-style=sysv \ + -Bsymbolic --build-id=sha1 -n $(btildflags-y) + +ifdef CONFIG_LD_ORPHAN_WARN + ldflags-y += --orphan-handling=$(CONFIG_LD_ORPHAN_WARN_LEVEL) +endif + +ldflags-y += -T + +# Flags set by the arm64 Makefile that should be removed as we are building +# for purecap here +REMOVE_FLAGS := --target=aarch64-linux-gnu -mgeneral-regs-only + +# Flags required for a purecap build. Will be passed to CC, AS, and LD. +PURECAP_FLAGS := --target=aarch64-linux-musl_purecap -mabi=purecap \ +--sysroot=$(MUSL_HOME) + +ccflags-remove-y := $(REMOVE_FLAGS) +asflags-remove-y := $(REMOVE_FLAGS) +asflags-y := $(PURECAP_FLAGS) +ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 +ccflags-y += $(PURECAP_FLAGS) -DPURECAP_VDSO +ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO + +# -Wmissing-prototypes and -Wmissing-declarations are removed from +# the CFLAGS of vgettimeofday.c to make possible to build the +# kernel with CONFIG_WERROR enabled. +CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ + $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \ + $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ + -Wmissing-prototypes -Wmissing-declarations +KASAN_SANITIZE := n +KCSAN_SANITIZE := n +UBSAN_SANITIZE := n +OBJECT_FILES_NON_STANDARD := y +KCOV_INSTRUMENT := n + +CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny -fasynchronous-unwind-tables + +ifneq ($(c-gettimeofday-y),) + CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y) +endif + +# Disable gcov profiling for VDSO code +GCOV_PROFILE := n + +targets += vdso.lds +CPPFLAGS_vdso.lds += -P -C -U$(ARCH) + +# Link rule for the .so file, .lds has to be first +$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE + $(call if_changed,vdsold_and_vdso_check) + +# Strip rule for the .so file +$(obj)/%.so: OBJCOPYFLAGS := -S +$(obj)/%.so: $(obj)/%.so.dbg FORCE + $(call if_changed,objcopy) + +# Generate VDSO offsets using helper script (borrowed from the 64-bit vDSO) +gen-vdsosym := $(srctree)/$(src)/../vdso/gen_vdso_offsets.sh +quiet_cmd_vdsosym = VDSOSYM $@ + cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ + +include/generated/vdso-purecap-offsets.h: $(obj)/vdso.so.dbg FORCE + $(call if_changed,vdsosym) + +# Actual build commands +quiet_cmd_vdsold_and_vdso_check = LD $@ + cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check) + +# Install commands for the unstripped file +quiet_cmd_vdso_install = INSTALL $@ + cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + +vdso.so: $(obj)/vdso.so.dbg + @mkdir -p $(MODLIB)/vdso + $(call cmd,vdso_install) + +vdso_install: vdso.so diff --git a/arch/arm64/kernel/vdso-purecap/note.S b/arch/arm64/kernel/vdso-purecap/note.S new file mode 100644 index 000000000000..4ea1fbbc6a39 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/note.S @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 ARM Limited + * + * Author: Will Deacon will.deacon@arm.com + * + * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text. + * Here we can supply some information useful to userland. + */ + +#include <linux/uts.h> +#include <linux/version.h> +#include <linux/elfnote.h> +#include <linux/build-salt.h> +#include <asm/assembler.h> + +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END + +BUILD_SALT + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso-purecap/sigreturn.S b/arch/arm64/kernel/vdso-purecap/sigreturn.S new file mode 100644 index 000000000000..04ce8a87fd75 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/sigreturn.S @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Sigreturn trampoline for returning from a signal when the SA_RESTORER + * flag is not set. It serves primarily as a hall of shame for crappy + * unwinders and features an exciting but mysterious NOP instruction. + * + * It's also fragile as hell, so please think twice before changing anything + * in here. + * + * Copyright (C) 2023 ARM Limited + * + * Author: Will Deacon will.deacon@arm.com + */ + +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/unistd.h> + + .text + +/* + * NOTE!!! You may notice that all of the .cfi directives in this file have + * been commented out. This is because they have been shown to trigger segfaults + * in libgcc when unwinding out of a SIGCANCEL handler to invoke pthread + * cleanup handlers during the thread cancellation dance. By omitting the + * directives, we trigger an arm64-specific fallback path in the unwinder which + * recognises the signal frame and restores many of the registers directly from + * the sigcontext. Re-enabling the cfi directives here therefore needs to be + * much more comprehensive to reduce the risk of further regressions. + */ + +/* Ensure that the mysterious NOP can be associated with a function. */ +// .cfi_startproc purecap + +/* + * .cfi_signal_frame causes the corresponding Frame Description Entry (FDE) in + * the .eh_frame section to be annotated as a signal frame. This allows DWARF + * unwinders (e.g. libstdc++) to implement _Unwind_GetIPInfo() and identify + * the next frame using the unmodified return address instead of subtracting 1, + * which may yield the wrong FDE. + */ +// .cfi_signal_frame + +/* + * Tell the unwinder where to locate the frame record linking back to the + * interrupted context. We don't provide unwind info for registers other than + * the frame pointer and the link register here; in practice, this is likely to + * be insufficient for unwinding in C/C++ based runtimes, especially without a + * means to restore the stack pointer. Thankfully, unwinders and debuggers + * already have baked-in strategies for attempting to unwind out of signals. + */ +// .cfi_def_cfa x29, 0 +// .cfi_offset x29, 0 * 8 +// .cfi_offset x30, 1 * 8 + +/* + * This mysterious NOP is required for some unwinders (e.g. libc++) that + * unconditionally subtract one from the result of _Unwind_GetIP() in order to + * identify the calling function. + * Hack borrowed from arch/powerpc/kernel/vdso64/sigtramp.S. + */ + nop // Mysterious NOP + +/* + * GDB, libgcc and libunwind rely on being able to identify the sigreturn + * instruction sequence to unwind from signal handlers. We cannot, therefore, + * use SYM_FUNC_START() here, as it will emit a BTI C instruction and break the + * unwinder. Thankfully, this function is only ever called from a RET and so + * omitting the landing pad is perfectly fine. + */ +SYM_CODE_START(__kernel_rt_sigreturn) +// PLEASE DO NOT MODIFY + mov x8, #__NR_rt_sigreturn +// PLEASE DO NOT MODIFY + svc #0 +// PLEASE DO NOT MODIFY +// .cfi_endproc +SYM_CODE_END(__kernel_rt_sigreturn) + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso-purecap/vdso.lds.S b/arch/arm64/kernel/vdso-purecap/vdso.lds.S new file mode 100644 index 000000000000..5405c79e6170 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/vdso.lds.S @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * GNU linker script for the VDSO library. +* + * Copyright (C) 2023 ARM Limited + * + * Author: Will Deacon will.deacon@arm.com + * Heavily based on the vDSO linker scripts for other archs. + */ + +#include <linux/const.h> +#include <asm/page.h> +#include <asm/vdso.h> +#include <asm-generic/vmlinux.lds.h> + +OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64") +OUTPUT_ARCH(aarch64) + +SECTIONS +{ + PROVIDE(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE); +#ifdef CONFIG_TIME_NS + PROVIDE(_timens_data = _vdso_data + PAGE_SIZE); +#endif + . = VDSO_LBASE + SIZEOF_HEADERS; + + .hash : { *(.hash) } :text + .gnu.hash : { *(.gnu.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .gnu.version : { *(.gnu.version) } + .gnu.version_d : { *(.gnu.version_d) } + .gnu.version_r : { *(.gnu.version_r) } + + /* + * Discard .note.gnu.property sections which are unused and have + * different alignment requirement from vDSO note sections. + */ + /DISCARD/ : { + *(.note.GNU-stack .note.gnu.property) + } + .note : { *(.note.*) } :text :note + + . = ALIGN(16); + + .text : { *(.text*) } :text =0xd503201f + PROVIDE (__etext = .); + PROVIDE (_etext = .); + PROVIDE (etext = .); + + . = ALIGN(4); + .altinstructions : { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + + .dynamic : { *(.dynamic) } :text :dynamic + + .rela.dyn : ALIGN(8) { *(.rela .rela*) } + + .rodata : { + *(.rodata*) + *(.got) + *(.got.plt) + *(.plt) + *(.plt.*) + *(.iplt) + *(.igot .igot.plt) + } :text + + _end = .; + PROVIDE(end = .); + + DWARF_DEBUG + ELF_DETAILS + + /DISCARD/ : { + *(.data .data.* .gnu.linkonce.d.* .sdata*) + *(.bss .sbss .dynbss .dynsbss) + *(.eh_frame .eh_frame_hdr) + } +} + +/* + * We must supply the ELF program headers explicitly to get just one + * PT_LOAD segment, and set the flags explicitly to make segments read-only. + */ +PHDRS +{ + text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ + dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ + note PT_NOTE FLAGS(4); /* PF_R */ +} + +/* + * This controls what symbols we export from the DSO. + */ +VERSION +{ + LINUX_2.6.39 { + global: + __kernel_rt_sigreturn; + __kernel_gettimeofday; + __kernel_clock_gettime; + __kernel_clock_getres; + local: *; + }; +} + +/* + * Make the sigreturn code visible to the kernel. + */ +VDSO_sigtramp = __kernel_rt_sigreturn; diff --git a/arch/arm64/kernel/vdso-purecap/vgettimeofday.c b/arch/arm64/kernel/vdso-purecap/vgettimeofday.c new file mode 100644 index 000000000000..e9e462be74ee --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap/vgettimeofday.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ARM64 userspace implementations of gettimeofday() and similar. + * + * Copyright (C) 2023 ARM Limited + * + */ + +int __kernel_clock_gettime(clockid_t clock, + struct __kernel_timespec *ts) +{ + return __cvdso_clock_gettime(clock, ts); +} + +int __kernel_gettimeofday(struct __kernel_old_timeval *tv, + struct timezone *tz) +{ + return __cvdso_gettimeofday(tv, tz); +} + +int __kernel_clock_getres(clockid_t clock_id, + struct __kernel_timespec *res) +{ + return __cvdso_clock_getres(clock_id, res); +} diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..56e752138ef7 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -4,6 +4,11 @@
#include <linux/compiler_types.h>
+#if defined(PURECAP_VDSO) +#include <linux/user_ptr.h> +#include <linux/cheri.h> +#endif + #ifndef __ASSEMBLY__
#ifdef __KERNEL__ @@ -222,7 +227,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ static inline void *offset_to_ptr(const int *off) { +#if defined(PURECAP_VDSO) + return uaddr_to_user_ptr((unsigned long)off + *off); +#else return (void *)((unsigned long)off + *off); +#endif }
#endif /* __ASSEMBLY__ */ diff --git a/include/linux/user_ptr.h b/include/linux/user_ptr.h index 2c2180f0f0c3..21bfe5075141 100644 --- a/include/linux/user_ptr.h +++ b/include/linux/user_ptr.h @@ -3,6 +3,7 @@ #define _LINUX_USER_PTR_H
#include <linux/typecheck.h> +#include <linux/types.h>
/** * as_user_ptr() - Convert an arbitrary integer value to a user pointer.