Re: [PATCH 02/13] kselftest: arm64: adds arm64/signal support code

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jun 13, 2019 at 12:13:24PM +0100, Cristian Marussi wrote:
> Added some arm64/signal specific boilerplate and utility code to help
> further testcase development. Still no test case code included though.

There is a lot of code here, doing a variety of different things.

It may be more digestible if the common code is added in pieces,
starting with whatever is needed for the simplest testcase, then adding
that testcase, then extending the common code for the next case, and so
on.

This would make it easier to see why each bit of common code is being
added, and how it gets used.

I'll try to review as-is for now, but it might be worth splitting this
patch up a bit when reposting.

> Signed-off-by: Cristian Marussi <cristian.marussi@xxxxxxx>
> ---
>  tools/testing/selftests/arm64/Makefile        |   2 +-
>  .../testing/selftests/arm64/signal/.gitignore |   5 +
>  tools/testing/selftests/arm64/signal/Makefile |  86 ++++++
>  tools/testing/selftests/arm64/signal/README   |  56 ++++
>  .../testing/selftests/arm64/signal/signals.S  |  64 +++++
>  .../arm64/signal/test_arm64_signals.sh        |  44 +++
>  .../selftests/arm64/signal/test_signals.c     |  30 ++
>  .../selftests/arm64/signal/test_signals.h     | 136 ++++++++++
>  .../arm64/signal/test_signals_utils.c         | 256 ++++++++++++++++++
>  .../arm64/signal/test_signals_utils.h         | 110 ++++++++
>  .../arm64/signal/testcases/testcases.c        | 123 +++++++++
>  .../arm64/signal/testcases/testcases.h        |  85 ++++++
>  12 files changed, 996 insertions(+), 1 deletion(-)
>  create mode 100644 tools/testing/selftests/arm64/signal/.gitignore
>  create mode 100644 tools/testing/selftests/arm64/signal/Makefile
>  create mode 100644 tools/testing/selftests/arm64/signal/README
>  create mode 100644 tools/testing/selftests/arm64/signal/signals.S
>  create mode 100755 tools/testing/selftests/arm64/signal/test_arm64_signals.sh
>  create mode 100644 tools/testing/selftests/arm64/signal/test_signals.c
>  create mode 100644 tools/testing/selftests/arm64/signal/test_signals.h
>  create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.c
>  create mode 100644 tools/testing/selftests/arm64/signal/test_signals_utils.h
>  create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.c
>  create mode 100644 tools/testing/selftests/arm64/signal/testcases/testcases.h
> 
> diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile
> index 03a0d4f71218..af59dc74e0dc 100644
> --- a/tools/testing/selftests/arm64/Makefile
> +++ b/tools/testing/selftests/arm64/Makefile
> @@ -6,7 +6,7 @@ ARCH ?= $(shell uname -m)
>  ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/)
>  
>  ifeq ("x$(ARCH)", "xarm64")
> -SUBDIRS :=
> +SUBDIRS := signal
>  else
>  SUBDIRS :=
>  endif
> diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore
> new file mode 100644
> index 000000000000..7234ebd99363
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/.gitignore
> @@ -0,0 +1,5 @@
> +# Helper script's internal testcases list (TPROGS) is regenerated
> +# each time by Makefile on standalone (non KSFT driven) runs.
> +# Committing such list creates a dependency between testcases
> +# patches such that they are no more easily revertable. Just ignore.
> +test_arm64_signals.sh
> diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile
> new file mode 100644
> index 000000000000..9518841124a3
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/Makefile
> @@ -0,0 +1,86 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2019 ARM Limited
> +
> +# Supports also standalone invokation out of KSFT-tree.
> +#
> +# Build standalone with (issued from within the dir containing this makefile):
> +#
> +# 	host:~$ make clean && make INSTALL_PATH=<your-device-instdir> install

Minor nit: why clean?
Minor nit: this won't work if run from ~.  Maybe just say "$" for the
prompt.

> +#
> +# Run standalone on device with:
> +# 	device:~# <your-device-instdir>/test_arm64_signals.sh [-k|-v]

Minor nit: do these tests actually need to run as root?  If not (and for
the above reasons) you could again just say "$".

> +#
> +
> +# A proper top_srcdir is needed both by KSFT(lib.mk)
> +# and standalone builds
> +top_srcdir = ../../../../..
> +
> +CFLAGS += -std=gnu99 -I. -I$(top_srcdir)/tools/testing/selftests/
> +SRCS := $(filter-out testcases/testcases.c,$(wildcard testcases/*.c))
> +PROGS := $(patsubst %.c,%,$(SRCS))
> +
> +# Guessing as best as we can where the Kernel headers
> +# could have been installed depending on ENV config and
> +# type of invocation.
> +ifeq ($(KBUILD_OUTPUT),)
> +khdr_dir = $(top_srcdir)/usr/include
> +else
> +ifeq (0,$(MAKELEVEL))
> +khdr_dir = $(KBUILD_OUTPUT)/usr/include
> +else
> +# the KSFT preferred location when KBUILD_OUTPUT is set
> +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include
> +endif
> +endif
> +
> +CFLAGS += -I$(khdr_dir)
> +
> +# Standalone run
> +ifeq (0,$(MAKELEVEL))
> +CC := $(CROSS_COMPILE)gcc
> +RUNNER = test_arm64_signals.sh
> +INSTALL_PATH ?= install/
> +
> +all: $(RUNNER)
> +
> +$(RUNNER): $(PROGS)
> +	sed -i -e 's#PROGS=.*#PROGS="$(PROGS)"#' $@
> +
> +install: all
> +	mkdir -p $(INSTALL_PATH)/testcases
> +	cp $(PROGS) $(INSTALL_PATH)/testcases
> +	cp $(RUNNER) $(INSTALL_PATH)/
> +
> +.PHONY clean:
> +	rm -f $(PROGS)
> +# KSFT run
> +else
> +# Generated binaries to be installed by top KSFT script
> +TEST_GEN_PROGS := $(notdir $(PROGS))
> +
> +# Get Kernel headers installed and use them.
> +KSFT_KHDR_INSTALL := 1
> +
> +# This include mk will also mangle the TEST_GEN_PROGS list
> +# to account for any OUTPUT target-dirs optionally provided
> +# by the toplevel makefile
> +include ../../lib.mk
> +
> +$(TEST_GEN_PROGS): $(PROGS)
> +	-mkdir -p $(OUTPUT)
> +	-cp $(PROGS) $(OUTPUT)/

Why doesn't it matter if these fail?

(Maybe this was pasted from other kselftest Makefiles...)

> +
> +clean:
> +	$(CLEAN)
> +	rm -f $(PROGS)
> +endif
> +
> +# Common test-unit targets to build common-layout test-cases executables
> +# Needs secondary expansion to properly include the testcase c-file in pre-reqs
> +.SECONDEXPANSION:
> +$(PROGS): test_signals.c test_signals_utils.c signals.S testcases/testcases.c $$@.c test_signals.h test_signals_utils.h testcases/testcases.h
> +	@if [ ! -d $(khdr_dir) ]; then \
> +		echo -n "\n!!! WARNING: $(khdr_dir) NOT FOUND."; \
> +		echo "===>  Are you sure Kernel Headers have been installed properly ?\n"; \
> +	fi
> +	$(CC) $(CFLAGS) $^ -o $@
> diff --git a/tools/testing/selftests/arm64/signal/README b/tools/testing/selftests/arm64/signal/README
> new file mode 100644
> index 000000000000..315d77558e14
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/README
> @@ -0,0 +1,56 @@
> +KSelfTest arm64/signal/
> +=======================
> +
> +Signals Tests
> ++++++++++++++
> +
> +- Tests are built around a common main compilation unit: such shared main
> +  enforces a standard sequence of operations needed to perform a single
> +  signal-test (setup/trigger/run/result/cleanup)
> +
> +- The above mentioned ops are configurable on a test-by-test basis: each test
> +  is described (and configured) using the descriptor signals.h::struct tdescr
> +
> +- Each signal testcase is compiled into its own executable: a separate
> +  executable is used for each test since many tests complete successfully
> +  by receiving some kind of 'Term' signal from the Kernel, so it's safer

Nit: to avoid confusion with SIGTERM, maybe say "some fatal signal"
instead of 'Term' (although I can see what you mean).

> +  to run each test unit in its own standalone process, so as to start each
> +  test from a clean-slate.

Pedantic nit: "-" -> " "

> +
> +- New tests can be simply defined in testcases/ dir providing a proper struct
> +  tdescr overriding all the defaults we wish to change (as of now providing a
> +  custom run method is mandatory though)
> +
> +- Signals' test-cases hereafter defined belong currently to two
> +  principal families:
> +
> +  - 'mangle_' tests: a real signal (SIGUSR1) is raised and used as a trigger
> +    and then the test case code messes-up with the sigframe ucontext_t from
> +    inside the sighandler itself.
> +
> +  - 'fake_sigreturn_' tests: a brand new custom artificial sigframe structure
> +    is placed on the stack and a sigreturn syscall is called to simulate a
> +    real signal return. This kind of tests does not use a trigger usually and
> +    they are just fired using some simple included assembly trampoline code.
> +
> + - Most of these tests are successfully passing if the process gets killed by
> +   some 'Term' signal: usually SIGSEGV or SIGBUS. Since, while writing this

Nit: "some fatal signal"

> +   kind of tests, it is extremely easy in fact to end-up injecting other
> +   unrelated SEGV bugs in the testcases, it becomes extremely tricky to

For tests that receive a fatal signal on success, is it always the same
signal?

I'm wondering whether we can filter expected from unexpected signals
using siginfo.  For a bad sigreturn for example, I'd expect si_addr to
point to (or at least somewhere within) the signal frame.

Maybe it's not that simple, though.

> +   be really sure that the tests are really addressing what they are meant
> +   to address and they are not instead falling apart due to unplanned bugs.
> +   In order to alleviate the misery of the life of such test-developer, a few
> +   helpers are provided:
> +
> +   - a couple of ASSERT_BAD/GOOD_CONTEXT() macros to easily parse a ucontext_t
> +     and verify if it is indeed GOOD or BAD (depending on what we were
> +     expecting), using the same logic/perspective as in the arm64 Kernel signals
> +     routines.
> +
> +   - a sanity mechanism to be used in 'fake_sigreturn_'-alike tests: enabled by
> +     default it takes care to verify that the test-execution had at least
> +     successfully progressed upto the stage of triggering the fake sigreturn

Pedantic nit: "upto" -> "up to"

> +     call.
> +
> +  In both cases test results are expected in terms of some 'Term' signal sent

Nit: "some fatal signal"?

> +  by the Kernel to the test process, or analyzing some final regs state.

In general, putting comments nearer to the functions makes for easier
reviewing, and makes it easier to keep the documentation in sync when
the code changes. 

Since you've written this documentation, we may as well keep it, though.

> diff --git a/tools/testing/selftests/arm64/signal/signals.S b/tools/testing/selftests/arm64/signal/signals.S
> new file mode 100644
> index 000000000000..4f510b3a3b4b
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/signals.S
> @@ -0,0 +1,64 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0
> + * Copyright (C) 2019 ARM Limited
> + */
> +
> +#include <asm/unistd.h>
> +
> +.section	.rodata

This also needs to be marked allocatable (, "a")

You can also just say

	.rodata

which gives you the sane default attributes for this section.

> +call_fmt:
> +	.asciz "Calling sigreturn with fake sigframe sized:%zd at calculated SP @%08lX\n"
> +
> +.text
> +
> +.extern current

This is harmless, but people usually don't bother with .extern.  It
doesn't do anything IIUC.

> +
> +.globl fake_sigreturn
> +
> +/*	fake_sigreturn	x0:&sigframe  x1:sigframe_size  x2:alignment_SP */
> +fake_sigreturn:
> +	stp x20, x21, [sp, #-16]!
> +	stp x22, x23, [sp, #-16]!

Nit:
<tab> <mnemonic> <tab> [<operands> separated by ", " ...]

If this function doesn't return, does it need to save anything at all?

> +
> +	mov x20, x0
> +	mov x21, x1
> +	mov x22, x2
> +	mov x23, sp
> +
> +	/* create space on the stack for fake sigframe..."x22"-aligned */
> +	mov x0, #0
> +	add x0, x21, x22
> +	sub x22, x22, #1
> +	bic x0, x0, x22
> +	sub x23, x23, x0
> +
> +	ldr x0, =call_fmt
> +	mov x1, x21
> +	mov x2, x23
> +	bl printf
> +
> +	mov sp, x23
> +
> +	/* now fill it with the provided content... */
> +	mov x0, sp
> +	mov x1, x20
> +	mov x2, x21
> +	bl memcpy
> +
> +	/*
> +	 * Here saving a last minute SP to current->token acts as a marker:
> +	 * if we got here, we are successfully faking a sigreturn; in other
> +	 * words we are reasonably sure no bad Term signal has been raised
> +	 * till now for unrelated reasons, so we should consider the possibly
> +	 * observed SEGV coming from Kernel restore_sigframe() and triggered
> +	 * as expected from our test-case.
> +	 * For simplicity field 'token' is laid out as first in struct tdescr
> +	 */
> +	ldr x0, current

Nit: this assumes that current is 
> +	str x23, [x0, #0]

What's this for?  Doesn't this overwrite the first bytes of siginfo or
something?

Nit: The ", #0" is also redundant here.

> +	/* SP is already pointing back to the just built fake sigframe here */
> +	mov x8, #__NR_rt_sigreturn
> +	svc #0
> +
> +	/* This should not return here...looping lead to a timeout */
> +	b .

Could we also return here, rather than relying on a timeout?  This
function strictly doesn't return when it succeeds, so this would provide
an immediate indication of failure.

Maybe it's not worth it though, since some kinds of failure might lead
to corruption or jumping to a random location -- which might only be
detectable via a timeout in any case.

> diff --git a/tools/testing/selftests/arm64/signal/test_arm64_signals.sh b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh
> new file mode 100755
> index 000000000000..ecaa5a67d3ca
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/test_arm64_signals.sh
> @@ -0,0 +1,44 @@
> +#!/bin/sh
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2019 ARM Limited
> +
> +ret=0
> +keep_on_fail=0
> +err_out="2> /dev/null"
> +
> +# avoiding getopt

It doesn't matter, but do we need to avoid getopt?

> +while [ $# -gt 0 ]
> +do
> +	case $1 in
> +		"-k")
> +			keep_on_fail=1
> +			shift
> +			;;
> +		"-v")
> +			err_out=""

Or just

	err_out=

> +			shift
> +			;;
> +		*)
> +			shift

Could you move the shift out of the switch here?  Each case seems to end
with it.

> +			;;
> +	esac
> +done
> +
> +TPROGS=
> +
> +i=0
> +tot=$(echo $TPROGS | wc -w)
> +
> +for test in $TPROGS
> +do
> +	eval ./$test $err_out
> +	if [ $? != 0 ]; then
> +		[ $keep_on_fail = 0 ] && echo "===>>> FAILED:: $test <<<===" && ret=1 && break
> +	else
> +		i=$((i + 1))
> +	fi
> +done
> +
> +echo "==>> PASSED: $i/$tot"
> +
> +exit $ret
> diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c
> new file mode 100644
> index 000000000000..afadb8ae33e4
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/test_signals.c
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2019 ARM Limited */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stddef.h>

^ Are any of these needed?  I don't see direct use of any libc stuff in
this file...

> +
> +#include <kselftest.h>
> +
> +#include "test_signals.h"
> +#include "test_signals_utils.h"
> +
> +struct tdescr *current;
> +extern struct tdescr tde;
> +
> +int main(int argc, char *argv[])
> +{
> +	current = &tde;
> +
> +	ksft_print_msg("%s :: %s - SIG_TRIG:%d  SIG_OK:%d -- current:%p\n",
> +		       current->name, current->descr, current->sig_trig,
> +		       current->sig_ok, current);
> +	if (test_setup(current)) {
> +		if (test_run(current))
> +			test_result(current);
> +		test_cleanup(current);
> +	}
> +
> +	return !current->pass;
> +}
> diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h
> new file mode 100644
> index 000000000000..49536326db04
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/test_signals.h
> @@ -0,0 +1,136 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2019 ARM Limited */
> +
> +#ifndef __TEST_SIGNALS_H__
> +#define __TEST_SIGNALS_H__
> +
> +#include <assert.h>
> +#include <stdbool.h>
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <stdint.h>
> +
> +/*
> + * Using ARCH specific and sanitized Kernel headers installed by KSFT
> + * framework since we asked for it by setting flag KSFT_KHDR_INSTALL
> + * in our Makefile.
> + */
> +#include <asm/ptrace.h>
> +#include <asm/hwcap.h>
> +
> +/* pasted from include/linux/stringify.h */
> +#define __stringify_1(x...)	#x
> +#define __stringify(x...)	__stringify_1(x)
> +
> +#define FEAT_SSBS		(1 << 0)
> +#define FEAT_PAN		(1 << 1)
> +#define FEAT_UAO		(1 << 2)

^ Comment to say what these are?

> +
> + /*
> +  * Reads a sysreg using the, possibly provided, S3_ encoding in order to
> +  * avoid inject any dependency on the used toolchain regarding possibly
> +  * still unsupported ARMv8 extensions.
> +  *
> +  * Using a standard mnemonic here to indicate the specific sysreg (like SSBS)
> +  * would introduce a compile-time dependency on possibly unsupported ARMv8
> +  * Extensions: you could end-up failing to build the test depending on the
> +  * available toolchain.
> +  * This is undesirable since some tests, even if specifically targeted at some
> +  * ARMv8 Extensions, can be plausibly run even on hardware lacking the above
> +  * optional ARM features. (SSBS bit preservation is an example: Kernel handles
> +  * it transparently not caring at all about the effective set of supported
> +  * features).
> +  * On the other side we will expect to observe different behaviours if the
> +  * feature is supported or not: usually getting a SIGILL when trying to use
> +  * unsupported features. For this reason we have anyway in place some
> +  * preliminary run-time checks about the cpu effectively supported features.
> +  *
> +  * This helper macro is meant to be used for regs readable at EL0, BUT some
> +  * EL1 sysregs are indeed readable too through MRS emulation Kernel-mechanism
> +  * if the required reg is included in the supported encoding space:
> +  *
> +  *  Documentation/arm64/cpu-feature-regsiters.txt
> +  *
> +  *  "The infrastructure emulates only the following system register space:
> +  *   	Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7
> +  */
> +#define get_regval(regname, out) \
> +	asm volatile("mrs %0, " __stringify(regname) : "=r" (out) :: "memory")
> +
> +/* Regs encoding and masks naming copied in from sysreg.h */
> +#define SYS_ID_AA64MMFR1_EL1	S3_0_C0_C7_1	/* MRS Emulated */
> +#define SYS_ID_AA64MMFR2_EL1	S3_0_C0_C7_2	/* MRS Emulated */
> +#define ID_AA64MMFR1_PAN_SHIFT	20
> +#define ID_AA64MMFR2_UAO_SHIFT	4
> +
> +/* Local Helpers */
> +#define IS_PAN_SUPPORTED(val) \
> +	(!!((val) & (0xf << ID_AA64MMFR1_PAN_SHIFT)))
> +#define IS_UAO_SUPPORTED(val) \
> +	(!!((val) & (0xf << ID_AA64MMFR2_UAO_SHIFT)))

These should probably be 0xfUL.  The ID regs are 64-bit, so a
shift >= 28 would cause things to go wrong here.

> +
> +#define MRS_SSBS_SYSREG		S3_3_C4_C2_6	/* EL0 supported */
> +#define MRS_SSBS_BIT		(1 << 12)
> +
> +/*
> + * A descriptor used to describe and configure a test case.
> + * Fields with a non-trivial meaning are described inline in the following.
> + */
> +struct tdescr {
> +	/* KEEP THIS FIELD FIRST for easier lookup from assembly */
> +	void		*token;
> +	/* when disabled token based sanity checking is skipped in handler */
> +	bool		sanity_disabled;
> +	/* just a name for the test-case; manadatory field */
> +	char		*name;
> +	char		*descr;
> +	unsigned long	feats_required;
> +	/* bitmask of effectively supported feats: populated at run-time */
> +	unsigned long	feats_supported;
> +	bool		feats_ok;
> +	bool		initialized;
> +	unsigned int	minsigstksz;
> +	/* signum used as a test trigger. Zero if no trigger-signal is used */
> +	int		sig_trig;
> +	/*
> +	 * signum considered as a successfull test completion.

successful

> +	 * Zero when no signal is expected on success
> +	 */
> +	int		sig_ok;

Could this be bool, or is it a signal number if nonzero?

> +	/* signum expected on unsupported CPU features. */
> +	int		sig_unsupp;
> +	/*
> +	 * used to grab a sigcontext from a signal handler
> +	 * automatically set to SIGUSR2 if not configured
> +	 */
> +	int		sig_copyctx;

Is there any reason for this to be customisable?

> +	/* a timeout in second for test completion */
> +	unsigned int	timeout;
> +	bool		triggered;
> +	unsigned int	handled;

bool, or otherwise what does the value for handled mean?

> +	bool		pass;
> +	/* optional sa_flags for the installed handler */
> +	int		sa_flags;
> +	ucontext_t	saved_uc;
> +	/* used by copy_ctx */
> +	ucontext_t	*live_uc;
> +	size_t		live_sz;
> +
> +	/* a setup function to be called before test starts */
> +	int (*setup)(struct tdescr *td);
> +	int (*cleanup)(struct tdescr *td);
> +
> +	/* an optional function to be used as a trigger for test starting */
> +	int (*trigger)(struct tdescr *td);
> +	/*
> +	 * the actual test-core: invoked differently depending on the
> +	 * presence of the trigger function above; this is mandatory
> +	 */
> +	int (*run)(struct tdescr *td, siginfo_t *si, ucontext_t *uc);
> +
> +	/* an optional function for custom results' processing */
> +	int (*check_result)(struct tdescr *td);
> +
> +	void *priv;
> +};
> +#endif
> diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c
> new file mode 100644
> index 000000000000..c00ba355dc1b
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c
> @@ -0,0 +1,256 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2019 ARM Limited */
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <signal.h>
> +#include <unistd.h>
> +#include <assert.h>
> +#include <sys/auxv.h>
> +#include <linux/auxvec.h>
> +
> +#include "test_signals.h"
> +#include "test_signals_utils.h"
> +#include "testcases/testcases.h"
> +
> +extern struct tdescr *current;
> +
> +void dump_uc(const char *prefix, ucontext_t *uc, int filter)

Should probably #include <ucontext.h> for this, even if we're getting
it via some other header.

> +{
> +	int i;
> +
> +	if (prefix)
> +		fprintf(stderr, "%s", prefix);
> +	if (filter & DUMP_REGS)
> +		for (i = 0; i < 29; i++)
> +			fprintf(stderr, "x%02d:%016llX\n",
> +				i, uc->uc_mcontext.regs[i]);
> +	if (filter & DUMP_FP)
> +		fprintf(stderr, "x%02d(fp):%016llX\n",
> +			i, uc->uc_mcontext.regs[i]);
> +	i++;
> +	if (filter & DUMP_LR)
> +		fprintf(stderr, "x%02d(lr):%016llX\n",
> +			i, uc->uc_mcontext.regs[i]);
> +	if (filter & DUMP_SP)
> +		fprintf(stderr, "sp:%016llX\n", uc->uc_mcontext.sp);
> +	if (filter & DUMP_PC)
> +		fprintf(stderr, "pc:%016llX\n", uc->uc_mcontext.pc);
> +	if (filter & DUMP_PSTATE)
> +		fprintf(stderr, "pstate:%016llX\n", uc->uc_mcontext.pstate);
> +	if (filter & DUMP_FAULT)
> +		fprintf(stderr, "fault_address:%016llX\n",
> +			uc->uc_mcontext.fault_address);
> +}
> +
> +static void unblock_signal(int signum)
> +{
> +	sigset_t sset;
> +
> +	sigemptyset(&sset);
> +	sigaddset(&sset, signum);
> +	sigprocmask(SIG_UNBLOCK, &sset, NULL);
> +}
> +
> +static int default_result(struct tdescr *td, int force_exit);

Can we move default_result() here to avoid this forward declaration?

> +
> +static void default_handler(int signum, siginfo_t *si, void *uc)
> +{
> +	if (signum == current->sig_trig) {
> +		SAFE_WRITE(2, "Handling SIG_TRIG\n");
> +		current->triggered = 1;
> +		/* ->run was asserted NON-NULL in test_setup() already */
> +		current->run(current, si, uc);
> +	} else if (signum == SIGILL && !current->initialized) {
> +		/*
> +		 * A SIGILL here while still not initialized means we fail
> +		 * to even asses the existence of feature
> +		 */
> +		SAFE_WRITE(1, "Marking UNSUPPORTED features.\n");
> +	} else if (current->sig_ok && signum == current->sig_ok) {
> +		/* it's a bug in the test code when this assert fail */
> +		assert(!current->sig_trig || current->triggered);
> +		if (!current->sanity_disabled) {
> +			fprintf(stderr,
> +				"SIG_OK -- si_addr@:0x%p  token@:0x%p\n",
> +				si->si_addr, current->token);
> +			assert(current->token);
> +		}
> +		SAFE_WRITE(2, "Handling SIG_OK\n");
> +		current->pass = 1;
> +		current->handled++;
> +		/*
> +		 * Some tests can lead to SEGV loops: in such a case we want
> +		 * to terminate immediately exiting straight away
> +		 */
> +		default_result(current, 1);
> +	} else if (current->sig_copyctx && signum == current->sig_copyctx) {
> +		memcpy(current->live_uc, uc, current->live_sz);
> +		ASSERT_GOOD_CONTEXT(current->live_uc);
> +		SAFE_WRITE(2,
> +			   "GOOD CONTEXT grabbed from sig_copyctx handler\n");
> +	} else {
> +		if (signum == current->sig_unsupp && !current->feats_ok) {
> +			SAFE_WRITE(2, "-- RX SIG_UNSUPP on unsupported feature...OK\n");
> +			current->pass = 1;
> +		} else if (signum == SIGALRM && current->timeout) {
> +			SAFE_WRITE(2, "-- Timeout !\n");
> +		} else {
> +			SAFE_WRITE(2, "-- UNEXPECTED SIGNAL\n");
> +		}
> +		default_result(current, 1);
> +	}
> +}
> +
> +static int default_setup(struct tdescr *td)
> +{
> +	struct sigaction sa;
> +
> +	sa.sa_sigaction = default_handler;
> +	sa.sa_flags = SA_SIGINFO;
> +	if (td->sa_flags)
> +		sa.sa_flags |= td->sa_flags;
> +	sigemptyset(&sa.sa_mask);
> +	/* uncatchable signals naturally skipped ... */
> +	for (int sig = 1; sig < 32; sig++)
> +		sigaction(sig, &sa, NULL);
> +	/*
> +	 * RT Signals default disposition is Term but they cannot be
> +	 * generated by the Kernel in response to our tests; so just catch
> +	 * them all and report them as UNEXPECTED signals.
> +	 */
> +	for (int sig = SIGRTMIN; sig <= SIGRTMAX; sig++)
> +		sigaction(sig, &sa, NULL);
> +
> +	/* just in case...unblock explicitly all we need */
> +	if (td->sig_trig)
> +		unblock_signal(td->sig_trig);
> +	if (td->sig_ok)
> +		unblock_signal(td->sig_ok);
> +	if (td->sig_unsupp)
> +		unblock_signal(td->sig_unsupp);
> +
> +	if (td->timeout) {
> +		unblock_signal(SIGALRM);
> +		alarm(td->timeout);
> +	}
> +	fprintf(stderr, "Registered handlers for all signals.\n");
> +
> +	return 1;
> +}
> +
> +static inline int default_trigger(struct tdescr *td)
> +{
> +	return !raise(td->sig_trig);
> +}
> +
> +static int default_result(struct tdescr *td, int force_exit)
> +{
> +	if (td->pass)
> +		SAFE_WRITE(2, "==>> completed. PASS(1)\n");
> +	else
> +		SAFE_WRITE(1, "==>> completed. FAIL(0)\n");
> +	if (!force_exit)
> +		return td->pass;
> +	else
> +		exit(td->pass ? EXIT_SUCCESS : EXIT_FAILURE);
> +}
> +
> +static int test_init(struct tdescr *td)
> +{
> +	td->minsigstksz = getauxval(AT_MINSIGSTKSZ);
> +	if (!td->minsigstksz)
> +		td->minsigstksz = MINSIGSTKSZ;
> +	fprintf(stderr, "Detected MINSTKSIGSZ:%d\n", td->minsigstksz);
> +
> +	if (td->feats_required) {
> +		td->feats_supported = 0;
> +		/*
> +		 * Checking for CPU required features using both the
> +		 * auxval and the arm64 MRS Emulation to read sysregs.
> +		 */
> +		if (getauxval(AT_HWCAP) & HWCAP_CPUID) {
> +			uint64_t val = 0;
> +
> +			if (td->feats_required & FEAT_SSBS) {
> +				if (getauxval(AT_HWCAP) & HWCAP_SSBS)
> +					td->feats_supported |= FEAT_SSBS;
> +			}
> +			if (td->feats_required & FEAT_PAN) {
> +				get_regval(SYS_ID_AA64MMFR1_EL1, val);
> +				if (IS_PAN_SUPPORTED(val))
> +					td->feats_supported |= FEAT_PAN;
> +			}
> +			if (td->feats_required & FEAT_UAO) {
> +				get_regval(SYS_ID_AA64MMFR2_EL1 , val);
> +				if (IS_UAO_SUPPORTED(val))
> +					td->feats_supported |= FEAT_UAO;
> +			}
> +		} else {
> +			fprintf(stderr,
> +				"CPUID regs NOT available. Marking features unsupported\n");
> +		}
> +		td->feats_ok = td->feats_required == td->feats_supported;
> +		fprintf(stderr, "Required Features %08lX are %ssupported\n",
> +		       td->feats_required, !td->feats_ok ? "NOT " : "");
> +	}
> +
> +	if (!td->sig_copyctx) {
> +		if (td->sig_trig != SIGUSR2)
> +			td->sig_copyctx = SIGUSR2;
> +		else if (td->sig_trig != SIGUSR1)
> +			td->sig_copyctx = SIGUSR1;
> +		else
> +			td->sig_copyctx = 0;
> +	}
> +
> +	if (td->sig_copyctx)
> +		unblock_signal(td->sig_copyctx);
> +
> +	td->initialized = 1;
> +	return 1;
> +}
> +
> +int test_setup(struct tdescr *td)
> +{
> +	/* assert core invariants symptom of a rotten testcase */
> +	assert(current);
> +	assert(td);
> +	assert(td->name);
> +	assert(td->run);
> +
> +	if (!test_init(td))
> +		return 0;
> +
> +	if (td->setup)
> +		return td->setup(td);
> +	else
> +		return default_setup(td);
> +}
> +
> +int test_run(struct tdescr *td)
> +{
> +	if (td->sig_trig) {
> +		if (td->trigger)
> +			return td->trigger(td);
> +		else
> +			return default_trigger(td);
> +	} else {
> +		return td->run(td, NULL, NULL);
> +	}
> +}
> +
> +int test_result(struct tdescr *td)
> +{
> +	if (td->check_result)
> +		td->check_result(td);
> +	return default_result(td, 0);
> +}
> +
> +int test_cleanup(struct tdescr *td)
> +{
> +	if (td->cleanup)
> +		return td->cleanup(td);
> +	else
> +		return 1;
> +}

Do we use the return value of either of these functions?  Should we?

> diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.h b/tools/testing/selftests/arm64/signal/test_signals_utils.h
> new file mode 100644
> index 000000000000..e7ebae8e8077
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.h
> @@ -0,0 +1,110 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (C) 2019 ARM Limited */
> +
> +#ifndef __TEST_SIGNALS_UTILS_H__
> +#define __TEST_SIGNALS_UTILS_H__
> +#include <stdio.h>
> +#include <signal.h>
> +#include <unistd.h>
> +#include <ucontext.h>
> +#include <string.h>
> +
> +#include <asm/unistd.h>
> +
> +#include "test_signals.h"
> +
> +#define DUMP_REGS	(1 << 0)
> +#define DUMP_FP		(1 << 1)
> +#define DUMP_LR		(1 << 2)
> +#define DUMP_SP		(1 << 3)
> +#define DUMP_PC		(1 << 4)
> +#define DUMP_PSTATE	(1 << 5)
> +#define DUMP_FAULT	(1 << 6)
> +#define DUMP_ALL	0xffffffff
> +
> +/* Using a signal-safe function to write something out */
> +#define SAFE_WRITE(fd, str) \
> +	do { \
> +		int bytes = 0; \
> +		bytes = write((fd), (str) + bytes, sizeof(str) - bytes); \
> +		if (bytes < 0 || bytes == sizeof(str)) \
> +			break; \

In the interest of readability, consider lining up the \ here.  Doesn't
matter that much though.

Also: won't we write the start of the string repeatedly here?  bytes is
never updated AFAICT.

> +	} while(1)

For block-like macros, I tend to write

#define FOO(bar) do {	\
	/* ... */	\
} while (0)

to save a line and reduce indentation.  This is a matter of style though.


Finally, does this need to be a macro?

To avoid surprises, we could use strlen() for the string length.  If the
compiler is smart enough and the function is inlined, then strlen("foo")
can be evaluated at compile time... but anyway, we are going to make
syscalls, so the cost of an out-of-line strlen() is not a big deal.

> +
> +/* Be careful this helper is NOT signal-safe */
> +void dump_uc(const char *prefix, ucontext_t *uc, int filter);
> +
> +int test_setup(struct tdescr *td);
> +int test_cleanup(struct tdescr *td);
> +int test_run(struct tdescr *td);
> +int test_result(struct tdescr *td);
> +int fake_sigreturn(void *sigframe, size_t sz, int alignment);
> +
> +/*
> + * Obtaining a valid and full-blown ucontext_t from userspace is tricky:
> + * libc getcontext does() not save all the regs and messes with some of
> + * them (pstate value in particular is not reliable).
> + * Here we use a service signal to grab the ucontext_t from inside a
> + * dedicated signal handler, since there, it is populated by Kernel
> + * itself in setup_sigframe(). The grabbed context is then stored and
> + * made available in td->live_uc.
> + *
> + * Anyway this function really serves a dual purpose:
> + *
> + * 1. grab a valid sigcontext into td->live_uc for result analysis: in
> + * such case it returns 1.
> + *
> + * 2. detect if somehow a previously grabbed live_uc context has been
> + * used actively with a sigreturn: in such a case the execution would have
> + * magically resumed in the middle of the function itself (seen_already==1):
> + * in such a case return 0, since in fact we have not just simply grabbed
> + * the context.
> + *
> + * This latter case is useful to detect when a fake_sigreturn test-case has
> + * unexpectedly survived without hittig a SEGV.
> + */
> +static inline __attribute__((always_inline))
> +int get_current_context(struct tdescr *td, ucontext_t *uc)
> +{
> +	static volatile int seen_already;

If there could be multiple users of this in the same program, it may
make sense to make seen_already part of td.

Strictly speaking, this should be sig_atomic_t, not int (I think
volatile int can't be torn into multiple accesses on any reasonable
arch, but it depends which toolchain folks you talk to...)

> +
> +	if (!td || !td->sig_copyctx || !uc) {
> +		SAFE_WRITE(1, "Signal-based Context dumping NOT available\n");
> +		return 0;
> +	}
> +
> +	/* it's a genuine invokation..reinit */
> +	seen_already = 0;
> +	td->live_uc = uc;
> +	td->live_sz = sizeof(*uc);
> +	memset(td->live_uc, 0x00, td->live_sz);

Is this required?  It is just here to help sanity-check whether live_uc
is populatd with real data?

> +	/*
> +	 * Grab ucontext_t triggering a signal...
> +	 * ASM equivalent of raise(td->sig_copyctx);
> +	 */
> +	asm volatile ("mov x8, %0\n\t"
> +		      "svc #0\n\t"
> +		      "mov x1, %1\n\t"
> +		      "mov x8, %2\n\t"
> +		      "svc #0" :

Nit: for readability, it's usual to put the : only at the start of lines
when writing an asm on multiple lines.

> +		      : "r" (__NR_getpid),
> +		        "r" (td->sig_copyctx),
> +			"r" (__NR_kill)

For the __NR_foo constants, you can use "i" to avoid wasting a register.

> +		      : "memory");

You need clobbers for x1 and x8, and also x0 (for the svc return value).

How does the compiler know that live_uc may be modified by this asm?

> +
> +	/*
> +	 * If we get here with seen_already==1 it implies the td->live_uc
> +	 * context has been used to get back here....this probably means
> +	 * a test has failed to cause a SEGV...anyway the live_uc has not
> +	 * just been acquired...so return 0
> +	 */
> +	if (seen_already) {
> +		SAFE_WRITE(1, "....and we're back....seen_already !\n");

Can we make this message more self-explanatory?

Say "Can't populate already-populated ucontext" or somethine like that?

> +		return 0;
> +	}
> +	seen_already = 1;
> +
> +	return 1;

Do we have a way to detect that live_uc really was populated?  I think
that if sig_copyctx was blocked or handled inappropriately due to a bug
elsewhere in the program, we could just fall through here and assume
that td->live_uc contains valid data.

If we have a flag in td to indicate that live_uc was populated, we may
be able to use it for this.

> +}
> +
> +#endif
> diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c
> new file mode 100644
> index 000000000000..9f83f3517325
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c
> @@ -0,0 +1,123 @@
> +#include "testcases.h"

Should <asm/sigcontext.h> be included for the arm64 sigcontext types
here?

> +
> +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
> +				size_t resv_sz, size_t *offset)
> +{
> +	size_t offs = 0;
> +
> +	while (head && head->magic != magic && offs < resv_sz - HDR_SZ) {

Could resv_sz be < HDR_SZ?

Should head ever be NULL?

> +		offs += head->size;
> +		head = GET_RESV_NEXT_HEAD(head);
> +	}
> +	if (!head || head->magic != magic)
> +		return NULL;
> +	else if (offset)
> +		*offset = offs;

This could be probably be simplified a little by returning from inside
the while loop when the match is found.  It works either way, though.

> +
> +	return head;
> +}
> +
> +bool validate_extra_context(struct extra_context *extra, char **err)
> +{
> +	struct _aarch64_ctx *term;
> +
> +	if (!extra || !err)
> +		return false;
> +
> +	fprintf(stderr, "Validating EXTRA...\n");
> +	term = GET_RESV_NEXT_HEAD(extra);
> +	if (!term || term->magic || term->size) {
> +		SET_CTX_ERR("UN-Terminated EXTRA context");
> +		return false;
> +	}
> +	if (extra->datap & ~0x10UL)
> +		SET_CTX_ERR("Extra DATAP misaligned");

Did you mean & ~0xfUL?

Alternatively, datap % 0x10 == 0.


> +	else if (extra->size & ~0x10UL)
> +		SET_CTX_ERR("Extra SIZE misaligned");

Similarly.

> +	else if (extra->datap != (uint64_t)term + sizeof(*term))
> +		SET_CTX_ERR("Extra DATAP broken");

Make this more informative?  Broken how?

> +	if (*err)
> +		return false;
> +
> +	fprintf(stderr, "GOOD EXTRA CONTEXT FOUND !\n");

Maybe we don't need to print anything on success here.

> +
> +	return true;
> +}
> +
> +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
> +{
> +	bool terminated = false;
> +	size_t offs = 0;
> +	int flags = 0;
> +	struct extra_context *extra = NULL;
> +	struct _aarch64_ctx *head =
> +		(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
> +
> +	if (!err)
> +		return false;
> +	/* Walk till the end terminator verifying __reserved contents */
> +	while (head && !terminated && offs < resv_sz) {
> +		if ((uint64_t)head & 0x0fUL) {
> +			SET_CTX_ERR("Misaligned HEAD");
> +			return false;
> +		}
> +
> +		switch (head->magic) {
> +			case 0:
> +				if (head->size)
> +					SET_CTX_ERR("Bad size for MAGIC0");

I suggest just assigning *err rather than hiding this assignment in a
macro, otherwise it's not clear that err (let alone *err) is touched
by this.

> +				else
> +					terminated = true;
> +				break;
> +			case FPSIMD_MAGIC:
> +				if (flags & FPSIMD_CTX)
> +					SET_CTX_ERR("Multiple FPSIMD");
> +				else if (head->size !=
> +					 sizeof(struct fpsimd_context))
> +					SET_CTX_ERR("Bad size for FPSIMD context");

Can we use the names from sigcontext.h?

(Like "fpsimd_context" or FPSIMD_MAGIC?)

For the null record at the end, I have tended to write "terminator
record" in the past.

> +				flags |= FPSIMD_CTX;
> +				break;
> +			case ESR_MAGIC:
> +				if (head->size != sizeof(struct esr_context))
> +					SET_CTX_ERR("Bad size for ESR context");
> +				break;
> +			case SVE_MAGIC:
> +				if (flags & SVE_CTX)
> +					SET_CTX_ERR("Multiple SVE");
> +				else if (head->size !=
> +					 sizeof(struct sve_context))
> +					SET_CTX_ERR("Bad size for SVE context");
> +				flags |= SVE_CTX;
> +				break;
> +			case EXTRA_MAGIC:
> +				if (flags & EXTRA_CTX)
> +					SET_CTX_ERR("Multiple EXTRA");
> +				else if (head->size !=
> +					 sizeof(struct extra_context))
> +					SET_CTX_ERR("Bad size for EXTRA context");
> +				flags |= EXTRA_CTX;
> +				extra = (struct extra_context *)head;
> +				break;
> +			default:
> +				SET_CTX_ERR("Unknown MAGIC !");

This probably shouldn't be an error.  If we hit this, it may mean that
the kernel is newer than the kselftest build, but we can go ahead and
test the records that we understand.

> +				break;
> +		}
> +
> +		if (*err)
> +			return false;

We should do some sanity-checks on size here: for forward progress
guarantees it should be >= sizeof(*head).  To avoid ending up
misaligned, it should also be a multiple of 16.

> +
> +		offs += head->size;
> +		if (resv_sz - offs < sizeof(*head)) {
> +			SET_CTX_ERR("Broken HEAD");

Maybe describe this an an overrun rather than a corrupt header.

> +			return false;
> +		}
> +
> +		if (flags & EXTRA_CTX)
> +			if (!validate_extra_context(extra, err))
> +				return false;
> +
> +		head = GET_RESV_NEXT_HEAD(head);
> +	}
> +
> +	return true;
> +}
> diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.h b/tools/testing/selftests/arm64/signal/testcases/testcases.h
> new file mode 100644
> index 000000000000..4f704c1501aa
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.h
> @@ -0,0 +1,85 @@
> +#ifndef __TESTCASES_H__
> +#define __TESTCASES_H__
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <ucontext.h>
> +
> +#include <asm/sigcontext.h>
> +

Add a comment to explain what these are?

> +#define FPSIMD_CTX	(1 << 0)
> +#define SVE_CTX		(1 << 1)
> +#define EXTRA_CTX	(1 << 2)
> +
> +#define HDR_SZ \
> +	sizeof(struct _aarch64_ctx)
> +
> +#define GET_SF_RESV_HEAD(sf) \
> +	(struct _aarch64_ctx *)(&sf.uc.uc_mcontext.__reserved)

((struct _aarch64_ctx *)&(sf).uc.uc_mcontext.__reserved)

> +
> +#define GET_SF_RESV_SIZE(sf) \
> +	sizeof(sf.uc.uc_mcontext.__reserved)

(sf)

> +
> +#define GET_UCP_RESV_SIZE(ucp) \
> +	sizeof(((ucontext_t *)ucp)->uc_mcontext.__reserved)

(ucp)

Ideally, lose the cast and require ucp to be the correct type.
Otherwise, the caller could pass any random type without the compiler
complaining here.

> +
> +#define ASSERT_BAD_CONTEXT(uc) \
> +	do { \
> +		char *err = NULL; \
> +		assert(!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)); \
> +		if (err) \
> +			fprintf(stderr, "Using badly built context - ERR: %s\n", err);\
> +	} while(0)

Using err implicitly may be confusing and make the code harder to
maintain.  Pass it in as an argument?

> +
> +#define ASSERT_GOOD_CONTEXT(uc) \
> +	do { \
> +		char *err = NULL; \
> +		if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \
> +			if (err) \
> +				fprintf(stderr, "Detected BAD context - ERR: %s\n", err);\
> +			assert(0); \
> +		} else { \
> +			fprintf(stderr, "uc context validated.\n"); \
> +		} \
> +	} while(0)
> +
> +/* head->size accounts both for payload and header _aarch64_ctx size ! */
> +#define GET_RESV_NEXT_HEAD(h) \
> +	(struct _aarch64_ctx *)((uint8_t *)(h) + (h)->size)

Pedantic nit: maybe use (char *) here.  The C standard is explicit about
the magic properties of character types, including sizeof(char) == 1.

We "know" that uint8_t the same as unsigned char, but superficially this
is just "whatever unsigned integer type is 8 bits in size".

> +
> +#define SET_CTX_ERR(str) \
> +	do { \
> +		if (err) \
> +			*err = str; \
> +	} while(0)
> +
> +struct a_sigframe {

Maybe fake_sigframe would be a better name?  It depends on how you use
this type, though.

> +	siginfo_t	info;
> +	ucontext_t	uc;
> +};
> +
> +
> +bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err);
> +
> +bool validate_extra_context(struct extra_context *extra, char **err);
> +
> +struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
> +				size_t resv_sz, size_t *offset);
> +
> +static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head,
> +						  size_t resv_sz,
> +						  size_t *offset)
> +{
> +	return get_header(head, 0, resv_sz, offset);
> +}
> +
> +static inline void write_terminator(struct _aarch64_ctx *tail)
> +{
> +	if (tail) {
> +		tail->magic = 0;
> +		tail->size = 0;
> +	}
> +}
> +#endif
> -- 
> 2.17.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@xxxxxxxxxxxxxxxxxxx
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux