Commit b920aa77 authored by Heiko Carstens's avatar Heiko Carstens Committed by Jason A. Donenfeld
Browse files

s390/vdso: Wire up getrandom() vdso implementation



Provide the s390 specific vdso getrandom() architecture backend.

_vdso_rng_data required data is placed within the _vdso_data vvar page,
by using a hardcoded offset larger than vdso_data.

As required the chacha20 implementation does not write to the stack.

The implementation follows more or less the arm64 implementations and
makes use of vector instructions. It has a fallback to the getrandom()
system call for machines where the vector facility is not installed.

The check if the vector facility is installed, as well as an
optimization for machines with the vector-enhancements facility 2, is
implemented with alternatives, avoiding runtime checks.

Note that __kernel_getrandom() is implemented without the vdso user
wrapper which would setup a stack frame for odd cases (aka very old
glibc variants) where the caller has not done that. All callers of
__kernel_getrandom() are required to setup a stack frame, like the C ABI
requires it.

The vdso testcases vdso_test_getrandom and vdso_test_chacha pass.

Benchmark on a z16:

    $ ./vdso_test_getrandom bench-single
       vdso: 25000000 times in 0.493703559 seconds
    syscall: 25000000 times in 6.584025337 seconds

Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Reviewed-by: default avatarHarald Freudenberger <freude@linux.ibm.com>
Signed-off-by: default avatarJason A. Donenfeld <Jason@zx2c4.com>
parent c1ae1b4e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -243,6 +243,7 @@ config S390
	select TRACE_IRQFLAGS_SUPPORT
	select TTY
	select USER_STACKTRACE_SUPPORT
	select VDSO_GETRANDOM
	select VIRT_CPU_ACCOUNTING
	select ZONE_DMA
	# Note: keep the above list sorted alphabetically
+22 −0
Original line number Diff line number Diff line
@@ -407,6 +407,28 @@
	MRXBOPC	0, 0x0E, v1
.endm

/* VECTOR STORE BYTE REVERSED ELEMENTS */
	.macro	VSTBR	vr1, disp, index="%r0", base, m
	VX_NUM	v1, \vr1
	GR_NUM	x2, \index
	GR_NUM	b2, \base
	.word	0xE600 | ((v1&15) << 4) | (x2&15)
	.word	(b2 << 12) | (\disp)
	MRXBOPC	\m, 0x0E, v1
.endm
.macro	VSTBRH	vr1, disp, index="%r0", base
	VSTBR	\vr1, \disp, \index, \base, 1
.endm
.macro	VSTBRF	vr1, disp, index="%r0", base
	VSTBR	\vr1, \disp, \index, \base, 2
.endm
.macro	VSTBRG	vr1, disp, index="%r0", base
	VSTBR	\vr1, \disp, \index, \base, 3
.endm
.macro	VSTBRQ	vr1, disp, index="%r0", base
	VSTBR	\vr1, \disp, \index, \base, 4
.endm

/* VECTOR STORE MULTIPLE */
.macro	VSTM	vfrom, vto, disp, base, hint=3
	VX_NUM	v1, \vfrom
+40 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __ASM_VDSO_GETRANDOM_H
#define __ASM_VDSO_GETRANDOM_H

#ifndef __ASSEMBLY__

#include <vdso/datapage.h>
#include <asm/vdso/vsyscall.h>
#include <asm/syscall.h>
#include <asm/unistd.h>
#include <asm/page.h>

/**
 * 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 random 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 syscall3(__NR_getrandom, (long)buffer, (long)len, (long)flags);
}

static __always_inline const struct vdso_rng_data *__arch_get_vdso_rng_data(void)
{
	/*
	 * The RNG data is in the real VVAR data page, but if a task belongs to a time namespace
	 * then VVAR_DATA_PAGE_OFFSET points to the namespace-specific VVAR page and VVAR_TIMENS_
	 * PAGE_OFFSET points to the real VVAR page.
	 */
	if (IS_ENABLED(CONFIG_TIME_NS) && _vdso_data->clock_mode == VDSO_CLOCKMODE_TIMENS)
		return (void *)&_vdso_rng_data + VVAR_TIMENS_PAGE_OFFSET * PAGE_SIZE;
	return &_vdso_rng_data;
}

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_VDSO_GETRANDOM_H */
+15 −0
Original line number Diff line number Diff line
@@ -2,12 +2,21 @@
#ifndef __ASM_VDSO_VSYSCALL_H
#define __ASM_VDSO_VSYSCALL_H

#define __VDSO_RND_DATA_OFFSET	768

#ifndef __ASSEMBLY__

#include <linux/hrtimer.h>
#include <linux/timekeeper_internal.h>
#include <vdso/datapage.h>
#include <asm/vdso.h>

enum vvar_pages {
	VVAR_DATA_PAGE_OFFSET,
	VVAR_TIMENS_PAGE_OFFSET,
	VVAR_NR_PAGES
};

/*
 * Update the vDSO data page to keep in sync with kernel timekeeping.
 */
@@ -18,6 +27,12 @@ static __always_inline struct vdso_data *__s390_get_k_vdso_data(void)
}
#define __arch_get_k_vdso_data __s390_get_k_vdso_data

static __always_inline struct vdso_rng_data *__s390_get_k_vdso_rnd_data(void)
{
	return (void *)vdso_data + __VDSO_RND_DATA_OFFSET;
}
#define __arch_get_k_vdso_rng_data __s390_get_k_vdso_rnd_data

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

+1 −6
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/time_namespace.h>
#include <linux/random.h>
#include <vdso/datapage.h>
#include <asm/vdso/vsyscall.h>
#include <asm/alternative.h>
#include <asm/vdso.h>

@@ -31,12 +32,6 @@ static union vdso_data_store vdso_data_store __page_aligned_data;

struct vdso_data *vdso_data = vdso_data_store.data;

enum vvar_pages {
	VVAR_DATA_PAGE_OFFSET,
	VVAR_TIMENS_PAGE_OFFSET,
	VVAR_NR_PAGES,
};

#ifdef CONFIG_TIME_NS
struct vdso_data *arch_get_vdso_data(void *vvar_page)
{
Loading