[LTP] [PATCH 12/29] Hugetlb: Migrating libhugetlbfs icache-hygiene
Cyril Hrubis
chrubis@suse.cz
Mon Oct 17 21:37:30 CEST 2022
Hi!
> Migrating the libhugetlbfs/testcases/icache-hygiene.c test
>
> Test Description: Older ppc64 kernels don't properly flush dcache to
> icache before giving a cleared page to userspace. With some exceedingly
> hairy code, this attempts to test for this bug.
>
> This test will never trigger (obviously) on machines with coherent
> icache and dcache (including x86 and POWER5). On any given run,
> even on a buggy kernel there's a chance the bug won't trigger -
> either because we don't get the same physical page back when we
> remap, or because the icache happens to get flushed in the interim.
>
> Signed-off-by: Tarun Sahu <tsahu@linux.ibm.com>
> ---
> runtest/hugetlb | 1 +
> testcases/kernel/mem/.gitignore | 1 +
> .../kernel/mem/hugetlb/hugemmap/hugemmap15.c | 250 ++++++++++++++++++
> 3 files changed, 252 insertions(+)
> create mode 100644 testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
>
> diff --git a/runtest/hugetlb b/runtest/hugetlb
> index 796ebe7fa..0714ed34c 100644
> --- a/runtest/hugetlb
> +++ b/runtest/hugetlb
> @@ -16,6 +16,7 @@ hugemmap11 hugemmap11
> hugemmap12 hugemmap12
> hugemmap13 hugemmap13
> hugemmap14 hugemmap14
> +hugemmap15 hugemmap15
> hugemmap05_1 hugemmap05 -m
> hugemmap05_2 hugemmap05 -s
> hugemmap05_3 hugemmap05 -s -m
> diff --git a/testcases/kernel/mem/.gitignore b/testcases/kernel/mem/.gitignore
> index 3106579ce..d59b60fd4 100644
> --- a/testcases/kernel/mem/.gitignore
> +++ b/testcases/kernel/mem/.gitignore
> @@ -15,6 +15,7 @@
> /hugetlb/hugemmap/hugemmap12
> /hugetlb/hugemmap/hugemmap13
> /hugetlb/hugemmap/hugemmap14
> +/hugetlb/hugemmap/hugemmap15
> /hugetlb/hugeshmat/hugeshmat01
> /hugetlb/hugeshmat/hugeshmat02
> /hugetlb/hugeshmat/hugeshmat03
> diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
> new file mode 100644
> index 000000000..cf1cd96ea
> --- /dev/null
> +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap15.c
> @@ -0,0 +1,250 @@
> +// SPDX-License-Identifier: LGPL-2.1-or-later
> +/*
> + * Copyright (C) 2005-2006 David Gibson & Adam Litke, IBM Corporation.
> + *
> + * Test Name: icache hygiene
> + *
> + * Test Description: Older ppc64 kernels don't properly flush dcache to
> + * icache before giving a cleared page to userspace. With some exceedingly
> + * hairy code, this attempts to test for this bug.
> + *
> + * This test will never trigger (obviously) on machines with coherent
> + * icache and dcache (including x86 and POWER5). On any given run,
> + * even on a buggy kernel there's a chance the bug won't trigger -
> + * either because we don't get the same physical page back when we
> + * remap, or because the icache happens to get flushed in the interim.
> + *
> + * HISTORY
> + * Written by David Gibson & Adam Litke
> + *
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <setjmp.h>
> +#include <unistd.h>
> +#include <signal.h>
> +#include <sys/mman.h>
> +#include <ucontext.h>
> +#include <sys/mount.h>
> +#include <limits.h>
> +#include <sys/param.h>
> +#include <sys/types.h>
> +
> +#include "hugetlb.h"
> +
> +static int fd = -1;
> +static char hfile[MAXPATHLEN];
> +
> +#define COPY_SIZE 128
> +#define NUM_REPETITIONS 64 /* Seems to be enough to trigger reliably */
> +
> +static char *verbose;
> +static long hpage_size;
> +
> +static void cacheflush(void *p)
> +{
> + (void)p;
> +#if defined(__powerpc__)
> + asm volatile("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r"(p));
> +#elif defined(__arm__) || defined(__aarch64__)
> + __clear_cache(p, p + COPY_SIZE);
> +#endif
> +}
> +
> +static void jumpfunc(int copy, void *p)
> +{
> + /* gcc bug workaround: if there is exactly one &&label
> + * construct in the function, gcc assumes the computed goto
> + * goes there, leading to the complete elision of the goto in
> + * this case
> + */
> + void *l = &&dummy;
> +
> + l = &&jumplabel;
> +
> + if (copy) {
> + memcpy(p, l, COPY_SIZE);
> + cacheflush(p);
> + }
> +
> + goto *p;
> + dummy:
> + tst_res(TWARN, "unreachable?");
> +
> + jumplabel:
> + return;
> +}
> +
> +static sigjmp_buf sig_escape;
> +static void *sig_expected;
> +
> +static void sig_handler(int signum, siginfo_t *si, void *uc)
> +{
> +#if defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || \
> + defined(__s390__) || defined(__s390x__) || defined(__sparc__) || \
> + defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
> + /* On powerpc, ia64, s390 and Aarch64, 0 bytes are an illegal
> + * instruction, so, if the icache is cleared properly, we SIGILL
> + * as soon as we jump into the cleared page
> + */
> + if (signum == SIGILL) {
> + if (verbose)
> + tst_res(TINFO, "SIGILL at %p (sig_expected=%p)", si->si_addr,
> + sig_expected);
> + if (si->si_addr == sig_expected)
> + siglongjmp(sig_escape, 1);
> + tst_res(TFAIL, "SIGILL somewhere unexpected");
> + goto fail;
> + }
> +#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
> + /* On x86, zero bytes form a valid instruction:
> + * add %al,(%eax) (i386)
> + * or add %al,(%rax) (x86_64)
> + *
> + * So, behaviour depends on the contents of [ER]AX, which in
> + * turn depends on the details of code generation. If [ER]AX
> + * contains a valid pointer, we will execute the instruction
> + * repeatedly until we run off that hugepage and get a SIGBUS
> + * on the second, truncated page. If [ER]AX does not contain
> + * a valid pointer, we will SEGV on the first instruction in
> + * the cleared page. We check for both possibilities
> + * below.
> + *
> + * On 32 bit ARM, zero bytes are interpreted as follows:
> + * andeq r0, r0, r0 (ARM state, 4 bytes)
> + * movs r0, r0 (Thumb state, 2 bytes)
> + *
> + * So, we only expect to run off the end of the huge page and
> + * generate a SIGBUS.
> + */
> + if (signum == SIGBUS) {
> + if (verbose)
> + tst_res(TINFO, "SIGBUS at %p (sig_expected=%p)", si->si_addr,
> + sig_expected);
> + if (sig_expected
> + && (ALIGN((unsigned long)sig_expected, hpage_size)
> + == (unsigned long)si->si_addr)) {
> + siglongjmp(sig_escape, 2);
> + }
> + tst_res(TFAIL, "SIGBUS somewhere unexpected");
> + goto fail;
> + }
> +#if defined(__x86_64__) || defined(__i386__)
> + if (signum == SIGSEGV) {
> +#ifdef __x86_64__
> + void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_RIP];
> +#else
> + void *pc = (void *)((ucontext_t *)uc)->uc_mcontext.gregs[REG_EIP];
> +#endif
> + if (verbose)
> + tst_res(TINFO, "SIGSEGV at %p, PC=%p (sig_expected=%p)\n",
> + si->si_addr, pc, sig_expected);
> + if (sig_expected == pc)
> + siglongjmp(sig_escape, 1);
> + tst_res(TFAIL, "SIGSEGV somewhere unexpected");
> + goto fail;
> + }
> +#endif
> +#else
> +#error "Need to setup signal conditions for this arch"
> +#endif
> + return;
> +fail:
> + tst_brk(TBROK, "Once Failed, No Need to continue the next iteration.");
> +}
Again no tst_res() or tst_brk() is allowed inside a signal handler, the
best we can is to propagate different values for pass/fail in the return
value from siglongjmp().
And the usuall comments apply to this test as well.
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list