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