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.