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

x86, nfit_test: Add unit test for memcpy_mcsafe()

Given the fact that the ACPI "EINJ" (error injection) facility is not
universally available, implement software infrastructure to validate the
memcpy_mcsafe() exception handling implementation.

For each potential read exception point in memcpy_mcsafe(), inject a
emulated exception point at the address identified by 'mcsafe_inject'
variable. With this infrastructure implement a test to validate that the
'bytes remaining' calculation is correct for a range of various source
buffer alignments.

This code is compiled out by default. The CONFIG_MCSAFE_DEBUG
configuration symbol needs to be manually enabled by editing
Kconfig.debug. I.e. this functionality can not be accidentally enabled
by a user / distro, it's only for development.

Cc: <x86@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Reported-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+192
+3
arch/x86/Kconfig.debug
··· 72 72 You should normally say N here, unless you want to debug early 73 73 crashes or need a very simple printk logging facility. 74 74 75 + config MCSAFE_TEST 76 + def_bool n 77 + 75 78 config X86_PTDUMP_CORE 76 79 def_bool n 77 80
+75
arch/x86/include/asm/mcsafe_test.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef _MCSAFE_TEST_H_ 3 + #define _MCSAFE_TEST_H_ 4 + 5 + #ifndef __ASSEMBLY__ 6 + #ifdef CONFIG_MCSAFE_TEST 7 + extern unsigned long mcsafe_test_src; 8 + extern unsigned long mcsafe_test_dst; 9 + 10 + static inline void mcsafe_inject_src(void *addr) 11 + { 12 + if (addr) 13 + mcsafe_test_src = (unsigned long) addr; 14 + else 15 + mcsafe_test_src = ~0UL; 16 + } 17 + 18 + static inline void mcsafe_inject_dst(void *addr) 19 + { 20 + if (addr) 21 + mcsafe_test_dst = (unsigned long) addr; 22 + else 23 + mcsafe_test_dst = ~0UL; 24 + } 25 + #else /* CONFIG_MCSAFE_TEST */ 26 + static inline void mcsafe_inject_src(void *addr) 27 + { 28 + } 29 + 30 + static inline void mcsafe_inject_dst(void *addr) 31 + { 32 + } 33 + #endif /* CONFIG_MCSAFE_TEST */ 34 + 35 + #else /* __ASSEMBLY__ */ 36 + #include <asm/export.h> 37 + 38 + #ifdef CONFIG_MCSAFE_TEST 39 + .macro MCSAFE_TEST_CTL 40 + .pushsection .data 41 + .align 8 42 + .globl mcsafe_test_src 43 + mcsafe_test_src: 44 + .quad 0 45 + EXPORT_SYMBOL_GPL(mcsafe_test_src) 46 + .globl mcsafe_test_dst 47 + mcsafe_test_dst: 48 + .quad 0 49 + EXPORT_SYMBOL_GPL(mcsafe_test_dst) 50 + .popsection 51 + .endm 52 + 53 + .macro MCSAFE_TEST_SRC reg count target 54 + leaq \count(\reg), %r9 55 + cmp mcsafe_test_src, %r9 56 + ja \target 57 + .endm 58 + 59 + .macro MCSAFE_TEST_DST reg count target 60 + leaq \count(\reg), %r9 61 + cmp mcsafe_test_dst, %r9 62 + ja \target 63 + .endm 64 + #else 65 + .macro MCSAFE_TEST_CTL 66 + .endm 67 + 68 + .macro MCSAFE_TEST_SRC reg count target 69 + .endm 70 + 71 + .macro MCSAFE_TEST_DST reg count target 72 + .endm 73 + #endif /* CONFIG_MCSAFE_TEST */ 74 + #endif /* __ASSEMBLY__ */ 75 + #endif /* _MCSAFE_TEST_H_ */
+10
arch/x86/lib/memcpy_64.S
··· 3 3 #include <linux/linkage.h> 4 4 #include <asm/errno.h> 5 5 #include <asm/cpufeatures.h> 6 + #include <asm/mcsafe_test.h> 6 7 #include <asm/alternative-asm.h> 7 8 #include <asm/export.h> 8 9 ··· 184 183 ENDPROC(memcpy_orig) 185 184 186 185 #ifndef CONFIG_UML 186 + 187 + MCSAFE_TEST_CTL 188 + 187 189 /* 188 190 * __memcpy_mcsafe - memory copy with machine check exception handling 189 191 * Note that we only catch machine checks when reading the source addresses. ··· 210 206 subl %ecx, %edx 211 207 .L_read_leading_bytes: 212 208 movb (%rsi), %al 209 + MCSAFE_TEST_SRC %rsi 1 .E_leading_bytes 210 + MCSAFE_TEST_DST %rdi 1 .E_leading_bytes 213 211 .L_write_leading_bytes: 214 212 movb %al, (%rdi) 215 213 incq %rsi ··· 227 221 228 222 .L_read_words: 229 223 movq (%rsi), %r8 224 + MCSAFE_TEST_SRC %rsi 8 .E_read_words 225 + MCSAFE_TEST_DST %rdi 8 .E_write_words 230 226 .L_write_words: 231 227 movq %r8, (%rdi) 232 228 addq $8, %rsi ··· 245 237 movl %edx, %ecx 246 238 .L_read_trailing_bytes: 247 239 movb (%rsi), %al 240 + MCSAFE_TEST_SRC %rsi 1 .E_trailing_bytes 241 + MCSAFE_TEST_DST %rdi 1 .E_trailing_bytes 248 242 .L_write_trailing_bytes: 249 243 movb %al, (%rdi) 250 244 incq %rsi
+104
tools/testing/nvdimm/test/nfit.c
··· 29 29 #include "nfit_test.h" 30 30 #include "../watermark.h" 31 31 32 + #include <asm/mcsafe_test.h> 33 + 32 34 /* 33 35 * Generate an NFIT table to describe the following topology: 34 36 * ··· 2683 2681 .id_table = nfit_test_id, 2684 2682 }; 2685 2683 2684 + static char mcsafe_buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); 2685 + 2686 + enum INJECT { 2687 + INJECT_NONE, 2688 + INJECT_SRC, 2689 + INJECT_DST, 2690 + }; 2691 + 2692 + static void mcsafe_test_init(char *dst, char *src, size_t size) 2693 + { 2694 + size_t i; 2695 + 2696 + memset(dst, 0xff, size); 2697 + for (i = 0; i < size; i++) 2698 + src[i] = (char) i; 2699 + } 2700 + 2701 + static bool mcsafe_test_validate(unsigned char *dst, unsigned char *src, 2702 + size_t size, unsigned long rem) 2703 + { 2704 + size_t i; 2705 + 2706 + for (i = 0; i < size - rem; i++) 2707 + if (dst[i] != (unsigned char) i) { 2708 + pr_info_once("%s:%d: offset: %zd got: %#x expect: %#x\n", 2709 + __func__, __LINE__, i, dst[i], 2710 + (unsigned char) i); 2711 + return false; 2712 + } 2713 + for (i = size - rem; i < size; i++) 2714 + if (dst[i] != 0xffU) { 2715 + pr_info_once("%s:%d: offset: %zd got: %#x expect: 0xff\n", 2716 + __func__, __LINE__, i, dst[i]); 2717 + return false; 2718 + } 2719 + return true; 2720 + } 2721 + 2722 + void mcsafe_test(void) 2723 + { 2724 + char *inject_desc[] = { "none", "source", "destination" }; 2725 + enum INJECT inj; 2726 + 2727 + if (IS_ENABLED(CONFIG_MCSAFE_TEST)) { 2728 + pr_info("%s: run...\n", __func__); 2729 + } else { 2730 + pr_info("%s: disabled, skip.\n", __func__); 2731 + return; 2732 + } 2733 + 2734 + for (inj = INJECT_NONE; inj <= INJECT_DST; inj++) { 2735 + int i; 2736 + 2737 + pr_info("%s: inject: %s\n", __func__, inject_desc[inj]); 2738 + for (i = 0; i < 512; i++) { 2739 + unsigned long expect, rem; 2740 + void *src, *dst; 2741 + bool valid; 2742 + 2743 + switch (inj) { 2744 + case INJECT_NONE: 2745 + mcsafe_inject_src(NULL); 2746 + mcsafe_inject_dst(NULL); 2747 + dst = &mcsafe_buf[2048]; 2748 + src = &mcsafe_buf[1024 - i]; 2749 + expect = 0; 2750 + break; 2751 + case INJECT_SRC: 2752 + mcsafe_inject_src(&mcsafe_buf[1024]); 2753 + mcsafe_inject_dst(NULL); 2754 + dst = &mcsafe_buf[2048]; 2755 + src = &mcsafe_buf[1024 - i]; 2756 + expect = 512 - i; 2757 + break; 2758 + case INJECT_DST: 2759 + mcsafe_inject_src(NULL); 2760 + mcsafe_inject_dst(&mcsafe_buf[2048]); 2761 + dst = &mcsafe_buf[2048 - i]; 2762 + src = &mcsafe_buf[1024]; 2763 + expect = 512 - i; 2764 + break; 2765 + } 2766 + 2767 + mcsafe_test_init(dst, src, 512); 2768 + rem = __memcpy_mcsafe(dst, src, 512); 2769 + valid = mcsafe_test_validate(dst, src, 512, expect); 2770 + if (rem == expect && valid) 2771 + continue; 2772 + pr_info("%s: copy(%#lx, %#lx, %d) off: %d rem: %ld %s expect: %ld\n", 2773 + __func__, 2774 + ((unsigned long) dst) & ~PAGE_MASK, 2775 + ((unsigned long ) src) & ~PAGE_MASK, 2776 + 512, i, rem, valid ? "valid" : "bad", 2777 + expect); 2778 + } 2779 + } 2780 + 2781 + mcsafe_inject_src(NULL); 2782 + mcsafe_inject_dst(NULL); 2783 + } 2784 + 2686 2785 static __init int nfit_test_init(void) 2687 2786 { 2688 2787 int rc, i; ··· 2792 2689 libnvdimm_test(); 2793 2690 acpi_nfit_test(); 2794 2691 device_dax_test(); 2692 + mcsafe_test(); 2795 2693 2796 2694 nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm); 2797 2695