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

powerpc/lib/xor_vmx: Ensure no altivec code executes before enable_kernel_altivec()

The xor_vmx.c file is used for the RAID5 xor operations. In these functions
altivec is enabled to run the operation and then disabled.

The code uses enable_kernel_altivec() around the core of the algorithm, however
the whole file is built with -maltivec, so the compiler is within its rights to
generate altivec code anywhere. This has been seen at least once in the wild:

0:mon> di $xor_altivec_2
c0000000000b97d0 3c4c01d9 addis r2,r12,473
c0000000000b97d4 3842db30 addi r2,r2,-9424
c0000000000b97d8 7c0802a6 mflr r0
c0000000000b97dc f8010010 std r0,16(r1)
c0000000000b97e0 60000000 nop
c0000000000b97e4 7c0802a6 mflr r0
c0000000000b97e8 faa1ffa8 std r21,-88(r1)
...
c0000000000b981c f821ff41 stdu r1,-192(r1)
c0000000000b9820 7f8101ce stvx v28,r1,r0 <-- POP
c0000000000b9824 38000030 li r0,48
c0000000000b9828 7fa101ce stvx v29,r1,r0
...
c0000000000b984c 4bf6a06d bl c0000000000238b8 # enable_kernel_altivec

This patch splits the non-altivec code into xor_vmx_glue.c which calls the
altivec functions in xor_vmx.c. By compiling xor_vmx_glue.c without
-maltivec we can guarantee that altivec instruction will not be executed
outside of the enable/disable block.

Signed-off-by: Matt Brown <matthew.brown.dev@gmail.com>
[mpe: Rework change log and include disassembly]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Matt Brown and committed by
Michael Ellerman
f718d426 48a316e3

+94 -43
+1 -1
arch/powerpc/lib/Makefile
··· 37 37 38 38 obj-$(CONFIG_FTR_FIXUP_SELFTEST) += feature-fixups-test.o 39 39 40 - obj-$(CONFIG_ALTIVEC) += xor_vmx.o 40 + obj-$(CONFIG_ALTIVEC) += xor_vmx.o xor_vmx_glue.o 41 41 CFLAGS_xor_vmx.o += -maltivec $(call cc-option,-mabi=altivec) 42 42 43 43 obj-$(CONFIG_PPC64) += $(obj64-y)
+11 -42
arch/powerpc/lib/xor_vmx.c
··· 29 29 #define vector __attribute__((vector_size(16))) 30 30 #endif 31 31 32 - #include <linux/preempt.h> 33 - #include <linux/export.h> 34 - #include <linux/sched.h> 35 - #include <asm/switch_to.h> 32 + #include "xor_vmx.h" 36 33 37 34 typedef vector signed char unative_t; 38 35 ··· 61 64 V1##_3 = vec_xor(V1##_3, V2##_3); \ 62 65 } while (0) 63 66 64 - void xor_altivec_2(unsigned long bytes, unsigned long *v1_in, 65 - unsigned long *v2_in) 67 + void __xor_altivec_2(unsigned long bytes, unsigned long *v1_in, 68 + unsigned long *v2_in) 66 69 { 67 70 DEFINE(v1); 68 71 DEFINE(v2); 69 72 unsigned long lines = bytes / (sizeof(unative_t)) / 4; 70 - 71 - preempt_disable(); 72 - enable_kernel_altivec(); 73 73 74 74 do { 75 75 LOAD(v1); ··· 77 83 v1 += 4; 78 84 v2 += 4; 79 85 } while (--lines > 0); 80 - 81 - disable_kernel_altivec(); 82 - preempt_enable(); 83 86 } 84 - EXPORT_SYMBOL(xor_altivec_2); 85 87 86 - void xor_altivec_3(unsigned long bytes, unsigned long *v1_in, 87 - unsigned long *v2_in, unsigned long *v3_in) 88 + void __xor_altivec_3(unsigned long bytes, unsigned long *v1_in, 89 + unsigned long *v2_in, unsigned long *v3_in) 88 90 { 89 91 DEFINE(v1); 90 92 DEFINE(v2); 91 93 DEFINE(v3); 92 94 unsigned long lines = bytes / (sizeof(unative_t)) / 4; 93 - 94 - preempt_disable(); 95 - enable_kernel_altivec(); 96 95 97 96 do { 98 97 LOAD(v1); ··· 99 112 v2 += 4; 100 113 v3 += 4; 101 114 } while (--lines > 0); 102 - 103 - disable_kernel_altivec(); 104 - preempt_enable(); 105 115 } 106 - EXPORT_SYMBOL(xor_altivec_3); 107 116 108 - void xor_altivec_4(unsigned long bytes, unsigned long *v1_in, 109 - unsigned long *v2_in, unsigned long *v3_in, 110 - unsigned long *v4_in) 117 + void __xor_altivec_4(unsigned long bytes, unsigned long *v1_in, 118 + unsigned long *v2_in, unsigned long *v3_in, 119 + unsigned long *v4_in) 111 120 { 112 121 DEFINE(v1); 113 122 DEFINE(v2); 114 123 DEFINE(v3); 115 124 DEFINE(v4); 116 125 unsigned long lines = bytes / (sizeof(unative_t)) / 4; 117 - 118 - preempt_disable(); 119 - enable_kernel_altivec(); 120 126 121 127 do { 122 128 LOAD(v1); ··· 126 146 v3 += 4; 127 147 v4 += 4; 128 148 } while (--lines > 0); 129 - 130 - disable_kernel_altivec(); 131 - preempt_enable(); 132 149 } 133 - EXPORT_SYMBOL(xor_altivec_4); 134 150 135 - void xor_altivec_5(unsigned long bytes, unsigned long *v1_in, 136 - unsigned long *v2_in, unsigned long *v3_in, 137 - unsigned long *v4_in, unsigned long *v5_in) 151 + void __xor_altivec_5(unsigned long bytes, unsigned long *v1_in, 152 + unsigned long *v2_in, unsigned long *v3_in, 153 + unsigned long *v4_in, unsigned long *v5_in) 138 154 { 139 155 DEFINE(v1); 140 156 DEFINE(v2); ··· 138 162 DEFINE(v4); 139 163 DEFINE(v5); 140 164 unsigned long lines = bytes / (sizeof(unative_t)) / 4; 141 - 142 - preempt_disable(); 143 - enable_kernel_altivec(); 144 165 145 166 do { 146 167 LOAD(v1); ··· 157 184 v4 += 4; 158 185 v5 += 4; 159 186 } while (--lines > 0); 160 - 161 - disable_kernel_altivec(); 162 - preempt_enable(); 163 187 } 164 - EXPORT_SYMBOL(xor_altivec_5);
+20
arch/powerpc/lib/xor_vmx.h
··· 1 + /* 2 + * Simple interface to link xor_vmx.c and xor_vmx_glue.c 3 + * 4 + * Separating these file ensures that no altivec instructions are run 5 + * outside of the enable/disable altivec block. 6 + */ 7 + 8 + void __xor_altivec_2(unsigned long bytes, unsigned long *v1_in, 9 + unsigned long *v2_in); 10 + 11 + void __xor_altivec_3(unsigned long bytes, unsigned long *v1_in, 12 + unsigned long *v2_in, unsigned long *v3_in); 13 + 14 + void __xor_altivec_4(unsigned long bytes, unsigned long *v1_in, 15 + unsigned long *v2_in, unsigned long *v3_in, 16 + unsigned long *v4_in); 17 + 18 + void __xor_altivec_5(unsigned long bytes, unsigned long *v1_in, 19 + unsigned long *v2_in, unsigned long *v3_in, 20 + unsigned long *v4_in, unsigned long *v5_in);
+62
arch/powerpc/lib/xor_vmx_glue.c
··· 1 + /* 2 + * Altivec XOR operations 3 + * 4 + * Copyright 2017 IBM Corp. 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License 8 + * as published by the Free Software Foundation; either version 9 + * 2 of the License, or (at your option) any later version. 10 + */ 11 + 12 + #include <linux/preempt.h> 13 + #include <linux/export.h> 14 + #include <linux/sched.h> 15 + #include <asm/switch_to.h> 16 + #include "xor_vmx.h" 17 + 18 + void xor_altivec_2(unsigned long bytes, unsigned long *v1_in, 19 + unsigned long *v2_in) 20 + { 21 + preempt_disable(); 22 + enable_kernel_altivec(); 23 + __xor_altivec_2(bytes, v1_in, v2_in); 24 + disable_kernel_altivec(); 25 + preempt_enable(); 26 + } 27 + EXPORT_SYMBOL(xor_altivec_2); 28 + 29 + void xor_altivec_3(unsigned long bytes, unsigned long *v1_in, 30 + unsigned long *v2_in, unsigned long *v3_in) 31 + { 32 + preempt_disable(); 33 + enable_kernel_altivec(); 34 + __xor_altivec_3(bytes, v1_in, v2_in, v3_in); 35 + disable_kernel_altivec(); 36 + preempt_enable(); 37 + } 38 + EXPORT_SYMBOL(xor_altivec_3); 39 + 40 + void xor_altivec_4(unsigned long bytes, unsigned long *v1_in, 41 + unsigned long *v2_in, unsigned long *v3_in, 42 + unsigned long *v4_in) 43 + { 44 + preempt_disable(); 45 + enable_kernel_altivec(); 46 + __xor_altivec_4(bytes, v1_in, v2_in, v3_in, v4_in); 47 + disable_kernel_altivec(); 48 + preempt_enable(); 49 + } 50 + EXPORT_SYMBOL(xor_altivec_4); 51 + 52 + void xor_altivec_5(unsigned long bytes, unsigned long *v1_in, 53 + unsigned long *v2_in, unsigned long *v3_in, 54 + unsigned long *v4_in, unsigned long *v5_in) 55 + { 56 + preempt_disable(); 57 + enable_kernel_altivec(); 58 + __xor_altivec_5(bytes, v1_in, v2_in, v3_in, v4_in, v5_in); 59 + disable_kernel_altivec(); 60 + preempt_enable(); 61 + } 62 + EXPORT_SYMBOL(xor_altivec_5);