alistair23-linux/tools/testing/selftests/powerpc/mm/wild_bctr.c
Gustavo Romero b2fed34a62 selftests/powerpc: Adjust wild_bctr to build with old binutils
Currently the selftest wild_bctr can fail to build when an old gcc is
used, notably on gcc using a binutils version <= 2.27, because the
assembler does not support the integer suffix UL.

This patch adjusts the wild_bctr test so the REG_POISON value is still
treated as an unsigned long for the shifts on compilation but the UL
suffix is absent on the stringification, so the inline asm code
generated has no UL suffixes.

Signed-off-by: Gustavo Romero <gromero@linux.vnet.ibm.com>
[mpe: Wrap long line]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
2018-11-15 23:05:17 +11:00

171 lines
3.4 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2018, Michael Ellerman, IBM Corp.
*
* Test that an out-of-bounds branch to counter behaves as expected.
*/
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ucontext.h>
#include <unistd.h>
#include "utils.h"
#define BAD_NIP 0x788c545a18000000ull
static struct pt_regs signal_regs;
static jmp_buf setjmp_env;
static void save_regs(ucontext_t *ctxt)
{
struct pt_regs *regs = ctxt->uc_mcontext.regs;
memcpy(&signal_regs, regs, sizeof(signal_regs));
}
static void segv_handler(int signum, siginfo_t *info, void *ctxt_v)
{
save_regs(ctxt_v);
longjmp(setjmp_env, 1);
}
static void usr2_handler(int signum, siginfo_t *info, void *ctxt_v)
{
save_regs(ctxt_v);
}
static int ok(void)
{
printf("Everything is OK in here.\n");
return 0;
}
#define REG_POISON 0x5a5a
#define POISONED_REG(n) ((((unsigned long)REG_POISON) << 48) | ((n) << 32) | \
(((unsigned long)REG_POISON) << 16) | (n))
static inline void poison_regs(void)
{
#define POISON_REG(n) \
"lis " __stringify(n) "," __stringify(REG_POISON) ";" \
"addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" \
"sldi " __stringify(n) "," __stringify(n) ", 32 ;" \
"oris " __stringify(n) "," __stringify(n) "," __stringify(REG_POISON) ";" \
"addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";"
asm (POISON_REG(15)
POISON_REG(16)
POISON_REG(17)
POISON_REG(18)
POISON_REG(19)
POISON_REG(20)
POISON_REG(21)
POISON_REG(22)
POISON_REG(23)
POISON_REG(24)
POISON_REG(25)
POISON_REG(26)
POISON_REG(27)
POISON_REG(28)
POISON_REG(29)
: // inputs
: // outputs
: "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25",
"26", "27", "28", "29"
);
#undef POISON_REG
}
static int check_regs(void)
{
unsigned long i;
for (i = 15; i <= 29; i++)
FAIL_IF(signal_regs.gpr[i] != POISONED_REG(i));
printf("Regs OK\n");
return 0;
}
static void dump_regs(void)
{
for (int i = 0; i < 32; i += 4) {
printf("r%02d 0x%016lx r%02d 0x%016lx " \
"r%02d 0x%016lx r%02d 0x%016lx\n",
i, signal_regs.gpr[i],
i+1, signal_regs.gpr[i+1],
i+2, signal_regs.gpr[i+2],
i+3, signal_regs.gpr[i+3]);
}
}
#ifdef _CALL_AIXDESC
struct opd {
unsigned long ip;
unsigned long toc;
unsigned long env;
};
static struct opd bad_opd = {
.ip = BAD_NIP,
};
#define BAD_FUNC (&bad_opd)
#else
#define BAD_FUNC BAD_NIP
#endif
int test_wild_bctr(void)
{
int (*func_ptr)(void);
struct sigaction segv = {
.sa_sigaction = segv_handler,
.sa_flags = SA_SIGINFO
};
struct sigaction usr2 = {
.sa_sigaction = usr2_handler,
.sa_flags = SA_SIGINFO
};
FAIL_IF(sigaction(SIGSEGV, &segv, NULL));
FAIL_IF(sigaction(SIGUSR2, &usr2, NULL));
bzero(&signal_regs, sizeof(signal_regs));
if (setjmp(setjmp_env) == 0) {
func_ptr = ok;
func_ptr();
kill(getpid(), SIGUSR2);
printf("Regs before:\n");
dump_regs();
bzero(&signal_regs, sizeof(signal_regs));
poison_regs();
func_ptr = (int (*)(void))BAD_FUNC;
func_ptr();
FAIL_IF(1); /* we didn't segv? */
}
FAIL_IF(signal_regs.nip != BAD_NIP);
printf("All good - took SEGV as expected branching to 0x%llx\n", BAD_NIP);
dump_regs();
FAIL_IF(check_regs());
return 0;
}
int main(void)
{
return test_harness(test_wild_bctr, "wild_bctr");
}