1  // SPDX-License-Identifier: GPL-2.0-or-later
2  /*
3   * Userspace test harness for load_unaligned_zeropad. Creates two
4   * pages and uses mprotect to prevent access to the second page and
5   * a SEGV handler that walks the exception tables and runs the fixup
6   * routine.
7   *
8   * The results are compared against a normal load that is that is
9   * performed while access to the second page is enabled via mprotect.
10   *
11   * Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM
12   */
13  
14  #include <stdlib.h>
15  #include <string.h>
16  #include <stdio.h>
17  #include <stdbool.h>
18  #include <signal.h>
19  #include <unistd.h>
20  #include <sys/mman.h>
21  
22  #define FIXUP_SECTION ".ex_fixup"
23  
24  static inline unsigned long __fls(unsigned long x);
25  
26  #include "word-at-a-time.h"
27  
28  #include "utils.h"
29  
__fls(unsigned long x)30  static inline unsigned long __fls(unsigned long x)
31  {
32  	int lz;
33  
34  	asm (PPC_CNTLZL "%0,%1" : "=r" (lz) : "r" (x));
35  	return sizeof(unsigned long) - 1 - lz;
36  }
37  
38  static int page_size;
39  static char *mem_region;
40  
protect_region(void)41  static int protect_region(void)
42  {
43  	if (mprotect(mem_region + page_size, page_size, PROT_NONE)) {
44  		perror("mprotect");
45  		return 1;
46  	}
47  
48  	return 0;
49  }
50  
unprotect_region(void)51  static int unprotect_region(void)
52  {
53  	if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) {
54  		perror("mprotect");
55  		return 1;
56  	}
57  
58  	return 0;
59  }
60  
61  extern char __start___ex_table[];
62  extern char __stop___ex_table[];
63  
64  struct extbl_entry {
65  	int insn;
66  	int fixup;
67  };
68  
segv_handler(int signr,siginfo_t * info,void * ptr)69  static void segv_handler(int signr, siginfo_t *info, void *ptr)
70  {
71  	ucontext_t *uc = (ucontext_t *)ptr;
72  	unsigned long addr = (unsigned long)info->si_addr;
73  	unsigned long *ip = &UCONTEXT_NIA(uc);
74  	struct extbl_entry *entry = (struct extbl_entry *)__start___ex_table;
75  
76  	while (entry < (struct extbl_entry *)__stop___ex_table) {
77  		unsigned long insn, fixup;
78  
79  		insn  = (unsigned long)&entry->insn + entry->insn;
80  		fixup = (unsigned long)&entry->fixup + entry->fixup;
81  
82  		if (insn == *ip) {
83  			*ip = fixup;
84  			return;
85  		}
86  	}
87  
88  	printf("No exception table match for NIA %lx ADDR %lx\n", *ip, addr);
89  	abort();
90  }
91  
setup_segv_handler(void)92  static void setup_segv_handler(void)
93  {
94  	struct sigaction action;
95  
96  	memset(&action, 0, sizeof(action));
97  	action.sa_sigaction = segv_handler;
98  	action.sa_flags = SA_SIGINFO;
99  	sigaction(SIGSEGV, &action, NULL);
100  }
101  
do_one_test(char * p,int page_offset)102  static int do_one_test(char *p, int page_offset)
103  {
104  	unsigned long should;
105  	unsigned long got;
106  
107  	FAIL_IF(unprotect_region());
108  	should = *(unsigned long *)p;
109  	FAIL_IF(protect_region());
110  
111  	got = load_unaligned_zeropad(p);
112  
113  	if (should != got) {
114  		printf("offset %u load_unaligned_zeropad returned 0x%lx, should be 0x%lx\n", page_offset, got, should);
115  		return 1;
116  	}
117  
118  	return 0;
119  }
120  
test_body(void)121  static int test_body(void)
122  {
123  	unsigned long i;
124  
125  	page_size = getpagesize();
126  	mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE,
127  		MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
128  
129  	FAIL_IF(mem_region == MAP_FAILED);
130  
131  	for (i = 0; i < page_size; i++)
132  		mem_region[i] = i;
133  
134  	memset(mem_region+page_size, 0, page_size);
135  
136  	setup_segv_handler();
137  
138  	for (i = 0; i < page_size; i++)
139  		FAIL_IF(do_one_test(mem_region+i, i));
140  
141  	return 0;
142  }
143  
main(void)144  int main(void)
145  {
146  	return test_harness(test_body, "load_unaligned_zeropad");
147  }
148