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 | } | ||
