18c2ecf20Sopenharmony_ci#include <stdlib.h>
28c2ecf20Sopenharmony_ci#include <string.h>
38c2ecf20Sopenharmony_ci#include <stdio.h>
48c2ecf20Sopenharmony_ci#include <signal.h>
58c2ecf20Sopenharmony_ci#include <unistd.h>
68c2ecf20Sopenharmony_ci#include <sys/mman.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "utils.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciextern char __start___ex_table[];
118c2ecf20Sopenharmony_ciextern char __stop___ex_table[];
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#if defined(__powerpc64__)
148c2ecf20Sopenharmony_ci#define UCONTEXT_NIA(UC)	(UC)->uc_mcontext.gp_regs[PT_NIP]
158c2ecf20Sopenharmony_ci#elif defined(__powerpc__)
168c2ecf20Sopenharmony_ci#define UCONTEXT_NIA(UC)	(UC)->uc_mcontext.uc_regs->gregs[PT_NIP]
178c2ecf20Sopenharmony_ci#else
188c2ecf20Sopenharmony_ci#error implement UCONTEXT_NIA
198c2ecf20Sopenharmony_ci#endif
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void segv_handler(int signr, siginfo_t *info, void *ptr)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	ucontext_t *uc = (ucontext_t *)ptr;
248c2ecf20Sopenharmony_ci	unsigned long addr = (unsigned long)info->si_addr;
258c2ecf20Sopenharmony_ci	unsigned long *ip = &UCONTEXT_NIA(uc);
268c2ecf20Sopenharmony_ci	unsigned long *ex_p = (unsigned long *)__start___ex_table;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	while (ex_p < (unsigned long *)__stop___ex_table) {
298c2ecf20Sopenharmony_ci		unsigned long insn, fixup;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci		insn = *ex_p++;
328c2ecf20Sopenharmony_ci		fixup = *ex_p++;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci		if (insn == *ip) {
358c2ecf20Sopenharmony_ci			*ip = fixup;
368c2ecf20Sopenharmony_ci			return;
378c2ecf20Sopenharmony_ci		}
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr);
418c2ecf20Sopenharmony_ci	abort();
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void setup_segv_handler(void)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct sigaction action;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	memset(&action, 0, sizeof(action));
498c2ecf20Sopenharmony_ci	action.sa_sigaction = segv_handler;
508c2ecf20Sopenharmony_ci	action.sa_flags = SA_SIGINFO;
518c2ecf20Sopenharmony_ci	sigaction(SIGSEGV, &action, NULL);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ciunsigned long COPY_LOOP(void *to, const void *from, unsigned long size);
558c2ecf20Sopenharmony_ciunsigned long test_copy_tofrom_user_reference(void *to, const void *from, unsigned long size);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int total_passed;
588c2ecf20Sopenharmony_cistatic int total_failed;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void do_one_test(char *dstp, char *srcp, unsigned long len)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned long got, expected;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	got = COPY_LOOP(dstp, srcp, len);
658c2ecf20Sopenharmony_ci	expected = test_copy_tofrom_user_reference(dstp, srcp, len);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (got != expected) {
688c2ecf20Sopenharmony_ci		total_failed++;
698c2ecf20Sopenharmony_ci		printf("FAIL from=%p to=%p len=%ld returned %ld, expected %ld\n",
708c2ecf20Sopenharmony_ci		       srcp, dstp, len, got, expected);
718c2ecf20Sopenharmony_ci		//abort();
728c2ecf20Sopenharmony_ci	} else
738c2ecf20Sopenharmony_ci		total_passed++;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci//#define MAX_LEN 512
778c2ecf20Sopenharmony_ci#define MAX_LEN 16
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint test_copy_exception(void)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int page_size;
828c2ecf20Sopenharmony_ci	static char *p, *q;
838c2ecf20Sopenharmony_ci	unsigned long src, dst, len;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	page_size = getpagesize();
868c2ecf20Sopenharmony_ci	p = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE,
878c2ecf20Sopenharmony_ci		MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (p == MAP_FAILED) {
908c2ecf20Sopenharmony_ci		perror("mmap");
918c2ecf20Sopenharmony_ci		exit(1);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	memset(p, 0, page_size);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	setup_segv_handler();
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (mprotect(p + page_size, page_size, PROT_NONE)) {
998c2ecf20Sopenharmony_ci		perror("mprotect");
1008c2ecf20Sopenharmony_ci		exit(1);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	q = p + page_size - MAX_LEN;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	for (src = 0; src < MAX_LEN; src++) {
1068c2ecf20Sopenharmony_ci		for (dst = 0; dst < MAX_LEN; dst++) {
1078c2ecf20Sopenharmony_ci			for (len = 0; len < MAX_LEN+1; len++) {
1088c2ecf20Sopenharmony_ci				// printf("from=%p to=%p len=%ld\n", q+dst, q+src, len);
1098c2ecf20Sopenharmony_ci				do_one_test(q+dst, q+src, len);
1108c2ecf20Sopenharmony_ci			}
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	printf("Totals:\n");
1158c2ecf20Sopenharmony_ci	printf("  Pass: %d\n", total_passed);
1168c2ecf20Sopenharmony_ci	printf("  Fail: %d\n", total_failed);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciint main(void)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	return test_harness(test_copy_exception, str(COPY_LOOP));
1248c2ecf20Sopenharmony_ci}
125