On 12/10/2023 12:36, Aditya Deshpande wrote:
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 ++++
AFAICT (diffing the directories), vdso-purecap/ is essentially the same as vdso/, with the Makefile tweaked. We should avoid creating so much duplication, especially since it makes it hard to keep the purecap vDSO in sync when rebasing. Since even the Makefile is mostly the same in both cases, it seems that the right thing to do is to make vdso/Makefile generate both the standard arm64 vDSO and the purecap vDSO (if CONFIG_CHERI_PURECAP_UABI is selected). That will probably require some refactoring of the Makefile, in order to build each file twice (with different flags and output directory).
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;
+}
We could avoid duplicating the entire helpers by defining macros that expand to "c0"/"x0", "c1"/"x1", "C"/"r" depending on __CHERI_PURE_CAPABILITY__ .
+// 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"
I suspect we don't need this directive, as the symbols are always defined (unlike __rela_dyn_{start,end} in the self-relocation case).
".hidden vdso_data\n\t"
"adrp %0, _vdso_data\n\t"
"add %0, %0, #:lo12:_vdso_data"
That would deserve a comment, as it's not entirely obvious why we need to go through these hoops in the purecap case.
: "=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;
That was clearly meant to be td, not vd. That makes me wonder if that path is being exercised at all?
+} +#endif +#else
It's good practice to annotate #else and #endif with /* CONFIG_... */, especially when they are nested. Also regarding empty lines, it is acceptable to either add them or not, but we should do it consistently. In other words, either:
#if ... [code] #else [code] #endif
or:
#if ...
[code]
#else [code]
#endif
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)
In principle, the vDSO should be entirely self-contained, without any dependency on other libraries. Is there an actual issue with inheriting the standard --target=aarch64-linux-gnu, without specifying a sysroot?
+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/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);
I doubt this function is ever used in the vDSO, but the nicest thing we can do is to make it purecap-friendly. Fortunately it's actually very simple: just replace unsigned long with uintptr_t. No need for any #ifdef'ing, and this way the additional #include's disappear too. (Note that no user pointer - in the sense of void __user * - is involved here, when built as part of the vDSO all pointers are capabilities so code in included headers needs to be compatible with that.)
Kevin
+#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.