Skip to content

Commit

Permalink
powerpc/vdso: Wire up getrandom() vDSO implementation on PPC32
Browse files Browse the repository at this point in the history
To be consistent with other VDSO functions, the function is called
__kernel_getrandom()

__arch_chacha20_blocks_nostack() fonction is implemented basically
with 32 bits operations. It performs 4 QUARTERROUND operations in
parallele. There are enough registers to avoid using the stack:

On input:
	r3: output bytes
	r4: 32-byte key input
	r5: 8-byte counter input/output
	r6: number of 64-byte blocks to write to output

During operation:
	stack: pointer to counter (r5) and non-volatile registers (r14-131)
	r0: counter of blocks (initialised with r6)
	r4: Value '4' after key has been read, used for indexing
	r5-r12: key
	r14-r15: block counter
	r16-r31: chacha state

At the end:
	r0, r6-r12: Zeroised
	r5, r14-r31: Restored

Performance on powerpc 885 (using kernel selftest):
	~# ./vdso_test_getrandom bench-single
	   vdso: 25000000 times in 62.938002291 seconds
	   libc: 25000000 times in 535.581916866 seconds
	syscall: 25000000 times in 531.525042806 seconds

Performance on powerpc 8321 (using kernel selftest):
	~# ./vdso_test_getrandom bench-single
	   vdso: 25000000 times in 16.899318858 seconds
	   libc: 25000000 times in 131.050596522 seconds
	syscall: 25000000 times in 129.794790389 seconds

This first patch adds support for PPC32. As selftests cannot easily
be generated only for PPC32, and because the following patch brings
support for PPC64 anyway, this patch opts out all code in
__arch_chacha20_blocks_nostack() so that vdso_test_chacha will not
fail to compile and will not crash on PPC64/PPC64LE, allthough the
selftest itself will fail. This patch also adds a dummy
__kernel_getrandom() function that returns ENOSYS on PPC64 so that
vdso_test_getrandom returns KSFT_SKIP instead of KSFT_FAIL.

Signed-off-by: Christophe Leroy <christophe.leroy@csgroup.eu>
---
v5:
- Add back vdso symlink that vanished in v4 after a rebase back and forth with rejected patch "selftests: vDSO: Do not rely on $ARCH for vdso_test_getrandom && vdso_test_chacha"
- Set meaningfull names to registers and constants in chacha assembly
- Add 32 bits LE logic in this patch as well allthought it is only usefull for ppc64le.
- Remove the temporary ppc64 __kernel_getrandom added in v4, selftest will return KSFT_FAIL until following patch, not a big issue.
- Move the -DBUILD_VDSO logic in patch 3 to allow build VDSO32 on ppc64.

v4:
- Counter has native byte order
- Fix selftest build on ppc64le until implemented.
- On ppc64, for now implement __kernel_getrandom to return ENOSYS error
- Use stwbrx directly, not compat macro.

v3:
- Preserve r13, implies saving r5 on stack
- Split PPC64 implementation out.
  • Loading branch information
chleroy committed Sep 2, 2024
1 parent 915d012 commit 2de4c0d
Show file tree
Hide file tree
Showing 13 changed files with 455 additions and 5 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ config PPC
select SYSCTL_EXCEPTION_TRACE
select THREAD_INFO_IN_TASK
select TRACE_IRQFLAGS_SUPPORT
select VDSO_GETRANDOM
#
# Please keep this list sorted alphabetically.
#
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/include/asm/mman.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <uapi/asm/mman.h>

#ifdef CONFIG_PPC64
#if defined(CONFIG_PPC64) && !defined(BUILD_VDSO)

#include <asm/cputable.h>
#include <linux/mm.h>
Expand Down
54 changes: 54 additions & 0 deletions arch/powerpc/include/asm/vdso/getrandom.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2024 Christophe Leroy <christophe.leroy@csgroup.eu>, CS GROUP France
*/
#ifndef _ASM_POWERPC_VDSO_GETRANDOM_H
#define _ASM_POWERPC_VDSO_GETRANDOM_H

#ifndef __ASSEMBLY__

static __always_inline int do_syscall_3(const unsigned long _r0, const unsigned long _r3,
const unsigned long _r4, const unsigned long _r5)
{
register long r0 asm("r0") = _r0;
register unsigned long r3 asm("r3") = _r3;
register unsigned long r4 asm("r4") = _r4;
register unsigned long r5 asm("r5") = _r5;
register int ret asm ("r3");

asm volatile(
" sc\n"
" bns+ 1f\n"
" neg %0, %0\n"
"1:\n"
: "=r" (ret), "+r" (r4), "+r" (r5), "+r" (r0)
: "r" (r3)
: "memory", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "cr0", "ctr");

return ret;
}

/**
* getrandom_syscall - Invoke the getrandom() syscall.
* @buffer: Destination buffer to fill with random bytes.
* @len: Size of @buffer in bytes.
* @flags: Zero or more GRND_* flags.
* Returns: The number of bytes written to @buffer, or a negative value indicating an error.
*/
static __always_inline ssize_t getrandom_syscall(void *buffer, size_t len, unsigned int flags)
{
return do_syscall_3(__NR_getrandom, (unsigned long)buffer,
(unsigned long)len, (unsigned long)flags);
}

static __always_inline struct vdso_rng_data *__arch_get_vdso_rng_data(void)
{
return NULL;
}

ssize_t __c_kernel_getrandom(void *buffer, size_t len, unsigned int flags, void *opaque_state,
size_t opaque_len, const struct vdso_rng_data *vd);

#endif /* !__ASSEMBLY__ */

#endif /* _ASM_POWERPC_VDSO_GETRANDOM_H */
6 changes: 6 additions & 0 deletions arch/powerpc/include/asm/vdso/vsyscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ struct vdso_data *__arch_get_k_vdso_data(void)
}
#define __arch_get_k_vdso_data __arch_get_k_vdso_data

static __always_inline
struct vdso_rng_data *__arch_get_k_vdso_rng_data(void)
{
return &vdso_data->rng_data;
}

/* The asm-generic header needs to be included after the definitions above */
#include <asm-generic/vdso/vsyscall.h>

Expand Down
2 changes: 2 additions & 0 deletions arch/powerpc/include/asm/vdso_datapage.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ struct vdso_arch_data {
__u32 compat_syscall_map[SYSCALL_MAP_SIZE]; /* Map of compat syscalls */

struct vdso_data data[CS_BASES];
struct vdso_rng_data rng_data;
};

#else /* CONFIG_PPC64 */
Expand All @@ -95,6 +96,7 @@ struct vdso_arch_data {
__u32 syscall_map[SYSCALL_MAP_SIZE]; /* Map of syscalls */
__u32 compat_syscall_map[0]; /* No compat syscalls on PPC32 */
struct vdso_data data[CS_BASES];
struct vdso_rng_data rng_data;
};

#endif /* CONFIG_PPC64 */
Expand Down
1 change: 1 addition & 0 deletions arch/powerpc/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ int main(void)

/* datapage offsets for use by vdso */
OFFSET(VDSO_DATA_OFFSET, vdso_arch_data, data);
OFFSET(VDSO_RNG_DATA_OFFSET, vdso_arch_data, rng_data);
OFFSET(CFG_TB_TICKS_PER_SEC, vdso_arch_data, tb_ticks_per_sec);
#ifdef CONFIG_PPC64
OFFSET(CFG_ICACHE_BLOCKSZ, vdso_arch_data, icache_block_size);
Expand Down
14 changes: 11 additions & 3 deletions arch/powerpc/kernel/vdso/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ include $(srctree)/lib/vdso/Makefile
obj-vdso32 = sigtramp32-32.o gettimeofday-32.o datapage-32.o cacheflush-32.o note-32.o getcpu-32.o
obj-vdso64 = sigtramp64-64.o gettimeofday-64.o datapage-64.o cacheflush-64.o note-64.o getcpu-64.o

obj-vdso32 += getrandom-32.o vgetrandom-chacha-32.o

ifneq ($(c-gettimeofday-y),)
CFLAGS_vgettimeofday-32.o += -include $(c-gettimeofday-y)
# Go prior to 1.16.x assumes r30 is not clobbered by any VDSO code. That used to be true
Expand All @@ -17,6 +19,10 @@ ifneq ($(c-gettimeofday-y),)
CFLAGS_vgettimeofday-64.o += -include $(c-gettimeofday-y) $(call cc-option, -ffixed-r30)
endif

ifneq ($(c-getrandom-y),)
CFLAGS_vgetrandom-32.o += -include $(c-getrandom-y)
endif

# Build rules

ifdef CROSS32_COMPILE
Expand All @@ -25,13 +31,13 @@ else
VDSOCC := $(CC)
endif

targets := $(obj-vdso32) vdso32.so.dbg vgettimeofday-32.o
targets := $(obj-vdso32) vdso32.so.dbg vgettimeofday-32.o vgetrandom-32.o
targets += crtsavres-32.o
obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
targets += $(obj-vdso64) vdso64.so.dbg vgettimeofday-64.o
obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))

ccflags-y := -fno-common -fno-builtin
ccflags-y := -fno-common -fno-builtin -DBUILD_VDSO
ccflags-y += $(DISABLE_LATENT_ENTROPY_PLUGIN)
ccflags-y += $(call cc-option, -fno-stack-protector)
ccflags-y += -DDISABLE_BRANCH_PROFILING
Expand Down Expand Up @@ -63,7 +69,7 @@ targets += vdso64.lds
CPPFLAGS_vdso64.lds += -P -C

# link rule for the .so file, .lds has to be first
$(obj)/vdso32.so.dbg: $(obj)/vdso32.lds $(obj-vdso32) $(obj)/vgettimeofday-32.o $(obj)/crtsavres-32.o FORCE
$(obj)/vdso32.so.dbg: $(obj)/vdso32.lds $(obj-vdso32) $(obj)/vgettimeofday-32.o $(obj)/vgetrandom-32.o $(obj)/crtsavres-32.o FORCE
$(call if_changed,vdso32ld_and_check)
$(obj)/vdso64.so.dbg: $(obj)/vdso64.lds $(obj-vdso64) $(obj)/vgettimeofday-64.o FORCE
$(call if_changed,vdso64ld_and_check)
Expand All @@ -75,6 +81,8 @@ $(obj)/crtsavres-32.o: %-32.o: $(srctree)/arch/powerpc/lib/crtsavres.S FORCE
$(call if_changed_dep,vdso32as)
$(obj)/vgettimeofday-32.o: %-32.o: %.c FORCE
$(call if_changed_dep,vdso32cc)
$(obj)/vgetrandom-32.o: %-32.o: %.c FORCE
$(call if_changed_dep,vdso32cc)
$(obj-vdso64): %-64.o: %.S FORCE
$(call if_changed_dep,vdso64as)
$(obj)/vgettimeofday-64.o: %-64.o: %.c FORCE
Expand Down
50 changes: 50 additions & 0 deletions arch/powerpc/kernel/vdso/getrandom.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Userland implementation of getrandom() for processes
* for use in the vDSO
*
* Copyright (C) 2024 Christophe Leroy <christophe.leroy@csgroup.eu>, CS GROUP France
*/
#include <asm/processor.h>
#include <asm/ppc_asm.h>
#include <asm/vdso.h>
#include <asm/vdso_datapage.h>
#include <asm/asm-offsets.h>
#include <asm/unistd.h>

/*
* The macro sets two stack frames, one for the caller and one for the callee
* because there are no requirement for the caller to set a stack frame when
* calling VDSO so it may have omitted to set one, especially on PPC64
*/

.macro cvdso_call funct
.cfi_startproc
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
.cfi_adjust_cfa_offset PPC_MIN_STKFRM
mflr r0
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
.cfi_adjust_cfa_offset PPC_MIN_STKFRM
PPC_STL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
.cfi_rel_offset lr, PPC_MIN_STKFRM + PPC_LR_STKOFF
get_datapage r8
addi r8, r8, VDSO_RNG_DATA_OFFSET
bl CFUNC(DOTSYM(\funct))
PPC_LL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
cmpwi r3, 0
mtlr r0
addi r1, r1, 2 * PPC_MIN_STKFRM
.cfi_restore lr
.cfi_def_cfa_offset 0
crclr so
bgelr+
crset so
neg r3, r3
blr
.cfi_endproc
.endm

.text
V_FUNCTION_BEGIN(__kernel_getrandom)
cvdso_call __c_kernel_getrandom
V_FUNCTION_END(__kernel_getrandom)
1 change: 1 addition & 0 deletions arch/powerpc/kernel/vdso/vdso32.lds.S
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ VERSION
#if defined(CONFIG_PPC64) || !defined(CONFIG_SMP)
__kernel_getcpu;
#endif
__kernel_getrandom;

local: *;
};
Expand Down
Loading

0 comments on commit 2de4c0d

Please sign in to comment.