Replace getauxval() with getauxptr() when building the tests for purecap. By building the vDSO selftests as purecap binaries, we can test the purecap vDSO.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- tools/testing/selftests/vDSO/parse_vdso.h | 16 ++++++++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 5 +++-- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 ++---- 3 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/vDSO/parse_vdso.h b/tools/testing/selftests/vDSO/parse_vdso.h index de0453067d7c..58f3df88160d 100644 --- a/tools/testing/selftests/vDSO/parse_vdso.h +++ b/tools/testing/selftests/vDSO/parse_vdso.h @@ -4,6 +4,7 @@ #define PARSE_VDSO_H
#include <stdint.h> +#include <stdlib.h>
/* * To use this vDSO parser, first call one of the vdso_init_* functions. @@ -28,4 +29,19 @@ void *vdso_sym(const char *version, const char *name); void vdso_init_from_sysinfo_ehdr(uintptr_t base); void vdso_init_from_auxv(void *auxv);
+/* + * Under PCuABI, pointers in the auxiliary vector can no longer be represented + * as unsigned long as they are now capabilities. Get the capability to the + * vDSO using getauxptr() instead, which returns a a capability instead of + * unsigned long. + */ +static inline uintptr_t get_sysinfo_ehdr() +{ +#ifdef __CHERI_PURE_CAPABILITY__ + return getauxptr(AT_SYSINFO_EHDR); +#else + return (uintptr_t)getauxval(AT_SYSINFO_EHDR); +#endif +} + #endif diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 883ca85424bc..091efeaa3172 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -20,6 +20,7 @@
#include "../kselftest.h" #include "vdso_config.h" +#include "parse_vdso.h"
extern void *vdso_sym(const char *version, const char *name); extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); @@ -175,7 +176,7 @@ static inline void vdso_test_clock(clockid_t clock_id)
int main(int argc, char **argv) { - unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + uintptr_t sysinfo_ehdr = get_sysinfo_ehdr();
ksft_print_header(); ksft_set_plan(VDSO_TEST_PLAN); @@ -190,7 +191,7 @@ int main(int argc, char **argv)
printf("[vDSO kselftest] VDSO_VERSION: %s\n", version);
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); + vdso_init_from_sysinfo_ehdr(sysinfo_ehdr);
vdso_test_gettimeofday();
diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index e411f287a426..359f881ac574 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -37,14 +37,12 @@ const char *name = "__vdso_gettimeofday";
int main(int argc, char **argv) { - unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); + uintptr_t sysinfo_ehdr = get_sysinfo_ehdr(); if (!sysinfo_ehdr) { printf("AT_SYSINFO_EHDR is not present!\n"); return KSFT_SKIP; } - - vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); - + vdso_init_from_sysinfo_ehdr(sysinfo_ehdr); /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); gtod_t gtod = (gtod_t)vdso_sym(version, name);
Modify the vDSO Makefile such that it can also build a vDSO for the purecap ABI and change the fallbacks to use capability registers when running in purecap. 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 | 60 +++++++++++++++++++--- arch/arm64/kernel/vdso/Makefile | 41 ++++++++++++--- include/linux/compiler.h | 3 +- include/linux/types.h | 4 ++ include/linux/user_ptr.h | 1 + 6 files changed, 99 insertions(+), 16 deletions(-)
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f5ac0cb0147e..41e7a415ec08 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_PURECAP_VDSO=y $(build)=arch/arm64/kernel/vdso $@)
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_PURECAP_VDSO=y $(build)=arch/arm64/kernel/vdso \ + 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..0ff23704c5c6 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -14,19 +14,35 @@
#define VDSO_HAS_CLOCK_GETRES 1
+// // Stringizing macros +#define xstr(s) str(s) +#define str(s) #s + +/* Inline Assembly Macros for Purecap + * PTR_REG(n) will expand to "cn" under purecap, and "xn" under non-purecap + * PTR_REG_OP will expand to "C" under purecap and "r" under non-purecap. + */ +#if defined(__CHERI_PURE_CAPABILITY__) +#define PTR_REG(n) "c" xstr(n) +#define PTR_REG_OP "C" +#else +#define PTR_REG(n) "x" xstr(n) +#define PTR_REG_OP "r" +#endif /* __CHERI_PURE_CAPABILITY__ */ + static __always_inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz) { - register struct timezone *tz asm("x1") = _tz; - register struct __kernel_old_timeval *tv asm("x0") = _tv; + register struct timezone *tz asm(PTR_REG(1)) = _tz; + register struct __kernel_old_timeval *tv asm(PTR_REG(0)) = _tv; register long ret asm ("x0"); register long nr asm("x8") = __NR_gettimeofday;
asm volatile( " svc #0\n" : "=r" (ret) - : "r" (tv), "r" (tz), "r" (nr) + : PTR_REG_OP (tv), PTR_REG_OP (tz), "r" (nr) : "memory");
return ret; @@ -35,7 +51,7 @@ int gettimeofday_fallback(struct __kernel_old_timeval *_tv, static __always_inline long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) { - register struct __kernel_timespec *ts asm("x1") = _ts; + register struct __kernel_timespec *ts asm(PTR_REG(1)) = _ts; register clockid_t clkid asm("x0") = _clkid; register long ret asm ("x0"); register long nr asm("x8") = __NR_clock_gettime; @@ -43,7 +59,7 @@ long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) asm volatile( " svc #0\n" : "=r" (ret) - : "r" (clkid), "r" (ts), "r" (nr) + : "r" (clkid), PTR_REG_OP (ts), "r" (nr) : "memory");
return ret; @@ -52,7 +68,7 @@ long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) static __always_inline int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) { - register struct __kernel_timespec *ts asm("x1") = _ts; + register struct __kernel_timespec *ts asm(PTR_REG(1)) = _ts; register clockid_t clkid asm("x0") = _clkid; register long ret asm ("x0"); register long nr asm("x8") = __NR_clock_getres; @@ -60,7 +76,7 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) asm volatile( " svc #0\n" : "=r" (ret) - : "r" (clkid), "r" (ts), "r" (nr) + : "r" (clkid), PTR_REG_OP (ts), "r" (nr) : "memory");
return ret; @@ -99,6 +115,32 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return res; }
+#if defined(__CHERI_PURE_CAPABILITY__) +static __always_inline +const struct vdso_data *__arch_get_vdso_data(void) +{ + const struct vdso_data *vd; + asm(".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(".hidden _timens_data\n\t" + "adrp %0, _timens_data\n\t" + "add %0, %0, #:lo12:_timens_data" + : "=C"(td)); + return td; +} +#endif /* CONFIG_TIME_NS */ + +#else /* !__CHERI_PURE_CAPABILITY__ */ static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { @@ -111,7 +153,9 @@ const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) { return _timens_data; } -#endif +#endif /* CONFIG_TIME_NS */ + +#endif /* __CHERI_PURE_CAPABILITY__ */
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index fe7a53c6781f..07705ec1304d 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -33,6 +33,26 @@ ldflags-y += -T ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO
+# When building for PCuABI, this Makefile will be called twice: first to build +# the regular arm64 vDSO, and second to build the purecap vDSO. We must change +# certain variables/flags depending on which vDSO we are building. +ifdef BUILD_PURECAP_VDSO + PURECAP_FLAGS := -mabi=purecap + REMOVE_FLAGS := -mgeneral-regs-only + ccflags-y += $(PURECAP_FLAGS) + asflags-y := $(PURECAP_FLAGS) + ccflags-remove-y := $(REMOVE_FLAGS) + asflags-remove-y := $(REMOVE_FLAGS) + +# We want the purecap vDSO in its own directory (kernel/vdso-purecap) + output-dir := $(obj)-purecap +# Put the offsets for the purecap vDSO into their own header file + offsets := include/generated/vdso-purecap-offsets.h +else + output-dir := $(obj) + offsets := include/generated/vdso-offsets.h +endif + # -Wmissing-prototypes and -Wmissing-declarations are removed from # the CFLAGS of vgettimeofday.c to make possible to build the # kernel with CONFIG_WERROR enabled. @@ -58,13 +78,20 @@ GCOV_PROFILE := n targets += vdso.lds CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
+# Make the vdso-purecap directory +purecap_mkdir: +ifdef BUILD_PURECAP_VDSO + @mkdir -p $(output-dir) +endif + # Link rule for the .so file, .lds has to be first -$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE +$(output-dir)/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 +$(output-dir)/%.so: purecap_mkdir +$(output-dir)/%.so: OBJCOPYFLAGS := -S +$(output-dir)/%.so: $(output-dir)/%.so.dbg FORCE $(call if_changed,objcopy)
# Generate VDSO offsets using helper script @@ -72,7 +99,7 @@ gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh quiet_cmd_vdsosym = VDSOSYM $@ cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
-include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE +$(offsets): $(output-dir)/vdso.so.dbg FORCE $(call if_changed,vdsosym)
# Actual build commands @@ -81,10 +108,10 @@ quiet_cmd_vdsold_and_vdso_check = LD $@
# Install commands for the unstripped file quiet_cmd_vdso_install = INSTALL $@ - cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ + cmd_vdso_install = cp $(output-dir)/$@.dbg $(MODLIB)/vdso/$@
-vdso.so: $(obj)/vdso.so.dbg +vdso.so: $(output-dir)/vdso.so.dbg @mkdir -p $(MODLIB)/vdso $(call cmd,vdso_install)
-vdso_install: vdso.so +vdso_install: purecap_mkdir vdso.so diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..7864350d0edb 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -3,6 +3,7 @@ #define __LINUX_COMPILER_H
#include <linux/compiler_types.h> +#include <linux/types.h>
#ifndef __ASSEMBLY__
@@ -222,7 +223,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ static inline void *offset_to_ptr(const int *off) { - return (void *)((unsigned long)off + *off); + return (void *)((uintptr_t)off + *off); }
#endif /* __ASSEMBLY__ */ diff --git a/include/linux/types.h b/include/linux/types.h index 897c134c1e8a..aa661f26fb6c 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -46,7 +46,11 @@ typedef __kernel_gid32_t gid_t; typedef __kernel_uid16_t uid16_t; typedef __kernel_gid16_t gid16_t;
+#ifdef __CHERI_PURE_CAPABILITY__ +typedef __uintcap_t uintptr_t; +#else typedef unsigned long uintptr_t; +#endif
#ifdef CONFIG_CHERI_PURECAP_UABI typedef __uintcap_t user_uintptr_t; 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.
On 16/11/2023 04:52, Aditya Deshpande wrote:
Modify the vDSO Makefile such that it can also build a vDSO for the purecap ABI and change the fallbacks to use capability registers when running in purecap. 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 | 60 +++++++++++++++++++--- arch/arm64/kernel/vdso/Makefile | 41 ++++++++++++--- include/linux/compiler.h | 3 +- include/linux/types.h | 4 ++ include/linux/user_ptr.h | 1 + 6 files changed, 99 insertions(+), 16 deletions(-)
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile index f5ac0cb0147e..41e7a415ec08 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_PURECAP_VDSO=y $(build)=arch/arm64/kernel/vdso $@)
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_PURECAP_VDSO=y $(build)=arch/arm64/kernel/vdso \
- include/generated/vdso-purecap-offsets.h arch/arm64/kernel/vdso-purecap/vdso.so
+endif
Nit: should be added in the same order as for vdso_install above (i.e. either before the compat32 one or after).
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..0ff23704c5c6 100644 --- a/arch/arm64/include/asm/vdso/gettimeofday.h +++ b/arch/arm64/include/asm/vdso/gettimeofday.h @@ -14,19 +14,35 @@ #define VDSO_HAS_CLOCK_GETRES 1 +// // Stringizing macros +#define xstr(s) str(s) +#define str(s) #s
Since the kernel is stringifying things all around, such macros already exist, and very conveniently they are in their own standalone header: <linux/stringify.h>
+/* Inline Assembly Macros for Purecap
Nit: for multi-line comments, the first line is normally empty, that is:
/* * Inline ... * ... */
- PTR_REG(n) will expand to "cn" under purecap, and "xn" under non-purecap
- PTR_REG_OP will expand to "C" under purecap and "r" under non-purecap.
- */
+#if defined(__CHERI_PURE_CAPABILITY__) +#define PTR_REG(n) "c" xstr(n) +#define PTR_REG_OP "C" +#else +#define PTR_REG(n) "x" xstr(n) +#define PTR_REG_OP "r" +#endif /* __CHERI_PURE_CAPABILITY__ */
static __always_inline int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz) {
- register struct timezone *tz asm("x1") = _tz;
- register struct __kernel_old_timeval *tv asm("x0") = _tv;
- register struct timezone *tz asm(PTR_REG(1)) = _tz;
- register struct __kernel_old_timeval *tv asm(PTR_REG(0)) = _tv; register long ret asm ("x0"); register long nr asm("x8") = __NR_gettimeofday;
asm volatile( " svc #0\n" : "=r" (ret)
- : "r" (tv), "r" (tz), "r" (nr)
- : PTR_REG_OP (tv), PTR_REG_OP (tz), "r" (nr) : "memory");
return ret; @@ -35,7 +51,7 @@ int gettimeofday_fallback(struct __kernel_old_timeval *_tv, static __always_inline long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) {
- register struct __kernel_timespec *ts asm("x1") = _ts;
- register struct __kernel_timespec *ts asm(PTR_REG(1)) = _ts; register clockid_t clkid asm("x0") = _clkid; register long ret asm ("x0"); register long nr asm("x8") = __NR_clock_gettime;
@@ -43,7 +59,7 @@ long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) asm volatile( " svc #0\n" : "=r" (ret)
- : "r" (clkid), "r" (ts), "r" (nr)
- : "r" (clkid), PTR_REG_OP (ts), "r" (nr) : "memory");
return ret; @@ -52,7 +68,7 @@ long clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) static __always_inline int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) {
- register struct __kernel_timespec *ts asm("x1") = _ts;
- register struct __kernel_timespec *ts asm(PTR_REG(1)) = _ts; register clockid_t clkid asm("x0") = _clkid; register long ret asm ("x0"); register long nr asm("x8") = __NR_clock_getres;
@@ -60,7 +76,7 @@ int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts) asm volatile( " svc #0\n" : "=r" (ret)
- : "r" (clkid), "r" (ts), "r" (nr)
- : "r" (clkid), PTR_REG_OP (ts), "r" (nr) : "memory");
return ret; @@ -99,6 +115,32 @@ static __always_inline u64 __arch_get_hw_counter(s32 clock_mode, return res; } +#if defined(__CHERI_PURE_CAPABILITY__) +static __always_inline +const struct vdso_data *__arch_get_vdso_data(void) +{
- const struct vdso_data *vd;
- asm(".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(".hidden _timens_data\n\t"
"adrp %0, _timens_data\n\t"
"add %0, %0, #:lo12:_timens_data"
: "=C"(td));
- return td;
+} +#endif /* CONFIG_TIME_NS */
+#else /* !__CHERI_PURE_CAPABILITY__ */ static __always_inline const struct vdso_data *__arch_get_vdso_data(void) { @@ -111,7 +153,9 @@ const struct vdso_data *__arch_get_timens_vdso_data(const struct vdso_data *vd) { return _timens_data; } -#endif +#endif /* CONFIG_TIME_NS */
+#endif /* __CHERI_PURE_CAPABILITY__ */ #endif /* !__ASSEMBLY__ */ diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index fe7a53c6781f..07705ec1304d 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -33,6 +33,26 @@ ldflags-y += -T ccflags-y := -fno-common -fno-builtin -fno-stack-protector -ffixed-x18 ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO +# When building for PCuABI, this Makefile will be called twice: first to build +# the regular arm64 vDSO, and second to build the purecap vDSO. We must change +# certain variables/flags depending on which vDSO we are building. +ifdef BUILD_PURECAP_VDSO
- PURECAP_FLAGS := -mabi=purecap
- REMOVE_FLAGS := -mgeneral-regs-only
Would be nice to clarify why we need this (it's not obvious that the compiler doesn't consider capability registers as "general-regs").
- ccflags-y += $(PURECAP_FLAGS)
- asflags-y := $(PURECAP_FLAGS)
- ccflags-remove-y := $(REMOVE_FLAGS)
- asflags-remove-y := $(REMOVE_FLAGS)
+# We want the purecap vDSO in its own directory (kernel/vdso-purecap)
Nit: better to indent the comments like the rest of the code (one tab here).
- output-dir := $(obj)-purecap
This should work, but obj-vdso is still using $(obj). I assume this still works because the objects will be rebuilt when building for purecap, but clearly we shouldn't be overwriting the standard objects with the purecap ones. Making obj-vdso use $(output-dir) should just work.
+# Put the offsets for the purecap vDSO into their own header file
- offsets := include/generated/vdso-purecap-offsets.h
+else
- output-dir := $(obj)
- offsets := include/generated/vdso-offsets.h
+endif
# -Wmissing-prototypes and -Wmissing-declarations are removed from # the CFLAGS of vgettimeofday.c to make possible to build the # kernel with CONFIG_WERROR enabled. @@ -58,13 +78,20 @@ GCOV_PROFILE := n targets += vdso.lds CPPFLAGS_vdso.lds += -P -C -U$(ARCH) +# Make the vdso-purecap directory +purecap_mkdir: +ifdef BUILD_PURECAP_VDSO
- @mkdir -p $(output-dir)
Once obj-vdso is amended, this needs to be run before any object is built. After investigating a little, it seems that the right thing to do is to add an order-only prerequisite on the directory. Conveniently, the make documentation provides exactly this situation as example [1]. We don't even need to have a named target with that approach (that is, only objects need to depend on the directory, as dependencies as transitive).
[1] https://www.gnu.org/savannah-checkouts/gnu/make/manual/html_node/Prerequisit...
+endif
# Link rule for the .so file, .lds has to be first -$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE +$(output-dir)/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 +$(output-dir)/%.so: purecap_mkdir +$(output-dir)/%.so: OBJCOPYFLAGS := -S +$(output-dir)/%.so: $(output-dir)/%.so.dbg FORCE $(call if_changed,objcopy) # Generate VDSO offsets using helper script @@ -72,7 +99,7 @@ gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh quiet_cmd_vdsosym = VDSOSYM $@ cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ -include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE +$(offsets): $(output-dir)/vdso.so.dbg FORCE $(call if_changed,vdsosym) # Actual build commands @@ -81,10 +108,10 @@ quiet_cmd_vdsold_and_vdso_check = LD $@ # Install commands for the unstripped file quiet_cmd_vdso_install = INSTALL $@
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
cmd_vdso_install = cp $(output-dir)/$@.dbg $(MODLIB)/vdso/$@
-vdso.so: $(obj)/vdso.so.dbg +vdso.so: $(output-dir)/vdso.so.dbg @mkdir -p $(MODLIB)/vdso $(call cmd,vdso_install) -vdso_install: vdso.so +vdso_install: purecap_mkdir vdso.so diff --git a/include/linux/compiler.h b/include/linux/compiler.h index d7779a18b24f..7864350d0edb 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -3,6 +3,7 @@ #define __LINUX_COMPILER_H #include <linux/compiler_types.h> +#include <linux/types.h> #ifndef __ASSEMBLY__ @@ -222,7 +223,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, */ static inline void *offset_to_ptr(const int *off) {
- return (void *)((unsigned long)off + *off);
- return (void *)((uintptr_t)off + *off);
} #endif /* __ASSEMBLY__ */ diff --git a/include/linux/types.h b/include/linux/types.h index 897c134c1e8a..aa661f26fb6c 100644 --- a/include/linux/types.h +++ b/include/linux/types.h @@ -46,7 +46,11 @@ typedef __kernel_gid32_t gid_t; typedef __kernel_uid16_t uid16_t; typedef __kernel_gid16_t gid16_t; +#ifdef __CHERI_PURE_CAPABILITY__ +typedef __uintcap_t uintptr_t;
Nit: uintptr_t isn't aligned (one tab too many).
+#else typedef unsigned long uintptr_t; +#endif
As much as possible, patches should only touch one subsystem at a time. In particular, arch patches should avoid touching generic code, if possible. Since these header changes are no-ops outside of purecap, we can easily split them out into a preparatory patch. It does look pretty unexpected to be adding purecap-specific code when we still have a hybrid kernel, so the commit message should clarify the purpose.
#ifdef CONFIG_CHERI_PURECAP_UABI typedef __uintcap_t user_uintptr_t; 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>
Sorry I forgot to ask in v1, why is this change needed?
Kevin
/**
- as_user_ptr() - Convert an arbitrary integer value to a user pointer.
Map the purecap vDSO into userspace application address space, therefore providing purecap userspace applications a purecap vDSO to use. Redefine the aarch64 vDSO as the compat vDSO, therefore allowing both regular arm64 and purecap binaries to use a vDSO under PCuABI.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com --- arch/arm64/include/asm/elf.h | 26 +++++++++---- arch/arm64/include/asm/vdso.h | 1 + arch/arm64/kernel/Makefile | 4 +- arch/arm64/kernel/vdso-purecap-wrap.S | 22 +++++++++++ arch/arm64/kernel/vdso.c | 54 ++++++++++++++++++++++++++- fs/compat_binfmt_elf.c | 24 ++++++------ 6 files changed, 109 insertions(+), 22 deletions(-) create mode 100644 arch/arm64/kernel/vdso-purecap-wrap.S
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index f01f1f99cf03..3ebbab3cf92f 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -188,21 +188,26 @@ do { \ NEW_AUX_ENT(AT_IGNORE, 0); \ } while (0)
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES +struct linux_binprm; + #ifdef CONFIG_CHERI_PURECAP_UABI +extern int purecap_setup_additional_pages(struct linux_binprm *bprm, + int uses_interp); +#define arch_setup_additional_pages purecap_setup_additional_pages /* - * TODO [PCuABI]: In Transitional PCuABI, AT_SYSINFO_EHDR is passed as NULL - * as there is no purecap vDSO yet. + * TODO [PCuABI]: Look into restricting the bounds of this capability to just + * the vDSO pages, as currently the bounds are of the root user capability. */ -#define ARCH_DLINFO SETUP_DLINFO(0) +#define ARCH_DLINFO SETUP_DLINFO(uaddr_to_user_ptr_safe(\ +(elf_addr_t)current->mm->context.vdso)) #else /* !CONFIG_CHERI_PURECAP_UABI */ +extern int aarch64_setup_additional_pages(struct linux_binprm *bprm, + int uses_interp); +#define arch_setup_additional_pages aarch64_setup_additional_pages #define ARCH_DLINFO SETUP_DLINFO((elf_addr_t)current->mm->context.vdso) #endif /* CONFIG_CHERI_PURECAP_UABI */
-#define ARCH_HAS_SETUP_ADDITIONAL_PAGES -struct linux_binprm; -extern int arch_setup_additional_pages(struct linux_binprm *bprm, - int uses_interp); - /* 1GB of VA */ #ifdef CONFIG_COMPAT #define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \ @@ -244,6 +249,11 @@ typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
#define COMPAT_ARCH_DLINFO SETUP_DLINFO((elf_addr_t)current->mm->context.vdso)
+extern int aarch64_setup_additional_pages(struct linux_binprm *bprm, + int uses_interp); +#define compat_arch_setup_additional_pages \ + aarch64_setup_additional_pages + #else /* !CONFIG_COMPAT64 */
/* PIE load location for compat arm. Must match ARM ELF_ET_DYN_BASE. */ diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h index 83a50071a85e..709b5bbec255 100644 --- a/arch/arm64/include/asm/vdso.h +++ b/arch/arm64/include/asm/vdso.h @@ -28,6 +28,7 @@
extern char vdso_start[], vdso_end[]; extern char vdso32_start[], vdso32_end[]; +extern char vdso_purecap_start[], vdso_purecap_end[];
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 12eb4c55234e..934a677c8f69 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile @@ -72,12 +72,14 @@ obj-$(CONFIG_ARM64_MTE) += mte.o obj-$(CONFIG_ARM64_MORELLO) += morello.o obj-y += vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o +obj-$(CONFIG_CHERI_PURECAP_UABI) += vdso-purecap-wrap.o obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o CFLAGS_patch-scs.o += -mbranch-protection=none
# Force dependency (vdso*-wrap.S includes vdso.so through incbin) $(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so $(obj)/vdso32-wrap.o: $(obj)/vdso32/vdso.so +$(obj)/vdso-purecap-wrap.o: $(obj)/vdso-purecap/vdso.so
obj-y += probes/ obj-y += head.o @@ -88,4 +90,4 @@ AFLAGS_head.o += -DVMLINUX_PATH=""$(realpath $(objtree)/vmlinux)"" endif
# for cleaning -subdir- += vdso vdso32 +subdir- += vdso vdso32 vdso-purecap diff --git a/arch/arm64/kernel/vdso-purecap-wrap.S b/arch/arm64/kernel/vdso-purecap-wrap.S new file mode 100644 index 000000000000..b0bac3af4925 --- /dev/null +++ b/arch/arm64/kernel/vdso-purecap-wrap.S @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023 ARM Limited + */ + +#include <linux/init.h> +#include <linux/linkage.h> +#include <linux/const.h> +#include <asm/assembler.h> +#include <asm/page.h> + + .globl vdso_purecap_start, vdso_purecap_end + .section .rodata + .balign PAGE_SIZE +vdso_purecap_start: + .incbin "arch/arm64/kernel/vdso-purecap/vdso.so" + .balign PAGE_SIZE +vdso_purecap_end: + + .previous + +emit_aarch64_feature_1_and diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index c9d961249894..53269afa5bc1 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -32,6 +32,7 @@ enum vdso_abi { VDSO_ABI_AA64, VDSO_ABI_AA32, + VDSO_ABI_PURECAP, };
enum vvar_pages { @@ -64,6 +65,13 @@ static struct vdso_abi_info vdso_info[] __ro_after_init = { .vdso_code_end = vdso32_end, }, #endif /* CONFIG_COMPAT_VDSO */ +#ifdef CONFIG_CHERI_PURECAP_UABI + [VDSO_ABI_PURECAP] = { + .name = "vdso_purecap", + .vdso_code_start = vdso_purecap_start, + .vdso_code_end = vdso_purecap_end, + }, +#endif /* CONFIG_CHERI_PURECAP_UABI */ };
/* @@ -143,6 +151,10 @@ int vdso_join_timens(struct task_struct *task, struct time_namespace *ns) #ifdef CONFIG_COMPAT_VDSO if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_AA32].dm)) zap_vma_pages(vma); +#endif +#ifdef CONFIG_CHERI_PURECAP_UABI + if (vma_is_special_mapping(vma, vdso_info[VDSO_ABI_PURECAP].dm)) + zap_vma_pages(vma); #endif }
@@ -411,6 +423,46 @@ int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) } #endif /* CONFIG_COMPAT32 */
+#ifdef CONFIG_CHERI_PURECAP_UABI +enum purecap_map { + PURECAP_MAP_VVAR, + PURECAP_MAP_VDSO, +}; + +static struct vm_special_mapping purecap_vdso_maps[] __ro_after_init = { + [PURECAP_MAP_VVAR] = { + .name = "[vvar]", + .fault = vvar_fault, + }, + [PURECAP_MAP_VDSO] = { + .name = "[vdso]", + .mremap = vdso_mremap, + }, +}; + +static int __init purecap_vdso_init(void) { + vdso_info[VDSO_ABI_PURECAP].dm = &purecap_vdso_maps[PURECAP_MAP_VVAR]; + vdso_info[VDSO_ABI_PURECAP].cm = &purecap_vdso_maps[PURECAP_MAP_VDSO]; + + return __vdso_init(VDSO_ABI_PURECAP); +} +arch_initcall(purecap_vdso_init); + +int purecap_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +{ + struct mm_struct *mm = current->mm; + int ret; + + if (mmap_write_lock_killable(mm)) + return -EINTR; + + ret = __setup_additional_pages(VDSO_ABI_PURECAP, mm, bprm, uses_interp); + mmap_write_unlock(mm); + + return ret; +} +#endif /* CONFIG_CHERI_PURECAP_UABI */ + enum aarch64_map { AA64_MAP_VVAR, AA64_MAP_VDSO, @@ -436,7 +488,7 @@ static int __init vdso_init(void) } arch_initcall(vdso_init);
-int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) +int aarch64_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) { struct mm_struct *mm = current->mm; int ret; diff --git a/fs/compat_binfmt_elf.c b/fs/compat_binfmt_elf.c index b6b453b35946..631f8cff8d36 100644 --- a/fs/compat_binfmt_elf.c +++ b/fs/compat_binfmt_elf.c @@ -120,18 +120,6 @@ #define ELF_PLAT_INIT COMPAT_ELF_PLAT_INIT #endif
-#ifdef compat_arch_setup_additional_pages -#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ - compat_arch_setup_additional_pages(bprm, interpreter) -#endif - -#ifdef COMPAT_ARCH_SETUP_ADDITIONAL_PAGES -#undef ARCH_HAS_SETUP_ADDITIONAL_PAGES -#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 -#undef ARCH_SETUP_ADDITIONAL_PAGES -#define ARCH_SETUP_ADDITIONAL_PAGES COMPAT_ARCH_SETUP_ADDITIONAL_PAGES -#endif - #ifdef compat_elf_read_implies_exec #undef elf_read_implies_exec #define elf_read_implies_exec compat_elf_read_implies_exec @@ -177,6 +165,18 @@ }) #endif
+#ifdef compat_arch_setup_additional_pages +#define COMPAT_ARCH_SETUP_ADDITIONAL_PAGES(bprm, ex, interpreter) \ + compat_arch_setup_additional_pages(bprm, interpreter) +#endif + +#ifdef COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#undef ARCH_HAS_SETUP_ADDITIONAL_PAGES +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 +#undef ARCH_SETUP_ADDITIONAL_PAGES +#define ARCH_SETUP_ADDITIONAL_PAGES COMPAT_ARCH_SETUP_ADDITIONAL_PAGES +#endif + /* * We share all the actual code with the native (64-bit) version. */
On 16/11/2023 04:52, Aditya Deshpande wrote:
[...]
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h index f01f1f99cf03..3ebbab3cf92f 100644 --- a/arch/arm64/include/asm/elf.h +++ b/arch/arm64/include/asm/elf.h @@ -188,21 +188,26 @@ do { \ NEW_AUX_ENT(AT_IGNORE, 0); \ } while (0) +#define ARCH_HAS_SETUP_ADDITIONAL_PAGES +struct linux_binprm;
#ifdef CONFIG_CHERI_PURECAP_UABI +extern int purecap_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
+#define arch_setup_additional_pages purecap_setup_additional_pages /*
- TODO [PCuABI]: In Transitional PCuABI, AT_SYSINFO_EHDR is passed as NULL
- as there is no purecap vDSO yet.
- TODO [PCuABI]: Look into restricting the bounds of this capability to just
*/
- the vDSO pages, as currently the bounds are of the root user capability.
-#define ARCH_DLINFO SETUP_DLINFO(0) +#define ARCH_DLINFO SETUP_DLINFO(uaddr_to_user_ptr_safe(\ +(elf_addr_t)current->mm->context.vdso))
Nit: should be indented (typically at the level of the opening parenthesis). Another nit, there is normally a space before continuation backslashes.
#else /* !CONFIG_CHERI_PURECAP_UABI */ +extern int aarch64_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
This declaration can be moved next to #define ARCH_HAS_SETUP... / struct linux_binprm; above (you chose the right place to move them to, that's where they were originally). We are always using this function (either in native or compat64), so it's simpler to define it in just one place, and it makes the diff easier to digest.
+#define arch_setup_additional_pages aarch64_setup_additional_pages #define ARCH_DLINFO SETUP_DLINFO((elf_addr_t)current->mm->context.vdso) #endif /* CONFIG_CHERI_PURECAP_UABI */ [...]
+static int __init purecap_vdso_init(void) {
{ is always in its own line when defining a function (it gets confusing when initialising globals as in that case { is at the end of the line).
Kevin
On 16/11/2023 04:52, Aditya Deshpande wrote:
Replace getauxval() with getauxptr() when building the tests for purecap. By building the vDSO selftests as purecap binaries, we can test the purecap vDSO.
Signed-off-by: Aditya Deshpande aditya.deshpande@arm.com
tools/testing/selftests/vDSO/parse_vdso.h | 16 ++++++++++++++++ tools/testing/selftests/vDSO/vdso_test_abi.c | 5 +++-- .../selftests/vDSO/vdso_test_gettimeofday.c | 6 ++---- 3 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/vDSO/parse_vdso.h b/tools/testing/selftests/vDSO/parse_vdso.h index de0453067d7c..58f3df88160d 100644 --- a/tools/testing/selftests/vDSO/parse_vdso.h +++ b/tools/testing/selftests/vDSO/parse_vdso.h @@ -4,6 +4,7 @@ #define PARSE_VDSO_H #include <stdint.h> +#include <stdlib.h>
Do we need this header for something in particular? getauxval/getauxptr are pulled by sys/auxv.h, included by all the .c's that include this header. Arguably it would be better to move the #include <sys/auxv.h> here, to avoid relying on those implicit #include.
/*
- To use this vDSO parser, first call one of the vdso_init_* functions.
@@ -28,4 +29,19 @@ void *vdso_sym(const char *version, const char *name); void vdso_init_from_sysinfo_ehdr(uintptr_t base); void vdso_init_from_auxv(void *auxv); +/*
- Under PCuABI, pointers in the auxiliary vector can no longer be represented
- as unsigned long as they are now capabilities. Get the capability to the
- vDSO using getauxptr() instead, which returns a a capability instead of
- unsigned long.
- */
+static inline uintptr_t get_sysinfo_ehdr() +{ +#ifdef __CHERI_PURE_CAPABILITY__
- return getauxptr(AT_SYSINFO_EHDR);
+#else
- return (uintptr_t)getauxval(AT_SYSINFO_EHDR);
+#endif +}
#endif diff --git a/tools/testing/selftests/vDSO/vdso_test_abi.c b/tools/testing/selftests/vDSO/vdso_test_abi.c index 883ca85424bc..091efeaa3172 100644 --- a/tools/testing/selftests/vDSO/vdso_test_abi.c +++ b/tools/testing/selftests/vDSO/vdso_test_abi.c @@ -20,6 +20,7 @@ #include "../kselftest.h" #include "vdso_config.h" +#include "parse_vdso.h" extern void *vdso_sym(const char *version, const char *name); extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); @@ -175,7 +176,7 @@ static inline void vdso_test_clock(clockid_t clock_id) int main(int argc, char **argv) {
- unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
- uintptr_t sysinfo_ehdr = get_sysinfo_ehdr();
ksft_print_header(); ksft_set_plan(VDSO_TEST_PLAN); @@ -190,7 +191,7 @@ int main(int argc, char **argv) printf("[vDSO kselftest] VDSO_VERSION: %s\n", version);
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
- vdso_init_from_sysinfo_ehdr(sysinfo_ehdr);
vdso_test_gettimeofday(); diff --git a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c index e411f287a426..359f881ac574 100644 --- a/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c +++ b/tools/testing/selftests/vDSO/vdso_test_gettimeofday.c @@ -37,14 +37,12 @@ const char *name = "__vdso_gettimeofday"; int main(int argc, char **argv) {
- unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
- uintptr_t sysinfo_ehdr = get_sysinfo_ehdr(); if (!sysinfo_ehdr) { printf("AT_SYSINFO_EHDR is not present!\n"); return KSFT_SKIP; }
- vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR));
Nit: I'd leave the empty lines unchanged.
Kevin
- vdso_init_from_sysinfo_ehdr(sysinfo_ehdr); /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); gtod_t gtod = (gtod_t)vdso_sym(version, name);
linux-morello@op-lists.linaro.org