Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Add test of load_unaligned_zero_pad()

It is a rarely exercised case, so we want to have a test to ensure it
works as required.

Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

+177 -2
+12 -1
arch/powerpc/include/asm/word-at-a-time.h
··· 116 116 117 117 #endif 118 118 119 + /* 120 + * We use load_unaligned_zero() in a selftest, which builds a userspace 121 + * program. Some linker scripts seem to discard the .fixup section, so allow 122 + * the test code to use a different section name. 123 + */ 124 + #ifndef FIXUP_SECTION 125 + #define FIXUP_SECTION ".fixup" 126 + #endif 127 + 119 128 static inline unsigned long load_unaligned_zeropad(const void *addr) 120 129 { 121 130 unsigned long ret, offset, tmp; ··· 132 123 asm( 133 124 "1: " PPC_LL "%[ret], 0(%[addr])\n" 134 125 "2:\n" 135 - ".section .fixup,\"ax\"\n" 126 + ".section " FIXUP_SECTION ",\"ax\"\n" 136 127 "3: " 137 128 #ifdef __powerpc64__ 138 129 "clrrdi %[tmp], %[addr], 3\n\t" ··· 164 155 165 156 return ret; 166 157 } 158 + 159 + #undef FIXUP_SECTION 167 160 168 161 #endif /* _ASM_WORD_AT_A_TIME_H */
+1 -1
tools/testing/selftests/powerpc/Makefile
··· 13 13 14 14 export CC CFLAGS 15 15 16 - TARGETS = pmu copyloops mm tm 16 + TARGETS = pmu copyloops mm tm primitives 17 17 18 18 endif 19 19
+17
tools/testing/selftests/powerpc/primitives/Makefile
··· 1 + CFLAGS += -I$(CURDIR) 2 + 3 + PROGS := load_unaligned_zeropad 4 + 5 + all: $(PROGS) 6 + 7 + $(PROGS): ../harness.c 8 + 9 + run_tests: all 10 + @-for PROG in $(PROGS); do \ 11 + ./$$PROG; \ 12 + done; 13 + 14 + clean: 15 + rm -f $(PROGS) *.o 16 + 17 + .PHONY: all run_tests clean
tools/testing/selftests/powerpc/primitives/asm/ppc-opcode.h
+147
tools/testing/selftests/powerpc/primitives/load_unaligned_zeropad.c
··· 1 + /* 2 + * Userspace test harness for load_unaligned_zeropad. Creates two 3 + * pages and uses mprotect to prevent access to the second page and 4 + * a SEGV handler that walks the exception tables and runs the fixup 5 + * routine. 6 + * 7 + * The results are compared against a normal load that is that is 8 + * performed while access to the second page is enabled via mprotect. 9 + * 10 + * Copyright (C) 2014 Anton Blanchard <anton@au.ibm.com>, IBM 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License 14 + * as published by the Free Software Foundation; either version 15 + * 2 of the License, or (at your option) any later version. 16 + */ 17 + 18 + #include <stdlib.h> 19 + #include <string.h> 20 + #include <stdio.h> 21 + #include <stdbool.h> 22 + #include <signal.h> 23 + #include <unistd.h> 24 + #include <sys/mman.h> 25 + 26 + #define FIXUP_SECTION ".ex_fixup" 27 + 28 + #include "word-at-a-time.h" 29 + 30 + #include "utils.h" 31 + 32 + 33 + static int page_size; 34 + static char *mem_region; 35 + 36 + static int protect_region(void) 37 + { 38 + if (mprotect(mem_region + page_size, page_size, PROT_NONE)) { 39 + perror("mprotect"); 40 + return 1; 41 + } 42 + 43 + return 0; 44 + } 45 + 46 + static int unprotect_region(void) 47 + { 48 + if (mprotect(mem_region + page_size, page_size, PROT_READ|PROT_WRITE)) { 49 + perror("mprotect"); 50 + return 1; 51 + } 52 + 53 + return 0; 54 + } 55 + 56 + extern char __start___ex_table[]; 57 + extern char __stop___ex_table[]; 58 + 59 + #if defined(__powerpc64__) 60 + #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.gp_regs[PT_NIP] 61 + #elif defined(__powerpc__) 62 + #define UCONTEXT_NIA(UC) (UC)->uc_mcontext.uc_regs->gregs[PT_NIP] 63 + #else 64 + #error implement UCONTEXT_NIA 65 + #endif 66 + 67 + static int segv_error; 68 + 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 + unsigned long *ex_p = (unsigned long *)__start___ex_table; 75 + 76 + while (ex_p < (unsigned long *)__stop___ex_table) { 77 + unsigned long insn, fixup; 78 + 79 + insn = *ex_p++; 80 + fixup = *ex_p++; 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 + segv_error++; 90 + } 91 + 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 + 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 + 116 + return 0; 117 + } 118 + 119 + static int test_body(void) 120 + { 121 + unsigned long i; 122 + 123 + page_size = getpagesize(); 124 + mem_region = mmap(NULL, page_size * 2, PROT_READ|PROT_WRITE, 125 + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 126 + 127 + FAIL_IF(mem_region == MAP_FAILED); 128 + 129 + for (i = 0; i < page_size; i++) 130 + mem_region[i] = i; 131 + 132 + memset(mem_region+page_size, 0, page_size); 133 + 134 + setup_segv_handler(); 135 + 136 + for (i = 0; i < page_size; i++) 137 + FAIL_IF(do_one_test(mem_region+i, i)); 138 + 139 + FAIL_IF(segv_error); 140 + 141 + return 0; 142 + } 143 + 144 + int main(void) 145 + { 146 + return test_harness(test_body, "load_unaligned_zeropad"); 147 + }