diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-19 14:31:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-19 14:31:38 -0400 |
commit | e5a489abcfd216d07ad6b33ea0d191e61d0f25ea (patch) | |
tree | 2d56b24cfc559c8138a72d4ef52b45c2f2d536d4 | |
parent | 8b4822de59d5d9919b9b045183a36c673ce20b73 (diff) | |
parent | e41e53cd4fe331d0d1f06f8e4ed7e2cc63ee2c34 (diff) |
Merge tag 'powerpc-4.12-3' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux
Pull powerpc fixes from Michael Ellerman:
"The headliner is a fix for FP/VMX register corruption when using
transactional memory, and a new selftest to go with it.
Then there's the virt_addr_valid() fix, currently HARDENDED_USERCOPY
is tripping on that causing some machines to crash.
A few other fairly minor fixes for long tail things, and a couple of
fixes for code we just merged.
Thanks to: Breno Leitao, Gautham Shenoy, Michael Neuling, Naveen Rao.
Nicholas Piggin, Paul Mackerras"
* tag 'powerpc-4.12-3' of git://git.kernel.org/pub/scm/linux/kernel/git/powerpc/linux:
powerpc/mm: Fix virt_addr_valid() etc. on 64-bit hash
powerpc/mm: Fix crash in page table dump with huge pages
powerpc/kprobes: Fix handling of instruction emulation on probe re-entry
powerpc/powernv: Set NAPSTATELOST after recovering paca on P9 DD1
selftests/powerpc: Test TM and VMX register state
powerpc/tm: Fix FP and VMX register corruption
powerpc/modules: If mprofile-kernel is enabled add it to vermagic
-rw-r--r-- | arch/powerpc/include/asm/module.h | 4 | ||||
-rw-r--r-- | arch/powerpc/include/asm/page.h | 12 | ||||
-rw-r--r-- | arch/powerpc/kernel/idle_book3s.S | 2 | ||||
-rw-r--r-- | arch/powerpc/kernel/kprobes.c | 3 | ||||
-rw-r--r-- | arch/powerpc/kernel/process.c | 19 | ||||
-rw-r--r-- | arch/powerpc/mm/dump_linuxpagetables.c | 7 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/.gitignore | 1 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/Makefile | 4 | ||||
-rw-r--r-- | tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c | 118 |
9 files changed, 164 insertions, 6 deletions
diff --git a/arch/powerpc/include/asm/module.h b/arch/powerpc/include/asm/module.h index 53885512b8d3..6c0132c7212f 100644 --- a/arch/powerpc/include/asm/module.h +++ b/arch/powerpc/include/asm/module.h | |||
@@ -14,6 +14,10 @@ | |||
14 | #include <asm-generic/module.h> | 14 | #include <asm-generic/module.h> |
15 | 15 | ||
16 | 16 | ||
17 | #ifdef CC_USING_MPROFILE_KERNEL | ||
18 | #define MODULE_ARCH_VERMAGIC "mprofile-kernel" | ||
19 | #endif | ||
20 | |||
17 | #ifndef __powerpc64__ | 21 | #ifndef __powerpc64__ |
18 | /* | 22 | /* |
19 | * Thanks to Paul M for explaining this. | 23 | * Thanks to Paul M for explaining this. |
diff --git a/arch/powerpc/include/asm/page.h b/arch/powerpc/include/asm/page.h index 2a32483c7b6c..8da5d4c1cab2 100644 --- a/arch/powerpc/include/asm/page.h +++ b/arch/powerpc/include/asm/page.h | |||
@@ -132,7 +132,19 @@ extern long long virt_phys_offset; | |||
132 | #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) | 132 | #define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT) |
133 | #define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr)) | 133 | #define virt_to_page(kaddr) pfn_to_page(virt_to_pfn(kaddr)) |
134 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) | 134 | #define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT) |
135 | |||
136 | #ifdef CONFIG_PPC_BOOK3S_64 | ||
137 | /* | ||
138 | * On hash the vmalloc and other regions alias to the kernel region when passed | ||
139 | * through __pa(), which virt_to_pfn() uses. That means virt_addr_valid() can | ||
140 | * return true for some vmalloc addresses, which is incorrect. So explicitly | ||
141 | * check that the address is in the kernel region. | ||
142 | */ | ||
143 | #define virt_addr_valid(kaddr) (REGION_ID(kaddr) == KERNEL_REGION_ID && \ | ||
144 | pfn_valid(virt_to_pfn(kaddr))) | ||
145 | #else | ||
135 | #define virt_addr_valid(kaddr) pfn_valid(virt_to_pfn(kaddr)) | 146 | #define virt_addr_valid(kaddr) pfn_valid(virt_to_pfn(kaddr)) |
147 | #endif | ||
136 | 148 | ||
137 | /* | 149 | /* |
138 | * On Book-E parts we need __va to parse the device tree and we can't | 150 | * On Book-E parts we need __va to parse the device tree and we can't |
diff --git a/arch/powerpc/kernel/idle_book3s.S b/arch/powerpc/kernel/idle_book3s.S index 07d4e0ad60db..4898d676dcae 100644 --- a/arch/powerpc/kernel/idle_book3s.S +++ b/arch/powerpc/kernel/idle_book3s.S | |||
@@ -416,7 +416,7 @@ power9_dd1_recover_paca: | |||
416 | * which needs to be restored from the stack. | 416 | * which needs to be restored from the stack. |
417 | */ | 417 | */ |
418 | li r3, 1 | 418 | li r3, 1 |
419 | stb r0,PACA_NAPSTATELOST(r13) | 419 | stb r3,PACA_NAPSTATELOST(r13) |
420 | blr | 420 | blr |
421 | 421 | ||
422 | /* | 422 | /* |
diff --git a/arch/powerpc/kernel/kprobes.c b/arch/powerpc/kernel/kprobes.c index 160ae0fa7d0d..fc4343514bed 100644 --- a/arch/powerpc/kernel/kprobes.c +++ b/arch/powerpc/kernel/kprobes.c | |||
@@ -305,16 +305,17 @@ int kprobe_handler(struct pt_regs *regs) | |||
305 | save_previous_kprobe(kcb); | 305 | save_previous_kprobe(kcb); |
306 | set_current_kprobe(p, regs, kcb); | 306 | set_current_kprobe(p, regs, kcb); |
307 | kprobes_inc_nmissed_count(p); | 307 | kprobes_inc_nmissed_count(p); |
308 | prepare_singlestep(p, regs); | ||
309 | kcb->kprobe_status = KPROBE_REENTER; | 308 | kcb->kprobe_status = KPROBE_REENTER; |
310 | if (p->ainsn.boostable >= 0) { | 309 | if (p->ainsn.boostable >= 0) { |
311 | ret = try_to_emulate(p, regs); | 310 | ret = try_to_emulate(p, regs); |
312 | 311 | ||
313 | if (ret > 0) { | 312 | if (ret > 0) { |
314 | restore_previous_kprobe(kcb); | 313 | restore_previous_kprobe(kcb); |
314 | preempt_enable_no_resched(); | ||
315 | return 1; | 315 | return 1; |
316 | } | 316 | } |
317 | } | 317 | } |
318 | prepare_singlestep(p, regs); | ||
318 | return 1; | 319 | return 1; |
319 | } else { | 320 | } else { |
320 | if (*addr != BREAKPOINT_INSTRUCTION) { | 321 | if (*addr != BREAKPOINT_INSTRUCTION) { |
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index d645da302bf2..baae104b16c7 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c | |||
@@ -864,6 +864,25 @@ static void tm_reclaim_thread(struct thread_struct *thr, | |||
864 | if (!MSR_TM_SUSPENDED(mfmsr())) | 864 | if (!MSR_TM_SUSPENDED(mfmsr())) |
865 | return; | 865 | return; |
866 | 866 | ||
867 | /* | ||
868 | * If we are in a transaction and FP is off then we can't have | ||
869 | * used FP inside that transaction. Hence the checkpointed | ||
870 | * state is the same as the live state. We need to copy the | ||
871 | * live state to the checkpointed state so that when the | ||
872 | * transaction is restored, the checkpointed state is correct | ||
873 | * and the aborted transaction sees the correct state. We use | ||
874 | * ckpt_regs.msr here as that's what tm_reclaim will use to | ||
875 | * determine if it's going to write the checkpointed state or | ||
876 | * not. So either this will write the checkpointed registers, | ||
877 | * or reclaim will. Similarly for VMX. | ||
878 | */ | ||
879 | if ((thr->ckpt_regs.msr & MSR_FP) == 0) | ||
880 | memcpy(&thr->ckfp_state, &thr->fp_state, | ||
881 | sizeof(struct thread_fp_state)); | ||
882 | if ((thr->ckpt_regs.msr & MSR_VEC) == 0) | ||
883 | memcpy(&thr->ckvr_state, &thr->vr_state, | ||
884 | sizeof(struct thread_vr_state)); | ||
885 | |||
867 | giveup_all(container_of(thr, struct task_struct, thread)); | 886 | giveup_all(container_of(thr, struct task_struct, thread)); |
868 | 887 | ||
869 | tm_reclaim(thr, thr->ckpt_regs.msr, cause); | 888 | tm_reclaim(thr, thr->ckpt_regs.msr, cause); |
diff --git a/arch/powerpc/mm/dump_linuxpagetables.c b/arch/powerpc/mm/dump_linuxpagetables.c index d659345a98d6..44fe4833910f 100644 --- a/arch/powerpc/mm/dump_linuxpagetables.c +++ b/arch/powerpc/mm/dump_linuxpagetables.c | |||
@@ -16,6 +16,7 @@ | |||
16 | */ | 16 | */ |
17 | #include <linux/debugfs.h> | 17 | #include <linux/debugfs.h> |
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <linux/hugetlb.h> | ||
19 | #include <linux/io.h> | 20 | #include <linux/io.h> |
20 | #include <linux/mm.h> | 21 | #include <linux/mm.h> |
21 | #include <linux/sched.h> | 22 | #include <linux/sched.h> |
@@ -391,7 +392,7 @@ static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start) | |||
391 | 392 | ||
392 | for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { | 393 | for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { |
393 | addr = start + i * PMD_SIZE; | 394 | addr = start + i * PMD_SIZE; |
394 | if (!pmd_none(*pmd)) | 395 | if (!pmd_none(*pmd) && !pmd_huge(*pmd)) |
395 | /* pmd exists */ | 396 | /* pmd exists */ |
396 | walk_pte(st, pmd, addr); | 397 | walk_pte(st, pmd, addr); |
397 | else | 398 | else |
@@ -407,7 +408,7 @@ static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start) | |||
407 | 408 | ||
408 | for (i = 0; i < PTRS_PER_PUD; i++, pud++) { | 409 | for (i = 0; i < PTRS_PER_PUD; i++, pud++) { |
409 | addr = start + i * PUD_SIZE; | 410 | addr = start + i * PUD_SIZE; |
410 | if (!pud_none(*pud)) | 411 | if (!pud_none(*pud) && !pud_huge(*pud)) |
411 | /* pud exists */ | 412 | /* pud exists */ |
412 | walk_pmd(st, pud, addr); | 413 | walk_pmd(st, pud, addr); |
413 | else | 414 | else |
@@ -427,7 +428,7 @@ static void walk_pagetables(struct pg_state *st) | |||
427 | */ | 428 | */ |
428 | for (i = 0; i < PTRS_PER_PGD; i++, pgd++) { | 429 | for (i = 0; i < PTRS_PER_PGD; i++, pgd++) { |
429 | addr = KERN_VIRT_START + i * PGDIR_SIZE; | 430 | addr = KERN_VIRT_START + i * PGDIR_SIZE; |
430 | if (!pgd_none(*pgd)) | 431 | if (!pgd_none(*pgd) && !pgd_huge(*pgd)) |
431 | /* pgd exists */ | 432 | /* pgd exists */ |
432 | walk_pud(st, pgd, addr); | 433 | walk_pud(st, pgd, addr); |
433 | else | 434 | else |
diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index 427621792229..2f1f7b013293 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore | |||
@@ -11,3 +11,4 @@ tm-signal-context-chk-fpu | |||
11 | tm-signal-context-chk-gpr | 11 | tm-signal-context-chk-gpr |
12 | tm-signal-context-chk-vmx | 12 | tm-signal-context-chk-vmx |
13 | tm-signal-context-chk-vsx | 13 | tm-signal-context-chk-vsx |
14 | tm-vmx-unavail | ||
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 5576ee6a51f2..958c11c14acd 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile | |||
@@ -2,7 +2,8 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu | |||
2 | tm-signal-context-chk-vmx tm-signal-context-chk-vsx | 2 | tm-signal-context-chk-vmx tm-signal-context-chk-vsx |
3 | 3 | ||
4 | TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ | 4 | TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ |
5 | tm-vmxcopy tm-fork tm-tar tm-tmspr $(SIGNAL_CONTEXT_CHK_TESTS) | 5 | tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail \ |
6 | $(SIGNAL_CONTEXT_CHK_TESTS) | ||
6 | 7 | ||
7 | include ../../lib.mk | 8 | include ../../lib.mk |
8 | 9 | ||
@@ -13,6 +14,7 @@ CFLAGS += -mhtm | |||
13 | $(OUTPUT)/tm-syscall: tm-syscall-asm.S | 14 | $(OUTPUT)/tm-syscall: tm-syscall-asm.S |
14 | $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include | 15 | $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include |
15 | $(OUTPUT)/tm-tmspr: CFLAGS += -pthread | 16 | $(OUTPUT)/tm-tmspr: CFLAGS += -pthread |
17 | $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 | ||
16 | 18 | ||
17 | SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) | 19 | SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) |
18 | $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S | 20 | $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S |
diff --git a/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c b/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c new file mode 100644 index 000000000000..137185ba4937 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-vmx-unavail.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * Copyright 2017, Michael Neuling, IBM Corp. | ||
3 | * Licensed under GPLv2. | ||
4 | * Original: Breno Leitao <brenohl@br.ibm.com> & | ||
5 | * Gustavo Bueno Romero <gromero@br.ibm.com> | ||
6 | * Edited: Michael Neuling | ||
7 | * | ||
8 | * Force VMX unavailable during a transaction and see if it corrupts | ||
9 | * the checkpointed VMX register state after the abort. | ||
10 | */ | ||
11 | |||
12 | #include <inttypes.h> | ||
13 | #include <htmintrin.h> | ||
14 | #include <string.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <stdio.h> | ||
17 | #include <pthread.h> | ||
18 | #include <sys/mman.h> | ||
19 | #include <unistd.h> | ||
20 | #include <pthread.h> | ||
21 | |||
22 | #include "tm.h" | ||
23 | #include "utils.h" | ||
24 | |||
25 | int passed; | ||
26 | |||
27 | void *worker(void *unused) | ||
28 | { | ||
29 | __int128 vmx0; | ||
30 | uint64_t texasr; | ||
31 | |||
32 | asm goto ( | ||
33 | "li 3, 1;" /* Stick non-zero value in VMX0 */ | ||
34 | "std 3, 0(%[vmx0_ptr]);" | ||
35 | "lvx 0, 0, %[vmx0_ptr];" | ||
36 | |||
37 | /* Wait here a bit so we get scheduled out 255 times */ | ||
38 | "lis 3, 0x3fff;" | ||
39 | "1: ;" | ||
40 | "addi 3, 3, -1;" | ||
41 | "cmpdi 3, 0;" | ||
42 | "bne 1b;" | ||
43 | |||
44 | /* Kernel will hopefully turn VMX off now */ | ||
45 | |||
46 | "tbegin. ;" | ||
47 | "beq failure;" | ||
48 | |||
49 | /* Cause VMX unavail. Any VMX instruction */ | ||
50 | "vaddcuw 0,0,0;" | ||
51 | |||
52 | "tend. ;" | ||
53 | "b %l[success];" | ||
54 | |||
55 | /* Check VMX0 sanity after abort */ | ||
56 | "failure: ;" | ||
57 | "lvx 1, 0, %[vmx0_ptr];" | ||
58 | "vcmpequb. 2, 0, 1;" | ||
59 | "bc 4, 24, %l[value_mismatch];" | ||
60 | "b %l[value_match];" | ||
61 | : | ||
62 | : [vmx0_ptr] "r"(&vmx0) | ||
63 | : "r3" | ||
64 | : success, value_match, value_mismatch | ||
65 | ); | ||
66 | |||
67 | /* HTM aborted and VMX0 is corrupted */ | ||
68 | value_mismatch: | ||
69 | texasr = __builtin_get_texasr(); | ||
70 | |||
71 | printf("\n\n==============\n\n"); | ||
72 | printf("Failure with error: %lx\n", _TEXASR_FAILURE_CODE(texasr)); | ||
73 | printf("Summary error : %lx\n", _TEXASR_FAILURE_SUMMARY(texasr)); | ||
74 | printf("TFIAR exact : %lx\n\n", _TEXASR_TFIAR_EXACT(texasr)); | ||
75 | |||
76 | passed = 0; | ||
77 | return NULL; | ||
78 | |||
79 | /* HTM aborted but VMX0 is correct */ | ||
80 | value_match: | ||
81 | // printf("!"); | ||
82 | return NULL; | ||
83 | |||
84 | success: | ||
85 | // printf("."); | ||
86 | return NULL; | ||
87 | } | ||
88 | |||
89 | int tm_vmx_unavail_test() | ||
90 | { | ||
91 | int threads; | ||
92 | pthread_t *thread; | ||
93 | |||
94 | SKIP_IF(!have_htm()); | ||
95 | |||
96 | passed = 1; | ||
97 | |||
98 | threads = sysconf(_SC_NPROCESSORS_ONLN) * 4; | ||
99 | thread = malloc(sizeof(pthread_t)*threads); | ||
100 | if (!thread) | ||
101 | return EXIT_FAILURE; | ||
102 | |||
103 | for (uint64_t i = 0; i < threads; i++) | ||
104 | pthread_create(&thread[i], NULL, &worker, NULL); | ||
105 | |||
106 | for (uint64_t i = 0; i < threads; i++) | ||
107 | pthread_join(thread[i], NULL); | ||
108 | |||
109 | free(thread); | ||
110 | |||
111 | return passed ? EXIT_SUCCESS : EXIT_FAILURE; | ||
112 | } | ||
113 | |||
114 | |||
115 | int main(int argc, char **argv) | ||
116 | { | ||
117 | return test_harness(tm_vmx_unavail_test, "tm_vmx_unavail_test"); | ||
118 | } | ||