162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2018, Michael Ellerman, IBM Corp.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Test that an out-of-bounds branch to counter behaves as expected.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <setjmp.h>
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <sys/mman.h>
1362306a36Sopenharmony_ci#include <sys/types.h>
1462306a36Sopenharmony_ci#include <sys/wait.h>
1562306a36Sopenharmony_ci#include <ucontext.h>
1662306a36Sopenharmony_ci#include <unistd.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "utils.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define BAD_NIP	0x788c545a18000000ull
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct pt_regs signal_regs;
2462306a36Sopenharmony_cistatic jmp_buf setjmp_env;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void save_regs(ucontext_t *ctxt)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct pt_regs *regs = ctxt->uc_mcontext.regs;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	memcpy(&signal_regs, regs, sizeof(signal_regs));
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic void segv_handler(int signum, siginfo_t *info, void *ctxt_v)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	save_regs(ctxt_v);
3662306a36Sopenharmony_ci	longjmp(setjmp_env, 1);
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic void usr2_handler(int signum, siginfo_t *info, void *ctxt_v)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	save_regs(ctxt_v);
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int ok(void)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	printf("Everything is OK in here.\n");
4762306a36Sopenharmony_ci	return 0;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#define REG_POISON	0x5a5a
5162306a36Sopenharmony_ci#define POISONED_REG(n)	((((unsigned long)REG_POISON) << 48) | ((n) << 32) | \
5262306a36Sopenharmony_ci			 (((unsigned long)REG_POISON) << 16) | (n))
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic inline void poison_regs(void)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	#define POISON_REG(n)	\
5762306a36Sopenharmony_ci	  "lis  " __stringify(n) "," __stringify(REG_POISON) ";" \
5862306a36Sopenharmony_ci	  "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";" \
5962306a36Sopenharmony_ci	  "sldi " __stringify(n) "," __stringify(n) ", 32 ;" \
6062306a36Sopenharmony_ci	  "oris " __stringify(n) "," __stringify(n) "," __stringify(REG_POISON) ";" \
6162306a36Sopenharmony_ci	  "addi " __stringify(n) "," __stringify(n) "," __stringify(n) ";"
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	asm (POISON_REG(15)
6462306a36Sopenharmony_ci	     POISON_REG(16)
6562306a36Sopenharmony_ci	     POISON_REG(17)
6662306a36Sopenharmony_ci	     POISON_REG(18)
6762306a36Sopenharmony_ci	     POISON_REG(19)
6862306a36Sopenharmony_ci	     POISON_REG(20)
6962306a36Sopenharmony_ci	     POISON_REG(21)
7062306a36Sopenharmony_ci	     POISON_REG(22)
7162306a36Sopenharmony_ci	     POISON_REG(23)
7262306a36Sopenharmony_ci	     POISON_REG(24)
7362306a36Sopenharmony_ci	     POISON_REG(25)
7462306a36Sopenharmony_ci	     POISON_REG(26)
7562306a36Sopenharmony_ci	     POISON_REG(27)
7662306a36Sopenharmony_ci	     POISON_REG(28)
7762306a36Sopenharmony_ci	     POISON_REG(29)
7862306a36Sopenharmony_ci	     : // inputs
7962306a36Sopenharmony_ci	     : // outputs
8062306a36Sopenharmony_ci	     : "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25",
8162306a36Sopenharmony_ci	       "26", "27", "28", "29"
8262306a36Sopenharmony_ci	);
8362306a36Sopenharmony_ci	#undef POISON_REG
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int check_regs(void)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	unsigned long i;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	for (i = 15; i <= 29; i++)
9162306a36Sopenharmony_ci		FAIL_IF(signal_regs.gpr[i] != POISONED_REG(i));
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	printf("Regs OK\n");
9462306a36Sopenharmony_ci	return 0;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void dump_regs(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	for (int i = 0; i < 32; i += 4) {
10062306a36Sopenharmony_ci		printf("r%02d 0x%016lx  r%02d 0x%016lx  " \
10162306a36Sopenharmony_ci		       "r%02d 0x%016lx  r%02d 0x%016lx\n",
10262306a36Sopenharmony_ci		       i, signal_regs.gpr[i],
10362306a36Sopenharmony_ci		       i+1, signal_regs.gpr[i+1],
10462306a36Sopenharmony_ci		       i+2, signal_regs.gpr[i+2],
10562306a36Sopenharmony_ci		       i+3, signal_regs.gpr[i+3]);
10662306a36Sopenharmony_ci	}
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#ifdef _CALL_AIXDESC
11062306a36Sopenharmony_cistruct opd {
11162306a36Sopenharmony_ci	unsigned long ip;
11262306a36Sopenharmony_ci	unsigned long toc;
11362306a36Sopenharmony_ci	unsigned long env;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_cistatic struct opd bad_opd = {
11662306a36Sopenharmony_ci	.ip = BAD_NIP,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci#define BAD_FUNC (&bad_opd)
11962306a36Sopenharmony_ci#else
12062306a36Sopenharmony_ci#define BAD_FUNC BAD_NIP
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciint test_wild_bctr(void)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int (*func_ptr)(void);
12662306a36Sopenharmony_ci	struct sigaction segv = {
12762306a36Sopenharmony_ci		.sa_sigaction = segv_handler,
12862306a36Sopenharmony_ci		.sa_flags = SA_SIGINFO
12962306a36Sopenharmony_ci	};
13062306a36Sopenharmony_ci	struct sigaction usr2 = {
13162306a36Sopenharmony_ci		.sa_sigaction = usr2_handler,
13262306a36Sopenharmony_ci		.sa_flags = SA_SIGINFO
13362306a36Sopenharmony_ci	};
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	FAIL_IF(sigaction(SIGSEGV, &segv, NULL));
13662306a36Sopenharmony_ci	FAIL_IF(sigaction(SIGUSR2, &usr2, NULL));
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	bzero(&signal_regs, sizeof(signal_regs));
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (setjmp(setjmp_env) == 0) {
14162306a36Sopenharmony_ci		func_ptr = ok;
14262306a36Sopenharmony_ci		func_ptr();
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		kill(getpid(), SIGUSR2);
14562306a36Sopenharmony_ci		printf("Regs before:\n");
14662306a36Sopenharmony_ci		dump_regs();
14762306a36Sopenharmony_ci		bzero(&signal_regs, sizeof(signal_regs));
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		poison_regs();
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		func_ptr = (int (*)(void))BAD_FUNC;
15262306a36Sopenharmony_ci		func_ptr();
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		FAIL_IF(1); /* we didn't segv? */
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	FAIL_IF(signal_regs.nip != BAD_NIP);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	printf("All good - took SEGV as expected branching to 0x%llx\n", BAD_NIP);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dump_regs();
16262306a36Sopenharmony_ci	FAIL_IF(check_regs());
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ciint main(void)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return test_harness(test_wild_bctr, "wild_bctr");
17062306a36Sopenharmony_ci}
171