aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/arch/loongarch/include/uapi/asm/perf_regs.h40
-rw-r--r--tools/arch/loongarch/include/uapi/asm/unistd.h9
-rw-r--r--tools/arch/x86/include/asm/orc_types.h12
-rw-r--r--tools/include/linux/objtool.h200
-rw-r--r--tools/include/linux/objtool_types.h57
-rw-r--r--tools/include/uapi/linux/kvm.h2
-rwxr-xr-xtools/kvm/kvm_stat/kvm_stat2
-rw-r--r--tools/objtool/check.c476
-rw-r--r--tools/objtool/elf.c2
-rw-r--r--tools/objtool/include/objtool/check.h4
-rw-r--r--tools/objtool/include/objtool/elf.h9
-rw-r--r--tools/objtool/include/objtool/warn.h5
-rw-r--r--tools/objtool/orc_dump.c15
-rw-r--r--tools/objtool/orc_gen.c48
-rwxr-xr-xtools/objtool/sync-check.sh2
-rw-r--r--tools/perf/Makefile.config12
-rw-r--r--tools/perf/arch/loongarch/Build1
-rw-r--r--tools/perf/arch/loongarch/Makefile28
-rw-r--r--tools/perf/arch/loongarch/annotate/instructions.c45
-rwxr-xr-xtools/perf/arch/loongarch/entry/syscalls/mksyscalltbl61
-rw-r--r--tools/perf/arch/loongarch/include/dwarf-regs-table.h16
-rw-r--r--tools/perf/arch/loongarch/include/perf_regs.h15
-rw-r--r--tools/perf/arch/loongarch/util/Build5
-rw-r--r--tools/perf/arch/loongarch/util/dwarf-regs.c44
-rw-r--r--tools/perf/arch/loongarch/util/perf_regs.c6
-rw-r--r--tools/perf/arch/loongarch/util/unwind-libdw.c56
-rw-r--r--tools/perf/arch/loongarch/util/unwind-libunwind.c82
-rwxr-xr-xtools/perf/check-headers.sh1
-rw-r--r--tools/perf/util/annotate.c8
-rw-r--r--tools/perf/util/dwarf-regs.c7
-rw-r--r--tools/perf/util/env.c2
-rw-r--r--tools/perf/util/genelf.h3
-rw-r--r--tools/perf/util/perf_regs.c76
-rw-r--r--tools/perf/util/syscalltbl.c4
-rw-r--r--tools/testing/cxl/config_check.c1
-rw-r--r--tools/testing/cxl/test/mem.c247
-rw-r--r--tools/testing/selftests/Makefile1
-rwxr-xr-xtools/testing/selftests/cgroup/test_cpuset_prs.sh25
-rw-r--r--tools/testing/selftests/kvm/Makefile2
-rw-r--r--tools/testing/selftests/kvm/aarch64/arch_timer.c56
-rw-r--r--tools/testing/selftests/kvm/aarch64/get-reg-list.c15
-rw-r--r--tools/testing/selftests/kvm/aarch64/smccc_filter.c268
-rw-r--r--tools/testing/selftests/kvm/config1
-rw-r--r--tools/testing/selftests/kvm/demand_paging_test.c2
-rw-r--r--tools/testing/selftests/kvm/include/aarch64/processor.h13
-rw-r--r--tools/testing/selftests/kvm/include/kvm_util_base.h1
-rw-r--r--tools/testing/selftests/kvm/include/x86_64/processor.h124
-rw-r--r--tools/testing/selftests/kvm/lib/aarch64/processor.c91
-rw-r--r--tools/testing/selftests/kvm/lib/kvm_util.c5
-rw-r--r--tools/testing/selftests/kvm/lib/x86_64/processor.c36
-rw-r--r--tools/testing/selftests/kvm/x86_64/amx_test.c118
-rw-r--r--tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c253
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c8
-rw-r--r--tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c231
-rw-r--r--tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c132
-rw-r--r--tools/testing/selftests/mm/ksm_functional_tests.c46
-rw-r--r--tools/testing/selftests/mm/protection_keys.c4
-rw-r--r--tools/testing/selftests/powerpc/Makefile8
-rw-r--r--tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h1
-rw-r--r--tools/testing/selftests/powerpc/dscr/Makefile3
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr.h4
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_default_test.c207
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c169
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c4
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c11
-rw-r--r--tools/testing/selftests/powerpc/dscr/dscr_user_test.c4
-rw-r--r--tools/testing/selftests/powerpc/dscr/settings1
-rw-r--r--tools/testing/selftests/powerpc/include/utils.h3
-rw-r--r--tools/testing/selftests/powerpc/math/vmx_signal.c1
-rw-r--r--tools/testing/selftests/powerpc/mm/Makefile2
-rw-r--r--tools/testing/selftests/powerpc/pmu/Makefile31
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c3
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c3
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c3
-rw-r--r--tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c6
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.c19
-rw-r--r--tools/testing/selftests/powerpc/pmu/lib.h1
-rw-r--r--tools/testing/selftests/powerpc/pmu/sampling_tests/mmcra_thresh_marked_sample_test.c4
-rw-r--r--tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h1
-rw-r--r--tools/testing/selftests/powerpc/utils.c23
-rw-r--r--tools/testing/selftests/riscv/Makefile58
-rw-r--r--tools/testing/selftests/riscv/hwprobe/Makefile10
-rw-r--r--tools/testing/selftests/riscv/hwprobe/hwprobe.c90
-rw-r--r--tools/testing/selftests/riscv/hwprobe/sys_hwprobe.S12
-rw-r--r--tools/testing/selftests/user_events/Makefile2
-rw-r--r--tools/testing/selftests/user_events/abi_test.c229
-rw-r--r--tools/testing/selftests/user_events/dyn_test.c2
-rw-r--r--tools/testing/selftests/user_events/ftrace_test.c176
-rw-r--r--tools/testing/selftests/user_events/perf_test.c39
-rw-r--r--tools/tracing/rtla/.gitignore1
-rw-r--r--tools/tracing/rtla/src/timerlat_aa.c2
-rw-r--r--tools/tracing/rtla/src/timerlat_top.c49
-rw-r--r--tools/verification/rv/src/rv.c2
93 files changed, 3135 insertions, 1084 deletions
diff --git a/tools/arch/loongarch/include/uapi/asm/perf_regs.h b/tools/arch/loongarch/include/uapi/asm/perf_regs.h
new file mode 100644
index 000000000000..29d69c00fc7a
--- /dev/null
+++ b/tools/arch/loongarch/include/uapi/asm/perf_regs.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _ASM_LOONGARCH_PERF_REGS_H
+#define _ASM_LOONGARCH_PERF_REGS_H
+
+enum perf_event_loongarch_regs {
+ PERF_REG_LOONGARCH_PC,
+ PERF_REG_LOONGARCH_R1,
+ PERF_REG_LOONGARCH_R2,
+ PERF_REG_LOONGARCH_R3,
+ PERF_REG_LOONGARCH_R4,
+ PERF_REG_LOONGARCH_R5,
+ PERF_REG_LOONGARCH_R6,
+ PERF_REG_LOONGARCH_R7,
+ PERF_REG_LOONGARCH_R8,
+ PERF_REG_LOONGARCH_R9,
+ PERF_REG_LOONGARCH_R10,
+ PERF_REG_LOONGARCH_R11,
+ PERF_REG_LOONGARCH_R12,
+ PERF_REG_LOONGARCH_R13,
+ PERF_REG_LOONGARCH_R14,
+ PERF_REG_LOONGARCH_R15,
+ PERF_REG_LOONGARCH_R16,
+ PERF_REG_LOONGARCH_R17,
+ PERF_REG_LOONGARCH_R18,
+ PERF_REG_LOONGARCH_R19,
+ PERF_REG_LOONGARCH_R20,
+ PERF_REG_LOONGARCH_R21,
+ PERF_REG_LOONGARCH_R22,
+ PERF_REG_LOONGARCH_R23,
+ PERF_REG_LOONGARCH_R24,
+ PERF_REG_LOONGARCH_R25,
+ PERF_REG_LOONGARCH_R26,
+ PERF_REG_LOONGARCH_R27,
+ PERF_REG_LOONGARCH_R28,
+ PERF_REG_LOONGARCH_R29,
+ PERF_REG_LOONGARCH_R30,
+ PERF_REG_LOONGARCH_R31,
+ PERF_REG_LOONGARCH_MAX,
+};
+#endif /* _ASM_LOONGARCH_PERF_REGS_H */
diff --git a/tools/arch/loongarch/include/uapi/asm/unistd.h b/tools/arch/loongarch/include/uapi/asm/unistd.h
new file mode 100644
index 000000000000..0c743344e92d
--- /dev/null
+++ b/tools/arch/loongarch/include/uapi/asm/unistd.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#define __ARCH_WANT_SYS_CLONE
+#define __ARCH_WANT_SYS_CLONE3
+
+#include <asm-generic/unistd.h>
diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h
index 1343a62106de..46d7e06763c9 100644
--- a/tools/arch/x86/include/asm/orc_types.h
+++ b/tools/arch/x86/include/asm/orc_types.h
@@ -39,6 +39,12 @@
#define ORC_REG_SP_INDIRECT 9
#define ORC_REG_MAX 15
+#define ORC_TYPE_UNDEFINED 0
+#define ORC_TYPE_END_OF_STACK 1
+#define ORC_TYPE_CALL 2
+#define ORC_TYPE_REGS 3
+#define ORC_TYPE_REGS_PARTIAL 4
+
#ifndef __ASSEMBLY__
#include <asm/byteorder.h>
@@ -56,16 +62,14 @@ struct orc_entry {
#if defined(__LITTLE_ENDIAN_BITFIELD)
unsigned sp_reg:4;
unsigned bp_reg:4;
- unsigned type:2;
+ unsigned type:3;
unsigned signal:1;
- unsigned end:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
unsigned bp_reg:4;
unsigned sp_reg:4;
unsigned unused:4;
- unsigned end:1;
unsigned signal:1;
- unsigned type:2;
+ unsigned type:3;
#endif
} __packed;
diff --git a/tools/include/linux/objtool.h b/tools/include/linux/objtool.h
deleted file mode 100644
index 9ac3df3fccf0..000000000000
--- a/tools/include/linux/objtool.h
+++ /dev/null
@@ -1,200 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_OBJTOOL_H
-#define _LINUX_OBJTOOL_H
-
-#ifndef __ASSEMBLY__
-
-#include <linux/types.h>
-
-/*
- * This struct is used by asm and inline asm code to manually annotate the
- * location of registers on the stack.
- */
-struct unwind_hint {
- u32 ip;
- s16 sp_offset;
- u8 sp_reg;
- u8 type;
- u8 signal;
- u8 end;
-};
-#endif
-
-/*
- * UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
- * (the caller's SP right before it made the call). Used for all callable
- * functions, i.e. all C code and all callable asm functions.
- *
- * UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
- * points to a fully populated pt_regs from a syscall, interrupt, or exception.
- *
- * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
- * sp_reg+sp_offset points to the iret return frame.
- *
- * UNWIND_HINT_FUNC: Generate the unwind metadata of a callable function.
- * Useful for code which doesn't have an ELF function annotation.
- *
- * UNWIND_HINT_ENTRY: machine entry without stack, SYSCALL/SYSENTER etc.
- */
-#define UNWIND_HINT_TYPE_CALL 0
-#define UNWIND_HINT_TYPE_REGS 1
-#define UNWIND_HINT_TYPE_REGS_PARTIAL 2
-#define UNWIND_HINT_TYPE_FUNC 3
-#define UNWIND_HINT_TYPE_ENTRY 4
-#define UNWIND_HINT_TYPE_SAVE 5
-#define UNWIND_HINT_TYPE_RESTORE 6
-
-#ifdef CONFIG_OBJTOOL
-
-#include <asm/asm.h>
-
-#ifndef __ASSEMBLY__
-
-#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
- "987: \n\t" \
- ".pushsection .discard.unwind_hints\n\t" \
- /* struct unwind_hint */ \
- ".long 987b - .\n\t" \
- ".short " __stringify(sp_offset) "\n\t" \
- ".byte " __stringify(sp_reg) "\n\t" \
- ".byte " __stringify(type) "\n\t" \
- ".byte " __stringify(signal) "\n\t" \
- ".byte " __stringify(end) "\n\t" \
- ".balign 4 \n\t" \
- ".popsection\n\t"
-
-/*
- * This macro marks the given function's stack frame as "non-standard", which
- * tells objtool to ignore the function when doing stack metadata validation.
- * It should only be used in special cases where you're 100% sure it won't
- * affect the reliability of frame pointers and kernel stack traces.
- *
- * For more information, see tools/objtool/Documentation/objtool.txt.
- */
-#define STACK_FRAME_NON_STANDARD(func) \
- static void __used __section(".discard.func_stack_frame_non_standard") \
- *__func_stack_frame_non_standard_##func = func
-
-/*
- * STACK_FRAME_NON_STANDARD_FP() is a frame-pointer-specific function ignore
- * for the case where a function is intentionally missing frame pointer setup,
- * but otherwise needs objtool/ORC coverage when frame pointers are disabled.
- */
-#ifdef CONFIG_FRAME_POINTER
-#define STACK_FRAME_NON_STANDARD_FP(func) STACK_FRAME_NON_STANDARD(func)
-#else
-#define STACK_FRAME_NON_STANDARD_FP(func)
-#endif
-
-#define ANNOTATE_NOENDBR \
- "986: \n\t" \
- ".pushsection .discard.noendbr\n\t" \
- _ASM_PTR " 986b\n\t" \
- ".popsection\n\t"
-
-#define ASM_REACHABLE \
- "998:\n\t" \
- ".pushsection .discard.reachable\n\t" \
- ".long 998b - .\n\t" \
- ".popsection\n\t"
-
-#else /* __ASSEMBLY__ */
-
-/*
- * This macro indicates that the following intra-function call is valid.
- * Any non-annotated intra-function call will cause objtool to issue a warning.
- */
-#define ANNOTATE_INTRA_FUNCTION_CALL \
- 999: \
- .pushsection .discard.intra_function_calls; \
- .long 999b; \
- .popsection;
-
-/*
- * In asm, there are two kinds of code: normal C-type callable functions and
- * the rest. The normal callable functions can be called by other code, and
- * don't do anything unusual with the stack. Such normal callable functions
- * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this
- * category. In this case, no special debugging annotations are needed because
- * objtool can automatically generate the ORC data for the ORC unwinder to read
- * at runtime.
- *
- * Anything which doesn't fall into the above category, such as syscall and
- * interrupt handlers, tends to not be called directly by other functions, and
- * often does unusual non-C-function-type things with the stack pointer. Such
- * code needs to be annotated such that objtool can understand it. The
- * following CFI hint macros are for this type of code.
- *
- * These macros provide hints to objtool about the state of the stack at each
- * instruction. Objtool starts from the hints and follows the code flow,
- * making automatic CFI adjustments when it sees pushes and pops, filling out
- * the debuginfo as necessary. It will also warn if it sees any
- * inconsistencies.
- */
-.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
-.Lunwind_hint_ip_\@:
- .pushsection .discard.unwind_hints
- /* struct unwind_hint */
- .long .Lunwind_hint_ip_\@ - .
- .short \sp_offset
- .byte \sp_reg
- .byte \type
- .byte \signal
- .byte \end
- .balign 4
- .popsection
-.endm
-
-.macro STACK_FRAME_NON_STANDARD func:req
- .pushsection .discard.func_stack_frame_non_standard, "aw"
- _ASM_PTR \func
- .popsection
-.endm
-
-.macro STACK_FRAME_NON_STANDARD_FP func:req
-#ifdef CONFIG_FRAME_POINTER
- STACK_FRAME_NON_STANDARD \func
-#endif
-.endm
-
-.macro ANNOTATE_NOENDBR
-.Lhere_\@:
- .pushsection .discard.noendbr
- .quad .Lhere_\@
- .popsection
-.endm
-
-.macro REACHABLE
-.Lhere_\@:
- .pushsection .discard.reachable
- .long .Lhere_\@ - .
- .popsection
-.endm
-
-#endif /* __ASSEMBLY__ */
-
-#else /* !CONFIG_OBJTOOL */
-
-#ifndef __ASSEMBLY__
-
-#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \
- "\n\t"
-#define STACK_FRAME_NON_STANDARD(func)
-#define STACK_FRAME_NON_STANDARD_FP(func)
-#define ANNOTATE_NOENDBR
-#define ASM_REACHABLE
-#else
-#define ANNOTATE_INTRA_FUNCTION_CALL
-.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0
-.endm
-.macro STACK_FRAME_NON_STANDARD func:req
-.endm
-.macro ANNOTATE_NOENDBR
-.endm
-.macro REACHABLE
-.endm
-#endif
-
-#endif /* CONFIG_OBJTOOL */
-
-#endif /* _LINUX_OBJTOOL_H */
diff --git a/tools/include/linux/objtool_types.h b/tools/include/linux/objtool_types.h
new file mode 100644
index 000000000000..453a4f4ef39d
--- /dev/null
+++ b/tools/include/linux/objtool_types.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_OBJTOOL_TYPES_H
+#define _LINUX_OBJTOOL_TYPES_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+/*
+ * This struct is used by asm and inline asm code to manually annotate the
+ * location of registers on the stack.
+ */
+struct unwind_hint {
+ u32 ip;
+ s16 sp_offset;
+ u8 sp_reg;
+ u8 type;
+ u8 signal;
+};
+
+#endif /* __ASSEMBLY__ */
+
+/*
+ * UNWIND_HINT_TYPE_UNDEFINED: A blind spot in ORC coverage which can result in
+ * a truncated and unreliable stack unwind.
+ *
+ * UNWIND_HINT_TYPE_END_OF_STACK: The end of the kernel stack unwind before
+ * hitting user entry, boot code, or fork entry (when there are no pt_regs
+ * available).
+ *
+ * UNWIND_HINT_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP
+ * (the caller's SP right before it made the call). Used for all callable
+ * functions, i.e. all C code and all callable asm functions.
+ *
+ * UNWIND_HINT_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset
+ * points to a fully populated pt_regs from a syscall, interrupt, or exception.
+ *
+ * UNWIND_HINT_TYPE_REGS_PARTIAL: Used in entry code to indicate that
+ * sp_reg+sp_offset points to the iret return frame.
+ *
+ * UNWIND_HINT_TYPE_FUNC: Generate the unwind metadata of a callable function.
+ * Useful for code which doesn't have an ELF function annotation.
+ *
+ * UNWIND_HINT_TYPE_{SAVE,RESTORE}: Save the unwind metadata at a certain
+ * location so that it can be restored later.
+ */
+#define UNWIND_HINT_TYPE_UNDEFINED 0
+#define UNWIND_HINT_TYPE_END_OF_STACK 1
+#define UNWIND_HINT_TYPE_CALL 2
+#define UNWIND_HINT_TYPE_REGS 3
+#define UNWIND_HINT_TYPE_REGS_PARTIAL 4
+/* The below hint types don't have corresponding ORC types */
+#define UNWIND_HINT_TYPE_FUNC 5
+#define UNWIND_HINT_TYPE_SAVE 6
+#define UNWIND_HINT_TYPE_RESTORE 7
+
+#endif /* _LINUX_OBJTOOL_TYPES_H */
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index d77aef872a0a..4003a166328c 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1451,7 +1451,7 @@ struct kvm_vfio_spapr_tce {
#define KVM_CREATE_VCPU _IO(KVMIO, 0x41)
#define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log)
#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44)
-#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45)
+#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45) /* deprecated */
#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46, \
struct kvm_userspace_memory_region)
#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47)
diff --git a/tools/kvm/kvm_stat/kvm_stat b/tools/kvm/kvm_stat/kvm_stat
index 6f28180ffeea..15bf00e79e3f 100755
--- a/tools/kvm/kvm_stat/kvm_stat
+++ b/tools/kvm/kvm_stat/kvm_stat
@@ -627,7 +627,7 @@ class TracepointProvider(Provider):
name)'.
All available events have directories under
- /sys/kernel/debug/tracing/events/ which export information
+ /sys/kernel/tracing/events/ which export information
about the specific event. Therefore, listing the dirs gives us
a list of all available events.
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 931cdb7dba19..0fcf99c91400 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -17,7 +17,7 @@
#include <objtool/warn.h>
#include <objtool/endianness.h>
-#include <linux/objtool.h>
+#include <linux/objtool_types.h>
#include <linux/hashtable.h>
#include <linux/kernel.h>
#include <linux/static_call_types.h>
@@ -202,6 +202,8 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
"__reiserfs_panic",
"__stack_chk_fail",
"__ubsan_handle_builtin_unreachable",
+ "arch_call_rest_init",
+ "arch_cpu_idle_dead",
"btrfs_assertfail",
"cpu_bringup_and_idle",
"cpu_startup_entry",
@@ -210,18 +212,28 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
"do_task_dead",
"ex_handler_msr_mce",
"fortify_panic",
+ "hlt_play_dead",
+ "hv_ghcb_terminate",
"kthread_complete_and_exit",
"kthread_exit",
"kunit_try_catch_throw",
"lbug_with_loc",
"machine_real_restart",
"make_task_dead",
+ "mpt_halt_firmware",
+ "nmi_panic_self_stop",
"panic",
+ "panic_smp_self_stop",
+ "rest_init",
+ "resume_play_dead",
"rewind_stack_and_make_dead",
"sev_es_terminate",
"snp_abort",
+ "start_kernel",
"stop_this_cpu",
"usercopy_abort",
+ "x86_64_start_kernel",
+ "x86_64_start_reservations",
"xen_cpu_bringup_again",
"xen_start_kernel",
};
@@ -229,14 +241,14 @@ static bool __dead_end_function(struct objtool_file *file, struct symbol *func,
if (!func)
return false;
- if (func->bind == STB_WEAK)
- return false;
-
- if (func->bind == STB_GLOBAL)
+ if (func->bind == STB_GLOBAL || func->bind == STB_WEAK)
for (i = 0; i < ARRAY_SIZE(global_noreturns); i++)
if (!strcmp(func->name, global_noreturns[i]))
return true;
+ if (func->bind == STB_WEAK)
+ return false;
+
if (!func->len)
return false;
@@ -470,7 +482,7 @@ static int decode_instructions(struct objtool_file *file)
// printf("%s: last chunk used: %d\n", sec->name, (int)idx);
- list_for_each_entry(func, &sec->symbol_list, list) {
+ sec_for_each_sym(sec, func) {
if (func->type != STT_NOTYPE && func->type != STT_FUNC)
continue;
@@ -924,7 +936,7 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
static int create_cfi_sections(struct objtool_file *file)
{
- struct section *sec, *s;
+ struct section *sec;
struct symbol *sym;
unsigned int *loc;
int idx;
@@ -937,19 +949,14 @@ static int create_cfi_sections(struct objtool_file *file)
}
idx = 0;
- for_each_sec(file, s) {
- if (!s->text)
+ for_each_sym(file, sym) {
+ if (sym->type != STT_FUNC)
continue;
- list_for_each_entry(sym, &s->symbol_list, list) {
- if (sym->type != STT_FUNC)
- continue;
-
- if (strncmp(sym->name, "__cfi_", 6))
- continue;
+ if (strncmp(sym->name, "__cfi_", 6))
+ continue;
- idx++;
- }
+ idx++;
}
sec = elf_create_section(file->elf, ".cfi_sites", 0, sizeof(unsigned int), idx);
@@ -957,28 +964,23 @@ static int create_cfi_sections(struct objtool_file *file)
return -1;
idx = 0;
- for_each_sec(file, s) {
- if (!s->text)
+ for_each_sym(file, sym) {
+ if (sym->type != STT_FUNC)
continue;
- list_for_each_entry(sym, &s->symbol_list, list) {
- if (sym->type != STT_FUNC)
- continue;
-
- if (strncmp(sym->name, "__cfi_", 6))
- continue;
+ if (strncmp(sym->name, "__cfi_", 6))
+ continue;
- loc = (unsigned int *)sec->data->d_buf + idx;
- memset(loc, 0, sizeof(unsigned int));
+ loc = (unsigned int *)sec->data->d_buf + idx;
+ memset(loc, 0, sizeof(unsigned int));
- if (elf_add_reloc_to_insn(file->elf, sec,
- idx * sizeof(unsigned int),
- R_X86_64_PC32,
- s, sym->offset))
- return -1;
+ if (elf_add_reloc_to_insn(file->elf, sec,
+ idx * sizeof(unsigned int),
+ R_X86_64_PC32,
+ sym->sec, sym->offset))
+ return -1;
- idx++;
- }
+ idx++;
}
return 0;
@@ -1279,6 +1281,8 @@ static const char *uaccess_safe_builtin[] = {
"__ubsan_handle_type_mismatch_v1",
"__ubsan_handle_shift_out_of_bounds",
"__ubsan_handle_load_invalid_value",
+ /* STACKLEAK */
+ "stackleak_track_stack",
/* misc */
"csum_partial_copy_generic",
"copy_mc_fragile",
@@ -1444,7 +1448,7 @@ static void annotate_call_site(struct objtool_file *file,
if (opts.mcount && sym->fentry) {
if (sibling)
- WARN_FUNC("Tail call to __fentry__ !?!?", insn->sec, insn->offset);
+ WARN_INSN(insn, "tail call to __fentry__ !?!?");
if (opts.mnop) {
if (reloc) {
reloc->type = R_NONE;
@@ -1646,9 +1650,8 @@ static int add_jump_destinations(struct objtool_file *file)
continue;
}
- WARN_FUNC("can't find jump dest instruction at %s+0x%lx",
- insn->sec, insn->offset, dest_sec->name,
- dest_off);
+ WARN_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
+ dest_sec->name, dest_off);
return -1;
}
@@ -1731,13 +1734,12 @@ static int add_call_destinations(struct objtool_file *file)
continue;
if (!insn_call_dest(insn)) {
- WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset);
+ WARN_INSN(insn, "unannotated intra-function call");
return -1;
}
if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) {
- WARN_FUNC("unsupported call to non-function",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unsupported call to non-function");
return -1;
}
@@ -1745,10 +1747,8 @@ static int add_call_destinations(struct objtool_file *file)
dest_off = arch_dest_reloc_offset(reloc->addend);
dest = find_call_destination(reloc->sym->sec, dest_off);
if (!dest) {
- WARN_FUNC("can't find call dest symbol at %s+0x%lx",
- insn->sec, insn->offset,
- reloc->sym->sec->name,
- dest_off);
+ WARN_INSN(insn, "can't find call dest symbol at %s+0x%lx",
+ reloc->sym->sec->name, dest_off);
return -1;
}
@@ -1808,8 +1808,7 @@ static int handle_group_alt(struct objtool_file *file,
} else {
if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
orig_alt_group->first_insn->offset != special_alt->orig_len) {
- WARN_FUNC("weirdly overlapping alternative! %ld != %d",
- orig_insn->sec, orig_insn->offset,
+ WARN_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d",
orig_alt_group->last_insn->offset +
orig_alt_group->last_insn->len -
orig_alt_group->first_insn->offset,
@@ -1878,8 +1877,7 @@ static int handle_group_alt(struct objtool_file *file,
if (alt_reloc && arch_pc_relative_reloc(alt_reloc) &&
!arch_support_alt_relocation(special_alt, insn, alt_reloc)) {
- WARN_FUNC("unsupported relocation in alternatives section",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unsupported relocation in alternatives section");
return -1;
}
@@ -1893,8 +1891,7 @@ static int handle_group_alt(struct objtool_file *file,
if (dest_off == special_alt->new_off + special_alt->new_len) {
insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
if (!insn->jump_dest) {
- WARN_FUNC("can't find alternative jump destination",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "can't find alternative jump destination");
return -1;
}
}
@@ -1928,8 +1925,7 @@ static int handle_jump_alt(struct objtool_file *file,
if (orig_insn->type != INSN_JUMP_UNCONDITIONAL &&
orig_insn->type != INSN_NOP) {
- WARN_FUNC("unsupported instruction at jump label",
- orig_insn->sec, orig_insn->offset);
+ WARN_INSN(orig_insn, "unsupported instruction at jump label");
return -1;
}
@@ -2008,8 +2004,7 @@ static int add_special_section_alts(struct objtool_file *file)
if (special_alt->group) {
if (!special_alt->orig_len) {
- WARN_FUNC("empty alternative entry",
- orig_insn->sec, orig_insn->offset);
+ WARN_INSN(orig_insn, "empty alternative entry");
continue;
}
@@ -2100,8 +2095,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn,
}
if (!prev_offset) {
- WARN_FUNC("can't find switch jump table",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "can't find switch jump table");
return -1;
}
@@ -2215,23 +2209,20 @@ static int add_func_jump_tables(struct objtool_file *file,
*/
static int add_jump_table_alts(struct objtool_file *file)
{
- struct section *sec;
struct symbol *func;
int ret;
if (!file->rodata)
return 0;
- for_each_sec(file, sec) {
- list_for_each_entry(func, &sec->symbol_list, list) {
- if (func->type != STT_FUNC)
- continue;
+ for_each_sym(file, func) {
+ if (func->type != STT_FUNC)
+ continue;
- mark_func_jump_tables(file, func);
- ret = add_func_jump_tables(file, func);
- if (ret)
- return ret;
- }
+ mark_func_jump_tables(file, func);
+ ret = add_func_jump_tables(file, func);
+ if (ret)
+ return ret;
}
return 0;
@@ -2243,6 +2234,7 @@ static void set_func_state(struct cfi_state *state)
memcpy(&state->regs, &initial_func_cfi.regs,
CFI_NUM_REGS * sizeof(struct cfi_reg));
state->stack_size = initial_func_cfi.cfa.offset;
+ state->type = UNWIND_HINT_TYPE_CALL;
}
static int read_unwind_hints(struct objtool_file *file)
@@ -2304,19 +2296,11 @@ static int read_unwind_hints(struct objtool_file *file)
if (sym && sym->bind == STB_GLOBAL) {
if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) {
- WARN_FUNC("UNWIND_HINT_IRET_REGS without ENDBR",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR");
}
-
- insn->entry = 1;
}
}
- if (hint->type == UNWIND_HINT_TYPE_ENTRY) {
- hint->type = UNWIND_HINT_TYPE_CALL;
- insn->entry = 1;
- }
-
if (hint->type == UNWIND_HINT_TYPE_FUNC) {
insn->cfi = &func_cfi;
continue;
@@ -2326,15 +2310,13 @@ static int read_unwind_hints(struct objtool_file *file)
cfi = *(insn->cfi);
if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
- WARN_FUNC("unsupported unwind_hint sp base reg %d",
- insn->sec, insn->offset, hint->sp_reg);
+ WARN_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg);
return -1;
}
cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
cfi.type = hint->type;
cfi.signal = hint->signal;
- cfi.end = hint->end;
insn->cfi = cfi_hash_find_or_add(&cfi);
}
@@ -2391,8 +2373,7 @@ static int read_retpoline_hints(struct objtool_file *file)
insn->type != INSN_CALL_DYNAMIC &&
insn->type != INSN_RETURN &&
insn->type != INSN_NOP) {
- WARN_FUNC("retpoline_safe hint not an indirect jump/call/ret/nop",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop");
return -1;
}
@@ -2449,6 +2430,34 @@ static int read_instr_hints(struct objtool_file *file)
return 0;
}
+static int read_validate_unret_hints(struct objtool_file *file)
+{
+ struct section *sec;
+ struct instruction *insn;
+ struct reloc *reloc;
+
+ sec = find_section_by_name(file->elf, ".rela.discard.validate_unret");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(reloc, &sec->reloc_list, list) {
+ if (reloc->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+
+ insn = find_insn(file, reloc->sym->sec, reloc->addend);
+ if (!insn) {
+ WARN("bad .discard.instr_end entry");
+ return -1;
+ }
+ insn->unret = 1;
+ }
+
+ return 0;
+}
+
+
static int read_intra_function_calls(struct objtool_file *file)
{
struct instruction *insn;
@@ -2475,8 +2484,7 @@ static int read_intra_function_calls(struct objtool_file *file)
}
if (insn->type != INSN_CALL) {
- WARN_FUNC("intra_function_call not a direct call",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "intra_function_call not a direct call");
return -1;
}
@@ -2490,8 +2498,7 @@ static int read_intra_function_calls(struct objtool_file *file)
dest_off = arch_jump_destination(insn);
insn->jump_dest = find_insn(file, insn->sec, dest_off);
if (!insn->jump_dest) {
- WARN_FUNC("can't find call dest at %s+0x%lx",
- insn->sec, insn->offset,
+ WARN_INSN(insn, "can't find call dest at %s+0x%lx",
insn->sec->name, dest_off);
return -1;
}
@@ -2527,30 +2534,27 @@ static bool is_profiling_func(const char *name)
static int classify_symbols(struct objtool_file *file)
{
- struct section *sec;
struct symbol *func;
- for_each_sec(file, sec) {
- list_for_each_entry(func, &sec->symbol_list, list) {
- if (func->bind != STB_GLOBAL)
- continue;
+ for_each_sym(file, func) {
+ if (func->bind != STB_GLOBAL)
+ continue;
- if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
- strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
- func->static_call_tramp = true;
+ if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
+ strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
+ func->static_call_tramp = true;
- if (arch_is_retpoline(func))
- func->retpoline_thunk = true;
+ if (arch_is_retpoline(func))
+ func->retpoline_thunk = true;
- if (arch_is_rethunk(func))
- func->return_thunk = true;
+ if (arch_is_rethunk(func))
+ func->return_thunk = true;
- if (arch_ftrace_match(func->name))
- func->fentry = true;
+ if (arch_ftrace_match(func->name))
+ func->fentry = true;
- if (is_profiling_func(func->name))
- func->profiling_func = true;
- }
+ if (is_profiling_func(func->name))
+ func->profiling_func = true;
}
return 0;
@@ -2667,6 +2671,10 @@ static int decode_sections(struct objtool_file *file)
if (ret)
return ret;
+ ret = read_validate_unret_hints(file);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -2828,7 +2836,7 @@ static int update_cfi_state(struct instruction *insn,
/* stack operations don't make sense with an undefined CFA */
if (cfa->base == CFI_UNDEFINED) {
if (insn_func(insn)) {
- WARN_FUNC("undefined stack state", insn->sec, insn->offset);
+ WARN_INSN(insn, "undefined stack state");
return -1;
}
return 0;
@@ -2977,17 +2985,6 @@ static int update_cfi_state(struct instruction *insn,
break;
}
- if (!cfi->drap && op->src.reg == CFI_SP &&
- op->dest.reg == CFI_BP && cfa->base == CFI_SP &&
- check_reg_frame_pos(&regs[CFI_BP], -cfa->offset + op->src.offset)) {
-
- /* lea disp(%rsp), %rbp */
- cfa->base = CFI_BP;
- cfa->offset -= op->src.offset;
- cfi->bp_scratch = false;
- break;
- }
-
if (op->src.reg == CFI_SP && cfa->base == CFI_SP) {
/* drap: lea disp(%rsp), %drap */
@@ -3022,8 +3019,7 @@ static int update_cfi_state(struct instruction *insn,
}
if (op->dest.reg == cfi->cfa.base && !(next_insn && next_insn->hint)) {
- WARN_FUNC("unsupported stack register modification",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unsupported stack register modification");
return -1;
}
@@ -3033,8 +3029,7 @@ static int update_cfi_state(struct instruction *insn,
if (op->dest.reg != CFI_SP ||
(cfi->drap_reg != CFI_UNDEFINED && cfa->base != CFI_SP) ||
(cfi->drap_reg == CFI_UNDEFINED && cfa->base != CFI_BP)) {
- WARN_FUNC("unsupported stack pointer realignment",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unsupported stack pointer realignment");
return -1;
}
@@ -3129,8 +3124,7 @@ static int update_cfi_state(struct instruction *insn,
break;
default:
- WARN_FUNC("unknown stack-related instruction",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unknown stack-related instruction");
return -1;
}
@@ -3219,8 +3213,7 @@ static int update_cfi_state(struct instruction *insn,
case OP_DEST_MEM:
if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) {
- WARN_FUNC("unknown stack-related memory operation",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unknown stack-related memory operation");
return -1;
}
@@ -3232,8 +3225,7 @@ static int update_cfi_state(struct instruction *insn,
break;
default:
- WARN_FUNC("unknown stack-related instruction",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unknown stack-related instruction");
return -1;
}
@@ -3272,8 +3264,7 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn
struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group;
struct instruction *orig = orig_group->first_insn;
char *where = offstr(insn->sec, insn->offset);
- WARN_FUNC("stack layout conflict in alternatives: %s",
- orig->sec, orig->offset, where);
+ WARN_INSN(orig, "stack layout conflict in alternatives: %s", where);
free(where);
return -1;
}
@@ -3300,8 +3291,7 @@ static int handle_insn_ops(struct instruction *insn,
if (!state->uaccess_stack) {
state->uaccess_stack = 1;
} else if (state->uaccess_stack >> 31) {
- WARN_FUNC("PUSHF stack exhausted",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "PUSHF stack exhausted");
return 1;
}
state->uaccess_stack <<= 1;
@@ -3333,8 +3323,7 @@ static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
if (memcmp(&cfi1->cfa, &cfi2->cfa, sizeof(cfi1->cfa))) {
- WARN_FUNC("stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
- insn->sec, insn->offset,
+ WARN_INSN(insn, "stack state mismatch: cfa1=%d%+d cfa2=%d%+d",
cfi1->cfa.base, cfi1->cfa.offset,
cfi2->cfa.base, cfi2->cfa.offset);
@@ -3344,8 +3333,7 @@ static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
sizeof(struct cfi_reg)))
continue;
- WARN_FUNC("stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
- insn->sec, insn->offset,
+ WARN_INSN(insn, "stack state mismatch: reg1[%d]=%d%+d reg2[%d]=%d%+d",
i, cfi1->regs[i].base, cfi1->regs[i].offset,
i, cfi2->regs[i].base, cfi2->regs[i].offset);
break;
@@ -3353,15 +3341,14 @@ static bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2)
} else if (cfi1->type != cfi2->type) {
- WARN_FUNC("stack state mismatch: type1=%d type2=%d",
- insn->sec, insn->offset, cfi1->type, cfi2->type);
+ WARN_INSN(insn, "stack state mismatch: type1=%d type2=%d",
+ cfi1->type, cfi2->type);
} else if (cfi1->drap != cfi2->drap ||
(cfi1->drap && cfi1->drap_reg != cfi2->drap_reg) ||
(cfi1->drap && cfi1->drap_offset != cfi2->drap_offset)) {
- WARN_FUNC("stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
- insn->sec, insn->offset,
+ WARN_INSN(insn, "stack state mismatch: drap1=%d(%d,%d) drap2=%d(%d,%d)",
cfi1->drap, cfi1->drap_reg, cfi1->drap_offset,
cfi2->drap, cfi2->drap_reg, cfi2->drap_offset);
@@ -3469,20 +3456,17 @@ static int validate_call(struct objtool_file *file,
{
if (state->noinstr && state->instr <= 0 &&
!noinstr_call_dest(file, insn, insn_call_dest(insn))) {
- WARN_FUNC("call to %s() leaves .noinstr.text section",
- insn->sec, insn->offset, call_dest_name(insn));
+ WARN_INSN(insn, "call to %s() leaves .noinstr.text section", call_dest_name(insn));
return 1;
}
if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) {
- WARN_FUNC("call to %s() with UACCESS enabled",
- insn->sec, insn->offset, call_dest_name(insn));
+ WARN_INSN(insn, "call to %s() with UACCESS enabled", call_dest_name(insn));
return 1;
}
if (state->df) {
- WARN_FUNC("call to %s() with DF set",
- insn->sec, insn->offset, call_dest_name(insn));
+ WARN_INSN(insn, "call to %s() with DF set", call_dest_name(insn));
return 1;
}
@@ -3494,8 +3478,7 @@ static int validate_sibling_call(struct objtool_file *file,
struct insn_state *state)
{
if (insn_func(insn) && has_modified_stack_frame(insn, state)) {
- WARN_FUNC("sibling call from callable instruction with modified stack frame",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "sibling call from callable instruction with modified stack frame");
return 1;
}
@@ -3505,38 +3488,32 @@ static int validate_sibling_call(struct objtool_file *file,
static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
{
if (state->noinstr && state->instr > 0) {
- WARN_FUNC("return with instrumentation enabled",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "return with instrumentation enabled");
return 1;
}
if (state->uaccess && !func_uaccess_safe(func)) {
- WARN_FUNC("return with UACCESS enabled",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "return with UACCESS enabled");
return 1;
}
if (!state->uaccess && func_uaccess_safe(func)) {
- WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "return with UACCESS disabled from a UACCESS-safe function");
return 1;
}
if (state->df) {
- WARN_FUNC("return with DF set",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "return with DF set");
return 1;
}
if (func && has_modified_stack_frame(insn, state)) {
- WARN_FUNC("return with modified stack frame",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "return with modified stack frame");
return 1;
}
if (state->cfi.bp_scratch) {
- WARN_FUNC("BP used as a scratch register",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "BP used as a scratch register");
return 1;
}
@@ -3608,8 +3585,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
}
if (func && insn->ignore) {
- WARN_FUNC("BUG: why am I validating an ignored function?",
- sec, insn->offset);
+ WARN_INSN(insn, "BUG: why am I validating an ignored function?");
return 1;
}
@@ -3642,14 +3618,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
}
if (!save_insn) {
- WARN_FUNC("no corresponding CFI save for CFI restore",
- sec, insn->offset);
+ WARN_INSN(insn, "no corresponding CFI save for CFI restore");
return 1;
}
if (!save_insn->visited) {
- WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo",
- sec, insn->offset);
+ WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
return 1;
}
@@ -3709,8 +3683,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
if (opts.stackval && func && !is_fentry_call(insn) &&
!has_valid_stack_frame(&state)) {
- WARN_FUNC("call without frame pointer save/setup",
- sec, insn->offset);
+ WARN_INSN(insn, "call without frame pointer save/setup");
return 1;
}
@@ -3756,15 +3729,14 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
case INSN_CONTEXT_SWITCH:
if (func && (!next_insn || !next_insn->hint)) {
- WARN_FUNC("unsupported instruction in callable function",
- sec, insn->offset);
+ WARN_INSN(insn, "unsupported instruction in callable function");
return 1;
}
return 0;
case INSN_STAC:
if (state.uaccess) {
- WARN_FUNC("recursive UACCESS enable", sec, insn->offset);
+ WARN_INSN(insn, "recursive UACCESS enable");
return 1;
}
@@ -3773,12 +3745,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
case INSN_CLAC:
if (!state.uaccess && func) {
- WARN_FUNC("redundant UACCESS disable", sec, insn->offset);
+ WARN_INSN(insn, "redundant UACCESS disable");
return 1;
}
if (func_uaccess_safe(func) && !state.uaccess_stack) {
- WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset);
+ WARN_INSN(insn, "UACCESS-safe disables UACCESS");
return 1;
}
@@ -3787,7 +3759,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
case INSN_STD:
if (state.df) {
- WARN_FUNC("recursive STD", sec, insn->offset);
+ WARN_INSN(insn, "recursive STD");
return 1;
}
@@ -3796,7 +3768,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
case INSN_CLD:
if (!state.df && func) {
- WARN_FUNC("redundant CLD", sec, insn->offset);
+ WARN_INSN(insn, "redundant CLD");
return 1;
}
@@ -3863,10 +3835,10 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec)
/*
* Validate rethunk entry constraint: must untrain RET before the first RET.
*
- * Follow every branch (intra-function) and ensure ANNOTATE_UNRET_END comes
+ * Follow every branch (intra-function) and ensure VALIDATE_UNRET_END comes
* before an actual RET instruction.
*/
-static int validate_entry(struct objtool_file *file, struct instruction *insn)
+static int validate_unret(struct objtool_file *file, struct instruction *insn)
{
struct instruction *next, *dest;
int ret, warnings = 0;
@@ -3874,10 +3846,10 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
for (;;) {
next = next_insn_to_validate(file, insn);
- if (insn->visited & VISITED_ENTRY)
+ if (insn->visited & VISITED_UNRET)
return 0;
- insn->visited |= VISITED_ENTRY;
+ insn->visited |= VISITED_UNRET;
if (!insn->ignore_alts && insn->alts) {
struct alternative *alt;
@@ -3887,7 +3859,7 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
if (alt->skip_orig)
skip_orig = true;
- ret = validate_entry(file, alt->insn);
+ ret = validate_unret(file, alt->insn);
if (ret) {
if (opts.backtrace)
BT_FUNC("(alt)", insn);
@@ -3904,18 +3876,17 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
case INSN_CALL_DYNAMIC:
case INSN_JUMP_DYNAMIC:
case INSN_JUMP_DYNAMIC_CONDITIONAL:
- WARN_FUNC("early indirect call", insn->sec, insn->offset);
+ WARN_INSN(insn, "early indirect call");
return 1;
case INSN_JUMP_UNCONDITIONAL:
case INSN_JUMP_CONDITIONAL:
if (!is_sibling_call(insn)) {
if (!insn->jump_dest) {
- WARN_FUNC("unresolved jump target after linking?!?",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "unresolved jump target after linking?!?");
return -1;
}
- ret = validate_entry(file, insn->jump_dest);
+ ret = validate_unret(file, insn->jump_dest);
if (ret) {
if (opts.backtrace) {
BT_FUNC("(branch%s)", insn,
@@ -3940,7 +3911,7 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
return -1;
}
- ret = validate_entry(file, dest);
+ ret = validate_unret(file, dest);
if (ret) {
if (opts.backtrace)
BT_FUNC("(call)", insn);
@@ -3953,7 +3924,7 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
return 0;
case INSN_RETURN:
- WARN_FUNC("RET before UNTRAIN", insn->sec, insn->offset);
+ WARN_INSN(insn, "RET before UNTRAIN");
return 1;
case INSN_NOP:
@@ -3966,7 +3937,7 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
}
if (!next) {
- WARN_FUNC("teh end!", insn->sec, insn->offset);
+ WARN_INSN(insn, "teh end!");
return -1;
}
insn = next;
@@ -3976,21 +3947,21 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn)
}
/*
- * Validate that all branches starting at 'insn->entry' encounter UNRET_END
- * before RET.
+ * Validate that all branches starting at VALIDATE_UNRET_BEGIN encounter
+ * VALIDATE_UNRET_END before RET.
*/
-static int validate_unret(struct objtool_file *file)
+static int validate_unrets(struct objtool_file *file)
{
struct instruction *insn;
int ret, warnings = 0;
for_each_insn(file, insn) {
- if (!insn->entry)
+ if (!insn->unret)
continue;
- ret = validate_entry(file, insn);
+ ret = validate_unret(file, insn);
if (ret < 0) {
- WARN_FUNC("Failed UNRET validation", insn->sec, insn->offset);
+ WARN_INSN(insn, "Failed UNRET validation");
return ret;
}
warnings += ret;
@@ -4018,13 +3989,11 @@ static int validate_retpoline(struct objtool_file *file)
if (insn->type == INSN_RETURN) {
if (opts.rethunk) {
- WARN_FUNC("'naked' return found in RETHUNK build",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "'naked' return found in RETHUNK build");
} else
continue;
} else {
- WARN_FUNC("indirect %s found in RETPOLINE build",
- insn->sec, insn->offset,
+ WARN_INSN(insn, "indirect %s found in RETPOLINE build",
insn->type == INSN_JUMP_DYNAMIC ? "jump" : "call");
}
@@ -4121,8 +4090,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
* It may also insert a UD2 after calling a __noreturn function.
*/
prev_insn = prev_insn_same_sec(file, insn);
- if ((prev_insn->dead_end ||
- dead_end_function(file, insn_call_dest(prev_insn))) &&
+ if (prev_insn->dead_end &&
(insn->type == INSN_BUG ||
(insn->type == INSN_JUMP_UNCONDITIONAL &&
insn->jump_dest && insn->jump_dest->type == INSN_BUG)))
@@ -4158,54 +4126,75 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
return false;
}
-static int add_prefix_symbol(struct objtool_file *file, struct symbol *func,
- struct instruction *insn)
+static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)
{
- if (!opts.prefix)
- return 0;
+ struct instruction *insn, *prev;
+ struct cfi_state *cfi;
- for (;;) {
- struct instruction *prev = prev_insn_same_sec(file, insn);
- u64 offset;
+ insn = find_insn(file, func->sec, func->offset);
+ if (!insn)
+ return -1;
- if (!prev)
- break;
+ for (prev = prev_insn_same_sec(file, insn);
+ prev;
+ prev = prev_insn_same_sec(file, prev)) {
+ u64 offset;
if (prev->type != INSN_NOP)
- break;
+ return -1;
offset = func->offset - prev->offset;
- if (offset >= opts.prefix) {
- if (offset == opts.prefix) {
- /*
- * Since the sec->symbol_list is ordered by
- * offset (see elf_add_symbol()) the added
- * symbol will not be seen by the iteration in
- * validate_section().
- *
- * Hence the lack of list_for_each_entry_safe()
- * there.
- *
- * The direct concequence is that prefix symbols
- * don't get visited (because pointless), except
- * for the logic in ignore_unreachable_insn()
- * that needs the terminating insn to be visited
- * otherwise it will report the hole.
- *
- * Hence mark the first instruction of the
- * prefix symbol as visisted.
- */
- prev->visited |= VISITED_BRANCH;
- elf_create_prefix_symbol(file->elf, func, opts.prefix);
- }
- break;
- }
- insn = prev;
+
+ if (offset > opts.prefix)
+ return -1;
+
+ if (offset < opts.prefix)
+ continue;
+
+ elf_create_prefix_symbol(file->elf, func, opts.prefix);
+ break;
}
+ if (!prev)
+ return -1;
+
+ if (!insn->cfi) {
+ /*
+ * This can happen if stack validation isn't enabled or the
+ * function is annotated with STACK_FRAME_NON_STANDARD.
+ */
+ return 0;
+ }
+
+ /* Propagate insn->cfi to the prefix code */
+ cfi = cfi_hash_find_or_add(insn->cfi);
+ for (; prev != insn; prev = next_insn_same_sec(file, prev))
+ prev->cfi = cfi;
+
return 0;
}
+static int add_prefix_symbols(struct objtool_file *file)
+{
+ struct section *sec;
+ struct symbol *func;
+ int warnings = 0;
+
+ for_each_sec(file, sec) {
+ if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+ continue;
+
+ sec_for_each_sym(sec, func) {
+ if (func->type != STT_FUNC)
+ continue;
+
+ add_prefix_symbol(file, func);
+ }
+ }
+
+ return warnings;
+}
+
static int validate_symbol(struct objtool_file *file, struct section *sec,
struct symbol *sym, struct insn_state *state)
{
@@ -4224,8 +4213,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
if (!insn || insn->ignore || insn->visited)
return 0;
- add_prefix_symbol(file, sym, insn);
-
state->uaccess = sym->uaccess_safe;
ret = validate_branch(file, insn_func(insn), insn, *state);
@@ -4240,7 +4227,7 @@ static int validate_section(struct objtool_file *file, struct section *sec)
struct symbol *func;
int warnings = 0;
- list_for_each_entry(func, &sec->symbol_list, list) {
+ sec_for_each_sym(sec, func) {
if (func->type != STT_FUNC)
continue;
@@ -4403,9 +4390,7 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn
if (noendbr_range(file, dest))
continue;
- WARN_FUNC("relocation to !ENDBR: %s",
- insn->sec, insn->offset,
- offstr(dest->sec, dest->offset));
+ WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset));
warnings++;
}
@@ -4507,16 +4492,14 @@ static int validate_sls(struct objtool_file *file)
switch (insn->type) {
case INSN_RETURN:
if (!next_insn || next_insn->type != INSN_TRAP) {
- WARN_FUNC("missing int3 after ret",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "missing int3 after ret");
warnings++;
}
break;
case INSN_JUMP_DYNAMIC:
if (!next_insn || next_insn->type != INSN_TRAP) {
- WARN_FUNC("missing int3 after indirect jump",
- insn->sec, insn->offset);
+ WARN_INSN(insn, "missing int3 after indirect jump");
warnings++;
}
break;
@@ -4539,7 +4522,7 @@ static int validate_reachable_instructions(struct objtool_file *file)
if (insn->visited || ignore_unreachable_insn(file, insn))
continue;
- WARN_FUNC("unreachable instruction", insn->sec, insn->offset);
+ WARN_INSN(insn, "unreachable instruction");
return 1;
}
@@ -4607,7 +4590,7 @@ int check(struct objtool_file *file)
* Must be after validate_branch() and friends, it plays
* further games with insn->visited.
*/
- ret = validate_unret(file);
+ ret = validate_unrets(file);
if (ret < 0)
return ret;
warnings += ret;
@@ -4669,6 +4652,13 @@ int check(struct objtool_file *file)
warnings += ret;
}
+ if (opts.prefix) {
+ ret = add_prefix_symbols(file);
+ if (ret < 0)
+ return ret;
+ warnings += ret;
+ }
+
if (opts.ibt) {
ret = create_ibt_endbr_seal_sections(file);
if (ret < 0)
diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 6806ce01d933..500e92979a31 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -474,7 +474,7 @@ static int read_symbols(struct elf *elf)
/* Create parent/child links for any cold subfunctions */
list_for_each_entry(sec, &elf->sections, list) {
- list_for_each_entry(sym, &sec->symbol_list, list) {
+ sec_for_each_sym(sec, sym) {
char pname[MAX_NAME_LEN + 1];
size_t pnamelen;
if (sym->type != STT_FUNC)
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index 3e7c7004f7df..daa46f1f0965 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -61,7 +61,7 @@ struct instruction {
restore : 1,
retpoline_safe : 1,
noendbr : 1,
- entry : 1,
+ unret : 1,
visited : 4,
no_reloc : 1;
/* 10 bit hole */
@@ -92,7 +92,7 @@ static inline struct symbol *insn_func(struct instruction *insn)
#define VISITED_BRANCH 0x01
#define VISITED_BRANCH_UACCESS 0x02
#define VISITED_BRANCH_MASK 0x03
-#define VISITED_ENTRY 0x04
+#define VISITED_UNRET 0x04
static inline bool is_static_jump(struct instruction *insn)
{
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index ad0024da262b..e1ca588eb69d 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -188,4 +188,13 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset);
#define for_each_sec(file, sec) \
list_for_each_entry(sec, &file->elf->sections, list)
+#define sec_for_each_sym(sec, sym) \
+ list_for_each_entry(sym, &sec->symbol_list, list)
+
+#define for_each_sym(file, sym) \
+ for (struct section *__sec, *__fake = (struct section *)1; \
+ __fake; __fake = NULL) \
+ for_each_sec(file, __sec) \
+ sec_for_each_sym(__sec, sym)
+
#endif /* _OBJTOOL_ELF_H */
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index a3e79ae75f2e..b1c920dc9516 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -53,6 +53,11 @@ static inline char *offstr(struct section *sec, unsigned long offset)
free(_str); \
})
+#define WARN_INSN(insn, format, ...) \
+({ \
+ WARN_FUNC(format, insn->sec, insn->offset, ##__VA_ARGS__); \
+})
+
#define BT_FUNC(format, insn, ...) \
({ \
struct instruction *_insn = (insn); \
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
index 2d8ebdcd1db3..0e183bb1c720 100644
--- a/tools/objtool/orc_dump.c
+++ b/tools/objtool/orc_dump.c
@@ -4,7 +4,6 @@
*/
#include <unistd.h>
-#include <linux/objtool.h>
#include <asm/orc_types.h>
#include <objtool/objtool.h>
#include <objtool/warn.h>
@@ -39,11 +38,15 @@ static const char *reg_name(unsigned int reg)
static const char *orc_type_name(unsigned int type)
{
switch (type) {
- case UNWIND_HINT_TYPE_CALL:
+ case ORC_TYPE_UNDEFINED:
+ return "(und)";
+ case ORC_TYPE_END_OF_STACK:
+ return "end";
+ case ORC_TYPE_CALL:
return "call";
- case UNWIND_HINT_TYPE_REGS:
+ case ORC_TYPE_REGS:
return "regs";
- case UNWIND_HINT_TYPE_REGS_PARTIAL:
+ case ORC_TYPE_REGS_PARTIAL:
return "regs (partial)";
default:
return "?";
@@ -202,6 +205,7 @@ int orc_dump(const char *_objname)
printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
}
+ printf("type:%s", orc_type_name(orc[i].type));
printf(" sp:");
@@ -211,8 +215,7 @@ int orc_dump(const char *_objname)
print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
- printf(" type:%s signal:%d end:%d\n",
- orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
+ printf(" signal:%d\n", orc[i].signal);
}
elf_end(elf);
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index 57a4527d5988..48efd1e2f00d 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -6,7 +6,7 @@
#include <stdlib.h>
#include <string.h>
-#include <linux/objtool.h>
+#include <linux/objtool_types.h>
#include <asm/orc_types.h>
#include <objtool/check.h>
@@ -21,19 +21,38 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
memset(orc, 0, sizeof(*orc));
if (!cfi) {
- orc->end = 0;
- orc->sp_reg = ORC_REG_UNDEFINED;
+ /*
+ * This is usually either unreachable nops/traps (which don't
+ * trigger unreachable instruction warnings), or
+ * STACK_FRAME_NON_STANDARD functions.
+ */
+ orc->type = ORC_TYPE_UNDEFINED;
return 0;
}
- orc->end = cfi->end;
- orc->signal = cfi->signal;
-
- if (cfi->cfa.base == CFI_UNDEFINED) {
- orc->sp_reg = ORC_REG_UNDEFINED;
+ switch (cfi->type) {
+ case UNWIND_HINT_TYPE_UNDEFINED:
+ orc->type = ORC_TYPE_UNDEFINED;
+ return 0;
+ case UNWIND_HINT_TYPE_END_OF_STACK:
+ orc->type = ORC_TYPE_END_OF_STACK;
return 0;
+ case UNWIND_HINT_TYPE_CALL:
+ orc->type = ORC_TYPE_CALL;
+ break;
+ case UNWIND_HINT_TYPE_REGS:
+ orc->type = ORC_TYPE_REGS;
+ break;
+ case UNWIND_HINT_TYPE_REGS_PARTIAL:
+ orc->type = ORC_TYPE_REGS_PARTIAL;
+ break;
+ default:
+ WARN_INSN(insn, "unknown unwind hint type %d", cfi->type);
+ return -1;
}
+ orc->signal = cfi->signal;
+
switch (cfi->cfa.base) {
case CFI_SP:
orc->sp_reg = ORC_REG_SP;
@@ -60,8 +79,7 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
orc->sp_reg = ORC_REG_DX;
break;
default:
- WARN_FUNC("unknown CFA base reg %d",
- insn->sec, insn->offset, cfi->cfa.base);
+ WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base);
return -1;
}
@@ -76,14 +94,12 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
orc->bp_reg = ORC_REG_BP;
break;
default:
- WARN_FUNC("unknown BP base reg %d",
- insn->sec, insn->offset, bp->base);
+ WARN_INSN(insn, "unknown BP base reg %d", bp->base);
return -1;
}
orc->sp_offset = cfi->cfa.offset;
orc->bp_offset = bp->offset;
- orc->type = cfi->type;
return 0;
}
@@ -148,11 +164,7 @@ int orc_create(struct objtool_file *file)
struct orc_list_entry *entry;
struct list_head orc_list;
- struct orc_entry null = {
- .sp_reg = ORC_REG_UNDEFINED,
- .bp_reg = ORC_REG_UNDEFINED,
- .type = UNWIND_HINT_TYPE_CALL,
- };
+ struct orc_entry null = { .type = ORC_TYPE_UNDEFINED };
/* Build a deduplicated list of ORC entries: */
INIT_LIST_HEAD(&orc_list);
diff --git a/tools/objtool/sync-check.sh b/tools/objtool/sync-check.sh
index 105a291ff8e7..81d120d05442 100755
--- a/tools/objtool/sync-check.sh
+++ b/tools/objtool/sync-check.sh
@@ -6,7 +6,7 @@ if [ -z "$SRCARCH" ]; then
exit 1
fi
-FILES="include/linux/objtool.h"
+FILES="include/linux/objtool_types.h"
if [ "$SRCARCH" = "x86" ]; then
FILES="$FILES
diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config
index 3519a0139026..c0a208f9b67b 100644
--- a/tools/perf/Makefile.config
+++ b/tools/perf/Makefile.config
@@ -38,7 +38,7 @@ ifneq ($(NO_SYSCALL_TABLE),1)
NO_SYSCALL_TABLE := 0
endif
else
- ifeq ($(SRCARCH),$(filter $(SRCARCH),powerpc arm64 s390 mips))
+ ifeq ($(SRCARCH),$(filter $(SRCARCH),powerpc arm64 s390 mips loongarch))
NO_SYSCALL_TABLE := 0
endif
endif
@@ -80,6 +80,12 @@ ifeq ($(SRCARCH),arm64)
LIBUNWIND_LIBS = -lunwind -lunwind-aarch64
endif
+ifeq ($(SRCARCH),loongarch)
+ NO_PERF_REGS := 0
+ CFLAGS += -I$(OUTPUT)arch/loongarch/include/generated
+ LIBUNWIND_LIBS = -lunwind -lunwind-loongarch64
+endif
+
ifeq ($(SRCARCH),riscv)
NO_PERF_REGS := 0
endif
@@ -107,7 +113,7 @@ endif
# Disable it on all other architectures in case libdw unwind
# support is detected in system. Add supported architectures
# to the check.
-ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv))
+ifneq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc s390 csky riscv loongarch))
NO_LIBDW_DWARF_UNWIND := 1
endif
@@ -129,7 +135,7 @@ endef
ifdef LIBUNWIND_DIR
LIBUNWIND_CFLAGS = -I$(LIBUNWIND_DIR)/include
LIBUNWIND_LDFLAGS = -L$(LIBUNWIND_DIR)/lib
- LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64
+ LIBUNWIND_ARCHS = x86 x86_64 arm aarch64 debug-frame-arm debug-frame-aarch64 loongarch
$(foreach libunwind_arch,$(LIBUNWIND_ARCHS),$(call libunwind_arch_set_flags,$(libunwind_arch)))
endif
diff --git a/tools/perf/arch/loongarch/Build b/tools/perf/arch/loongarch/Build
new file mode 100644
index 000000000000..e4e5f33c84d8
--- /dev/null
+++ b/tools/perf/arch/loongarch/Build
@@ -0,0 +1 @@
+perf-y += util/
diff --git a/tools/perf/arch/loongarch/Makefile b/tools/perf/arch/loongarch/Makefile
new file mode 100644
index 000000000000..c392e7af4743
--- /dev/null
+++ b/tools/perf/arch/loongarch/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+ifndef NO_DWARF
+PERF_HAVE_DWARF_REGS := 1
+endif
+PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
+PERF_HAVE_JITDUMP := 1
+
+#
+# Syscall table generation for perf
+#
+
+out := $(OUTPUT)arch/loongarch/include/generated/asm
+header := $(out)/syscalls.c
+incpath := $(srctree)/tools
+sysdef := $(srctree)/tools/arch/loongarch/include/uapi/asm/unistd.h
+sysprf := $(srctree)/tools/perf/arch/loongarch/entry/syscalls/
+systbl := $(sysprf)/mksyscalltbl
+
+# Create output directory if not already present
+_dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)')
+
+$(header): $(sysdef) $(systbl)
+ $(Q)$(SHELL) '$(systbl)' '$(CC)' '$(HOSTCC)' $(incpath) $(sysdef) > $@
+
+clean::
+ $(call QUIET_CLEAN, loongarch) $(RM) $(header)
+
+archheaders: $(header)
diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c
new file mode 100644
index 000000000000..ab21bf122135
--- /dev/null
+++ b/tools/perf/arch/loongarch/annotate/instructions.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Perf annotate functions.
+ *
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+static
+struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name)
+{
+ struct ins_ops *ops = NULL;
+
+ if (!strncmp(name, "beqz", 4) ||
+ !strncmp(name, "bnez", 4) ||
+ !strncmp(name, "beq", 3) ||
+ !strncmp(name, "bne", 3) ||
+ !strncmp(name, "blt", 3) ||
+ !strncmp(name, "bge", 3) ||
+ !strncmp(name, "bltu", 4) ||
+ !strncmp(name, "bgeu", 4) ||
+ !strncmp(name, "bl", 2))
+ ops = &call_ops;
+ else if (!strncmp(name, "jirl", 4))
+ ops = &ret_ops;
+ else if (name[0] == 'b')
+ ops = &jump_ops;
+ else
+ return NULL;
+
+ arch__associate_ins_ops(arch, name, ops);
+
+ return ops;
+}
+
+static
+int loongarch__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
+{
+ if (!arch->initialized) {
+ arch->associate_instruction_ops = loongarch__associate_ins_ops;
+ arch->initialized = true;
+ arch->objdump.comment_char = '#';
+ }
+
+ return 0;
+}
diff --git a/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl b/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl
new file mode 100755
index 000000000000..c52156f7204d
--- /dev/null
+++ b/tools/perf/arch/loongarch/entry/syscalls/mksyscalltbl
@@ -0,0 +1,61 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+#
+# Generate system call table for perf. Derived from
+# powerpc script.
+#
+# Author(s): Ming Wang <wangming01@loongson.cn>
+# Author(s): Huacai Chen <chenhuacai@loongson.cn>
+# Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+
+gcc=$1
+hostcc=$2
+incpath=$3
+input=$4
+
+if ! test -r $input; then
+ echo "Could not read input file" >&2
+ exit 1
+fi
+
+create_table_from_c()
+{
+ local sc nr last_sc
+
+ create_table_exe=`mktemp ${TMPDIR:-/tmp}/create-table-XXXXXX`
+
+ {
+
+ cat <<-_EoHEADER
+ #include <stdio.h>
+ #include "$input"
+ int main(int argc, char *argv[])
+ {
+ _EoHEADER
+
+ while read sc nr; do
+ printf "%s\n" " printf(\"\\t[%d] = \\\"$sc\\\",\\n\", $nr);"
+ last_sc=$nr
+ done
+
+ printf "%s\n" " printf(\"#define SYSCALLTBL_LOONGARCH_MAX_ID %d\\n\", $last_sc);"
+ printf "}\n"
+
+ } | $hostcc -I $incpath/include/uapi -o $create_table_exe -x c -
+
+ $create_table_exe
+
+ rm -f $create_table_exe
+}
+
+create_table()
+{
+ echo "static const char *syscalltbl_loongarch[] = {"
+ create_table_from_c
+ echo "};"
+}
+
+$gcc -E -dM -x c -I $incpath/include/uapi $input \
+ |sed -ne 's/^#define __NR_//p' \
+ |sort -t' ' -k2 -n \
+ |create_table
diff --git a/tools/perf/arch/loongarch/include/dwarf-regs-table.h b/tools/perf/arch/loongarch/include/dwarf-regs-table.h
new file mode 100644
index 000000000000..bb3944f5764a
--- /dev/null
+++ b/tools/perf/arch/loongarch/include/dwarf-regs-table.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * dwarf-regs-table.h : Mapping of DWARF debug register numbers into
+ * register names.
+ *
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#ifdef DEFINE_DWARF_REGSTR_TABLE
+static const char * const loongarch_regstr_tbl[] = {
+ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7",
+ "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15",
+ "%r16", "%r17", "%r18", "%r19", "%r20", "%r21", "%r22", "%r23",
+ "%r24", "%r25", "%r26", "%r27", "%r28", "%r29", "%r30", "%r31",
+};
+#endif
diff --git a/tools/perf/arch/loongarch/include/perf_regs.h b/tools/perf/arch/loongarch/include/perf_regs.h
new file mode 100644
index 000000000000..7833c7dbd38d
--- /dev/null
+++ b/tools/perf/arch/loongarch/include/perf_regs.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef ARCH_PERF_REGS_H
+#define ARCH_PERF_REGS_H
+
+#include <stdlib.h>
+#include <linux/types.h>
+#include <asm/perf_regs.h>
+
+#define PERF_REGS_MAX PERF_REG_LOONGARCH_MAX
+#define PERF_REG_IP PERF_REG_LOONGARCH_PC
+#define PERF_REG_SP PERF_REG_LOONGARCH_R3
+
+#define PERF_REGS_MASK ((1ULL << PERF_REG_LOONGARCH_MAX) - 1)
+
+#endif /* ARCH_PERF_REGS_H */
diff --git a/tools/perf/arch/loongarch/util/Build b/tools/perf/arch/loongarch/util/Build
new file mode 100644
index 000000000000..d776125a2d06
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/Build
@@ -0,0 +1,5 @@
+perf-y += perf_regs.o
+
+perf-$(CONFIG_DWARF) += dwarf-regs.o
+perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o
+perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o
diff --git a/tools/perf/arch/loongarch/util/dwarf-regs.c b/tools/perf/arch/loongarch/util/dwarf-regs.c
new file mode 100644
index 000000000000..0f6ebc387463
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/dwarf-regs.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwarf-regs.c : Mapping of DWARF debug register numbers into register names.
+ *
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <stdio.h>
+#include <errno.h> /* for EINVAL */
+#include <string.h> /* for strcmp */
+#include <dwarf-regs.h>
+
+struct pt_regs_dwarfnum {
+ const char *name;
+ unsigned int dwarfnum;
+};
+
+static struct pt_regs_dwarfnum loongarch_gpr_table[] = {
+ {"%r0", 0}, {"%r1", 1}, {"%r2", 2}, {"%r3", 3},
+ {"%r4", 4}, {"%r5", 5}, {"%r6", 6}, {"%r7", 7},
+ {"%r8", 8}, {"%r9", 9}, {"%r10", 10}, {"%r11", 11},
+ {"%r12", 12}, {"%r13", 13}, {"%r14", 14}, {"%r15", 15},
+ {"%r16", 16}, {"%r17", 17}, {"%r18", 18}, {"%r19", 19},
+ {"%r20", 20}, {"%r21", 21}, {"%r22", 22}, {"%r23", 23},
+ {"%r24", 24}, {"%r25", 25}, {"%r26", 26}, {"%r27", 27},
+ {"%r28", 28}, {"%r29", 29}, {"%r30", 30}, {"%r31", 31},
+ {NULL, 0}
+};
+
+const char *get_arch_regstr(unsigned int n)
+{
+ n %= 32;
+ return loongarch_gpr_table[n].name;
+}
+
+int regs_query_register_offset(const char *name)
+{
+ const struct pt_regs_dwarfnum *roff;
+
+ for (roff = loongarch_gpr_table; roff->name != NULL; roff++)
+ if (!strcmp(roff->name, name))
+ return roff->dwarfnum;
+ return -EINVAL;
+}
diff --git a/tools/perf/arch/loongarch/util/perf_regs.c b/tools/perf/arch/loongarch/util/perf_regs.c
new file mode 100644
index 000000000000..2833e101a7c6
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/perf_regs.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "../../../util/perf_regs.h"
+
+const struct sample_reg sample_reg_masks[] = {
+ SMPL_REG_END
+};
diff --git a/tools/perf/arch/loongarch/util/unwind-libdw.c b/tools/perf/arch/loongarch/util/unwind-libdw.c
new file mode 100644
index 000000000000..a9415385230a
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/unwind-libdw.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2020-2023 Loongson Technology Corporation Limited */
+
+#include <elfutils/libdwfl.h>
+#include "../../util/unwind-libdw.h"
+#include "../../util/perf_regs.h"
+#include "../../util/sample.h"
+
+bool libdw__arch_set_initial_registers(Dwfl_Thread *thread, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct regs_dump *user_regs = &ui->sample->user_regs;
+ Dwarf_Word dwarf_regs[PERF_REG_LOONGARCH_MAX];
+
+#define REG(r) ({ \
+ Dwarf_Word val = 0; \
+ perf_reg_value(&val, user_regs, PERF_REG_LOONGARCH_##r); \
+ val; \
+})
+
+ dwarf_regs[0] = 0;
+ dwarf_regs[1] = REG(R1);
+ dwarf_regs[2] = REG(R2);
+ dwarf_regs[3] = REG(R3);
+ dwarf_regs[4] = REG(R4);
+ dwarf_regs[5] = REG(R5);
+ dwarf_regs[6] = REG(R6);
+ dwarf_regs[7] = REG(R7);
+ dwarf_regs[8] = REG(R8);
+ dwarf_regs[9] = REG(R9);
+ dwarf_regs[10] = REG(R10);
+ dwarf_regs[11] = REG(R11);
+ dwarf_regs[12] = REG(R12);
+ dwarf_regs[13] = REG(R13);
+ dwarf_regs[14] = REG(R14);
+ dwarf_regs[15] = REG(R15);
+ dwarf_regs[16] = REG(R16);
+ dwarf_regs[17] = REG(R17);
+ dwarf_regs[18] = REG(R18);
+ dwarf_regs[19] = REG(R19);
+ dwarf_regs[20] = REG(R20);
+ dwarf_regs[21] = REG(R21);
+ dwarf_regs[22] = REG(R22);
+ dwarf_regs[23] = REG(R23);
+ dwarf_regs[24] = REG(R24);
+ dwarf_regs[25] = REG(R25);
+ dwarf_regs[26] = REG(R26);
+ dwarf_regs[27] = REG(R27);
+ dwarf_regs[28] = REG(R28);
+ dwarf_regs[29] = REG(R29);
+ dwarf_regs[30] = REG(R30);
+ dwarf_regs[31] = REG(R31);
+ dwfl_thread_state_register_pc(thread, REG(PC));
+
+ return dwfl_thread_state_registers(thread, 0, PERF_REG_LOONGARCH_MAX, dwarf_regs);
+}
diff --git a/tools/perf/arch/loongarch/util/unwind-libunwind.c b/tools/perf/arch/loongarch/util/unwind-libunwind.c
new file mode 100644
index 000000000000..f693167b86ef
--- /dev/null
+++ b/tools/perf/arch/loongarch/util/unwind-libunwind.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <errno.h>
+#include <libunwind.h>
+#include "perf_regs.h"
+#include "../../util/unwind.h"
+#include "util/debug.h"
+
+int libunwind__arch_reg_id(int regnum)
+{
+ switch (regnum) {
+ case UNW_LOONGARCH64_R1:
+ return PERF_REG_LOONGARCH_R1;
+ case UNW_LOONGARCH64_R2:
+ return PERF_REG_LOONGARCH_R2;
+ case UNW_LOONGARCH64_R3:
+ return PERF_REG_LOONGARCH_R3;
+ case UNW_LOONGARCH64_R4:
+ return PERF_REG_LOONGARCH_R4;
+ case UNW_LOONGARCH64_R5:
+ return PERF_REG_LOONGARCH_R5;
+ case UNW_LOONGARCH64_R6:
+ return PERF_REG_LOONGARCH_R6;
+ case UNW_LOONGARCH64_R7:
+ return PERF_REG_LOONGARCH_R7;
+ case UNW_LOONGARCH64_R8:
+ return PERF_REG_LOONGARCH_R8;
+ case UNW_LOONGARCH64_R9:
+ return PERF_REG_LOONGARCH_R9;
+ case UNW_LOONGARCH64_R10:
+ return PERF_REG_LOONGARCH_R10;
+ case UNW_LOONGARCH64_R11:
+ return PERF_REG_LOONGARCH_R11;
+ case UNW_LOONGARCH64_R12:
+ return PERF_REG_LOONGARCH_R12;
+ case UNW_LOONGARCH64_R13:
+ return PERF_REG_LOONGARCH_R13;
+ case UNW_LOONGARCH64_R14:
+ return PERF_REG_LOONGARCH_R14;
+ case UNW_LOONGARCH64_R15:
+ return PERF_REG_LOONGARCH_R15;
+ case UNW_LOONGARCH64_R16:
+ return PERF_REG_LOONGARCH_R16;
+ case UNW_LOONGARCH64_R17:
+ return PERF_REG_LOONGARCH_R17;
+ case UNW_LOONGARCH64_R18:
+ return PERF_REG_LOONGARCH_R18;
+ case UNW_LOONGARCH64_R19:
+ return PERF_REG_LOONGARCH_R19;
+ case UNW_LOONGARCH64_R20:
+ return PERF_REG_LOONGARCH_R20;
+ case UNW_LOONGARCH64_R21:
+ return PERF_REG_LOONGARCH_R21;
+ case UNW_LOONGARCH64_R22:
+ return PERF_REG_LOONGARCH_R22;
+ case UNW_LOONGARCH64_R23:
+ return PERF_REG_LOONGARCH_R23;
+ case UNW_LOONGARCH64_R24:
+ return PERF_REG_LOONGARCH_R24;
+ case UNW_LOONGARCH64_R25:
+ return PERF_REG_LOONGARCH_R25;
+ case UNW_LOONGARCH64_R26:
+ return PERF_REG_LOONGARCH_R26;
+ case UNW_LOONGARCH64_R27:
+ return PERF_REG_LOONGARCH_R27;
+ case UNW_LOONGARCH64_R28:
+ return PERF_REG_LOONGARCH_R28;
+ case UNW_LOONGARCH64_R29:
+ return PERF_REG_LOONGARCH_R29;
+ case UNW_LOONGARCH64_R30:
+ return PERF_REG_LOONGARCH_R30;
+ case UNW_LOONGARCH64_R31:
+ return PERF_REG_LOONGARCH_R31;
+ case UNW_LOONGARCH64_PC:
+ return PERF_REG_LOONGARCH_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", regnum);
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
index eacca9a874e2..9d6232f681ce 100755
--- a/tools/perf/check-headers.sh
+++ b/tools/perf/check-headers.sh
@@ -40,6 +40,7 @@ arch/x86/lib/x86-opcode-map.txt
arch/x86/tools/gen-insn-attr-x86.awk
arch/arm/include/uapi/asm/perf_regs.h
arch/arm64/include/uapi/asm/perf_regs.h
+arch/loongarch/include/uapi/asm/perf_regs.h
arch/mips/include/uapi/asm/perf_regs.h
arch/powerpc/include/uapi/asm/perf_regs.h
arch/s390/include/uapi/asm/perf_regs.h
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index db475e44f42f..0cc7710f32da 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -149,6 +149,7 @@ static int arch__associate_ins_ops(struct arch* arch, const char *name, struct i
#include "arch/arm/annotate/instructions.c"
#include "arch/arm64/annotate/instructions.c"
#include "arch/csky/annotate/instructions.c"
+#include "arch/loongarch/annotate/instructions.c"
#include "arch/mips/annotate/instructions.c"
#include "arch/x86/annotate/instructions.c"
#include "arch/powerpc/annotate/instructions.c"
@@ -211,6 +212,13 @@ static struct arch architectures[] = {
.comment_char = '#',
},
},
+ {
+ .name = "loongarch",
+ .init = loongarch__annotate_init,
+ .objdump = {
+ .comment_char = '#',
+ },
+ },
};
static void ins__delete(struct ins_operands *ops)
diff --git a/tools/perf/util/dwarf-regs.c b/tools/perf/util/dwarf-regs.c
index 3fa4486742cd..69cfaa5953bf 100644
--- a/tools/perf/util/dwarf-regs.c
+++ b/tools/perf/util/dwarf-regs.c
@@ -14,6 +14,10 @@
#define EM_AARCH64 183 /* ARM 64 bit */
#endif
+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH 258 /* LoongArch */
+#endif
+
/* Define const char * {arch}_register_tbl[] */
#define DEFINE_DWARF_REGSTR_TABLE
#include "../arch/x86/include/dwarf-regs-table.h"
@@ -25,6 +29,7 @@
#include "../arch/sparc/include/dwarf-regs-table.h"
#include "../arch/xtensa/include/dwarf-regs-table.h"
#include "../arch/mips/include/dwarf-regs-table.h"
+#include "../arch/loongarch/include/dwarf-regs-table.h"
#define __get_dwarf_regstr(tbl, n) (((n) < ARRAY_SIZE(tbl)) ? (tbl)[(n)] : NULL)
@@ -56,6 +61,8 @@ const char *get_dwarf_regstr(unsigned int n, unsigned int machine)
return __get_dwarf_regstr(xtensa_regstr_tbl, n);
case EM_MIPS:
return __get_dwarf_regstr(mips_regstr_tbl, n);
+ case EM_LOONGARCH:
+ return __get_dwarf_regstr(loongarch_regstr_tbl, n);
default:
pr_err("ELF MACHINE %x is not supported.\n", machine);
}
diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c
index 5b8cf6a421a4..0d5d40cb997b 100644
--- a/tools/perf/util/env.c
+++ b/tools/perf/util/env.c
@@ -435,6 +435,8 @@ static const char *normalize_arch(char *arch)
return "mips";
if (!strncmp(arch, "sh", 2) && isdigit(arch[2]))
return "sh";
+ if (!strncmp(arch, "loongarch", 9))
+ return "loongarch";
return arch;
}
diff --git a/tools/perf/util/genelf.h b/tools/perf/util/genelf.h
index 6af062d1c452..5f18d20ea903 100644
--- a/tools/perf/util/genelf.h
+++ b/tools/perf/util/genelf.h
@@ -43,6 +43,9 @@ int jit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_ent
#elif defined(__riscv) && __riscv_xlen == 64
#define GEN_ELF_ARCH EM_RISCV
#define GEN_ELF_CLASS ELFCLASS64
+#elif defined(__loongarch__)
+#define GEN_ELF_ARCH EM_LOONGARCH
+#define GEN_ELF_CLASS ELFCLASS64
#else
#error "unsupported architecture"
#endif
diff --git a/tools/perf/util/perf_regs.c b/tools/perf/util/perf_regs.c
index 57a567ee2cea..9bdbaa37f813 100644
--- a/tools/perf/util/perf_regs.c
+++ b/tools/perf/util/perf_regs.c
@@ -28,6 +28,7 @@ uint64_t __weak arch__user_reg_mask(void)
#include "../../arch/arm/include/uapi/asm/perf_regs.h"
#include "../../arch/csky/include/uapi/asm/perf_regs.h"
+#include "../../arch/loongarch/include/uapi/asm/perf_regs.h"
#include "../../arch/mips/include/uapi/asm/perf_regs.h"
#include "../../arch/powerpc/include/uapi/asm/perf_regs.h"
#include "../../arch/riscv/include/uapi/asm/perf_regs.h"
@@ -236,6 +237,79 @@ static const char *__perf_reg_name_csky(int id)
return NULL;
}
+static inline const char *__perf_reg_name_loongarch(int id)
+{
+ switch (id) {
+ case PERF_REG_LOONGARCH_PC:
+ return "PC";
+ case PERF_REG_LOONGARCH_R1:
+ return "%r1";
+ case PERF_REG_LOONGARCH_R2:
+ return "%r2";
+ case PERF_REG_LOONGARCH_R3:
+ return "%r3";
+ case PERF_REG_LOONGARCH_R4:
+ return "%r4";
+ case PERF_REG_LOONGARCH_R5:
+ return "%r5";
+ case PERF_REG_LOONGARCH_R6:
+ return "%r6";
+ case PERF_REG_LOONGARCH_R7:
+ return "%r7";
+ case PERF_REG_LOONGARCH_R8:
+ return "%r8";
+ case PERF_REG_LOONGARCH_R9:
+ return "%r9";
+ case PERF_REG_LOONGARCH_R10:
+ return "%r10";
+ case PERF_REG_LOONGARCH_R11:
+ return "%r11";
+ case PERF_REG_LOONGARCH_R12:
+ return "%r12";
+ case PERF_REG_LOONGARCH_R13:
+ return "%r13";
+ case PERF_REG_LOONGARCH_R14:
+ return "%r14";
+ case PERF_REG_LOONGARCH_R15:
+ return "%r15";
+ case PERF_REG_LOONGARCH_R16:
+ return "%r16";
+ case PERF_REG_LOONGARCH_R17:
+ return "%r17";
+ case PERF_REG_LOONGARCH_R18:
+ return "%r18";
+ case PERF_REG_LOONGARCH_R19:
+ return "%r19";
+ case PERF_REG_LOONGARCH_R20:
+ return "%r20";
+ case PERF_REG_LOONGARCH_R21:
+ return "%r21";
+ case PERF_REG_LOONGARCH_R22:
+ return "%r22";
+ case PERF_REG_LOONGARCH_R23:
+ return "%r23";
+ case PERF_REG_LOONGARCH_R24:
+ return "%r24";
+ case PERF_REG_LOONGARCH_R25:
+ return "%r25";
+ case PERF_REG_LOONGARCH_R26:
+ return "%r26";
+ case PERF_REG_LOONGARCH_R27:
+ return "%r27";
+ case PERF_REG_LOONGARCH_R28:
+ return "%r28";
+ case PERF_REG_LOONGARCH_R29:
+ return "%r29";
+ case PERF_REG_LOONGARCH_R30:
+ return "%r30";
+ case PERF_REG_LOONGARCH_R31:
+ return "%r31";
+ default:
+ break;
+ }
+ return NULL;
+}
+
static const char *__perf_reg_name_mips(int id)
{
switch (id) {
@@ -670,6 +744,8 @@ const char *perf_reg_name(int id, const char *arch)
if (!strcmp(arch, "csky"))
reg_name = __perf_reg_name_csky(id);
+ else if (!strcmp(arch, "loongarch"))
+ reg_name = __perf_reg_name_loongarch(id);
else if (!strcmp(arch, "mips"))
reg_name = __perf_reg_name_mips(id);
else if (!strcmp(arch, "powerpc"))
diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c
index a2e906858891..313eccef6cb4 100644
--- a/tools/perf/util/syscalltbl.c
+++ b/tools/perf/util/syscalltbl.c
@@ -38,6 +38,10 @@ static const char **syscalltbl_native = syscalltbl_arm64;
#include <asm/syscalls_n64.c>
const int syscalltbl_native_max_id = SYSCALLTBL_MIPS_N64_MAX_ID;
static const char **syscalltbl_native = syscalltbl_mips_n64;
+#elif defined(__loongarch__)
+#include <asm/syscalls.c>
+const int syscalltbl_native_max_id = SYSCALLTBL_LOONGARCH_MAX_ID;
+static const char **syscalltbl_native = syscalltbl_loongarch;
#endif
struct syscall {
diff --git a/tools/testing/cxl/config_check.c b/tools/testing/cxl/config_check.c
index 99b56b5f6edf..0902c5d6e410 100644
--- a/tools/testing/cxl/config_check.c
+++ b/tools/testing/cxl/config_check.c
@@ -13,4 +13,5 @@ void check(void)
BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM));
BUILD_BUG_ON(!IS_ENABLED(CONFIG_CXL_REGION_INVALIDATION_TEST));
BUILD_BUG_ON(!IS_ENABLED(CONFIG_NVDIMM_SECURITY_TEST));
+ BUILD_BUG_ON(!IS_ENABLED(CONFIG_DEBUG_FS));
}
diff --git a/tools/testing/cxl/test/mem.c b/tools/testing/cxl/test/mem.c
index 9263b04d35f7..ba572d03c687 100644
--- a/tools/testing/cxl/test/mem.c
+++ b/tools/testing/cxl/test/mem.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/sizes.h>
#include <linux/bits.h>
+#include <asm/unaligned.h>
#include <cxlmem.h>
#include "trace.h"
@@ -15,6 +16,11 @@
#define DEV_SIZE SZ_2G
#define EFFECT(x) (1U << x)
+#define MOCK_INJECT_DEV_MAX 8
+#define MOCK_INJECT_TEST_MAX 128
+
+static unsigned int poison_inject_dev_max = MOCK_INJECT_DEV_MAX;
+
static struct cxl_cel_entry mock_cel[] = {
{
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_SUPPORTED_LOGS),
@@ -40,6 +46,18 @@ static struct cxl_cel_entry mock_cel[] = {
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
.effect = cpu_to_le16(0),
},
+ {
+ .opcode = cpu_to_le16(CXL_MBOX_OP_GET_POISON),
+ .effect = cpu_to_le16(0),
+ },
+ {
+ .opcode = cpu_to_le16(CXL_MBOX_OP_INJECT_POISON),
+ .effect = cpu_to_le16(0),
+ },
+ {
+ .opcode = cpu_to_le16(CXL_MBOX_OP_CLEAR_POISON),
+ .effect = cpu_to_le16(0),
+ },
};
/* See CXL 2.0 Table 181 Get Health Info Output Payload */
@@ -98,6 +116,7 @@ struct cxl_mockmem_data {
int master_limit;
struct mock_event_store mes;
u8 event_buf[SZ_4K];
+ u64 timestamp;
};
static struct mock_event_log *event_find_log(struct device *dev, int log_type)
@@ -361,6 +380,22 @@ struct cxl_event_mem_module mem_module = {
}
};
+static int mock_set_timestamp(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mockmem_data *mdata = dev_get_drvdata(cxlds->dev);
+ struct cxl_mbox_set_timestamp_in *ts = cmd->payload_in;
+
+ if (cmd->size_in != sizeof(*ts))
+ return -EINVAL;
+
+ if (cmd->size_out != 0)
+ return -EINVAL;
+
+ mdata->timestamp = le64_to_cpu(ts->timestamp);
+ return 0;
+}
+
static void cxl_mock_add_event_logs(struct mock_event_store *mes)
{
put_unaligned_le16(CXL_GMER_VALID_CHANNEL | CXL_GMER_VALID_RANK,
@@ -469,8 +504,11 @@ static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
cpu_to_le64(SZ_256M / CXL_CAPACITY_MULTIPLIER),
.total_capacity =
cpu_to_le64(DEV_SIZE / CXL_CAPACITY_MULTIPLIER),
+ .inject_poison_limit = cpu_to_le16(MOCK_INJECT_TEST_MAX),
};
+ put_unaligned_le24(CXL_POISON_LIST_MAX, id.poison_list_max_mer);
+
if (cmd->size_out < sizeof(id))
return -EINVAL;
@@ -888,12 +926,203 @@ static int mock_health_info(struct cxl_dev_state *cxlds,
return 0;
}
+static struct mock_poison {
+ struct cxl_dev_state *cxlds;
+ u64 dpa;
+} mock_poison_list[MOCK_INJECT_TEST_MAX];
+
+static struct cxl_mbox_poison_out *
+cxl_get_injected_po(struct cxl_dev_state *cxlds, u64 offset, u64 length)
+{
+ struct cxl_mbox_poison_out *po;
+ int nr_records = 0;
+ u64 dpa;
+
+ po = kzalloc(struct_size(po, record, poison_inject_dev_max), GFP_KERNEL);
+ if (!po)
+ return NULL;
+
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (mock_poison_list[i].cxlds != cxlds)
+ continue;
+ if (mock_poison_list[i].dpa < offset ||
+ mock_poison_list[i].dpa > offset + length - 1)
+ continue;
+
+ dpa = mock_poison_list[i].dpa + CXL_POISON_SOURCE_INJECTED;
+ po->record[nr_records].address = cpu_to_le64(dpa);
+ po->record[nr_records].length = cpu_to_le32(1);
+ nr_records++;
+ if (nr_records == poison_inject_dev_max)
+ break;
+ }
+
+ /* Always return count, even when zero */
+ po->count = cpu_to_le16(nr_records);
+
+ return po;
+}
+
+static int mock_get_poison(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mbox_poison_in *pi = cmd->payload_in;
+ struct cxl_mbox_poison_out *po;
+ u64 offset = le64_to_cpu(pi->offset);
+ u64 length = le64_to_cpu(pi->length);
+ int nr_records;
+
+ po = cxl_get_injected_po(cxlds, offset, length);
+ if (!po)
+ return -ENOMEM;
+ nr_records = le16_to_cpu(po->count);
+ memcpy(cmd->payload_out, po, struct_size(po, record, nr_records));
+ cmd->size_out = struct_size(po, record, nr_records);
+ kfree(po);
+
+ return 0;
+}
+
+static bool mock_poison_dev_max_injected(struct cxl_dev_state *cxlds)
+{
+ int count = 0;
+
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (mock_poison_list[i].cxlds == cxlds)
+ count++;
+ }
+ return (count >= poison_inject_dev_max);
+}
+
+static bool mock_poison_add(struct cxl_dev_state *cxlds, u64 dpa)
+{
+ if (mock_poison_dev_max_injected(cxlds)) {
+ dev_dbg(cxlds->dev,
+ "Device poison injection limit has been reached: %d\n",
+ MOCK_INJECT_DEV_MAX);
+ return false;
+ }
+
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (!mock_poison_list[i].cxlds) {
+ mock_poison_list[i].cxlds = cxlds;
+ mock_poison_list[i].dpa = dpa;
+ return true;
+ }
+ }
+ dev_dbg(cxlds->dev,
+ "Mock test poison injection limit has been reached: %d\n",
+ MOCK_INJECT_TEST_MAX);
+
+ return false;
+}
+
+static bool mock_poison_found(struct cxl_dev_state *cxlds, u64 dpa)
+{
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (mock_poison_list[i].cxlds == cxlds &&
+ mock_poison_list[i].dpa == dpa)
+ return true;
+ }
+ return false;
+}
+
+static int mock_inject_poison(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mbox_inject_poison *pi = cmd->payload_in;
+ u64 dpa = le64_to_cpu(pi->address);
+
+ if (mock_poison_found(cxlds, dpa)) {
+ /* Not an error to inject poison if already poisoned */
+ dev_dbg(cxlds->dev, "DPA: 0x%llx already poisoned\n", dpa);
+ return 0;
+ }
+ if (!mock_poison_add(cxlds, dpa))
+ return -ENXIO;
+
+ return 0;
+}
+
+static bool mock_poison_del(struct cxl_dev_state *cxlds, u64 dpa)
+{
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (mock_poison_list[i].cxlds == cxlds &&
+ mock_poison_list[i].dpa == dpa) {
+ mock_poison_list[i].cxlds = NULL;
+ return true;
+ }
+ }
+ return false;
+}
+
+static int mock_clear_poison(struct cxl_dev_state *cxlds,
+ struct cxl_mbox_cmd *cmd)
+{
+ struct cxl_mbox_clear_poison *pi = cmd->payload_in;
+ u64 dpa = le64_to_cpu(pi->address);
+
+ /*
+ * A real CXL device will write pi->write_data to the address
+ * being cleared. In this mock, just delete this address from
+ * the mock poison list.
+ */
+ if (!mock_poison_del(cxlds, dpa))
+ dev_dbg(cxlds->dev, "DPA: 0x%llx not in poison list\n", dpa);
+
+ return 0;
+}
+
+static bool mock_poison_list_empty(void)
+{
+ for (int i = 0; i < MOCK_INJECT_TEST_MAX; i++) {
+ if (mock_poison_list[i].cxlds)
+ return false;
+ }
+ return true;
+}
+
+static ssize_t poison_inject_max_show(struct device_driver *drv, char *buf)
+{
+ return sysfs_emit(buf, "%u\n", poison_inject_dev_max);
+}
+
+static ssize_t poison_inject_max_store(struct device_driver *drv,
+ const char *buf, size_t len)
+{
+ int val;
+
+ if (kstrtoint(buf, 0, &val) < 0)
+ return -EINVAL;
+
+ if (!mock_poison_list_empty())
+ return -EBUSY;
+
+ if (val <= MOCK_INJECT_TEST_MAX)
+ poison_inject_dev_max = val;
+ else
+ return -EINVAL;
+
+ return len;
+}
+
+static DRIVER_ATTR_RW(poison_inject_max);
+
+static struct attribute *cxl_mock_mem_core_attrs[] = {
+ &driver_attr_poison_inject_max.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(cxl_mock_mem_core);
+
static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
{
struct device *dev = cxlds->dev;
int rc = -EIO;
switch (cmd->opcode) {
+ case CXL_MBOX_OP_SET_TIMESTAMP:
+ rc = mock_set_timestamp(cxlds, cmd);
+ break;
case CXL_MBOX_OP_GET_SUPPORTED_LOGS:
rc = mock_gsl(cmd);
break;
@@ -942,6 +1171,15 @@ static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *
case CXL_MBOX_OP_PASSPHRASE_SECURE_ERASE:
rc = mock_passphrase_secure_erase(cxlds, cmd);
break;
+ case CXL_MBOX_OP_GET_POISON:
+ rc = mock_get_poison(cxlds, cmd);
+ break;
+ case CXL_MBOX_OP_INJECT_POISON:
+ rc = mock_inject_poison(cxlds, cmd);
+ break;
+ case CXL_MBOX_OP_CLEAR_POISON:
+ rc = mock_clear_poison(cxlds, cmd);
+ break;
default:
break;
}
@@ -1010,6 +1248,14 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
if (rc)
return rc;
+ rc = cxl_poison_state_init(cxlds);
+ if (rc)
+ return rc;
+
+ rc = cxl_set_timestamp(cxlds);
+ if (rc)
+ return rc;
+
rc = cxl_dev_state_identify(cxlds);
if (rc)
return rc;
@@ -1083,6 +1329,7 @@ static struct platform_driver cxl_mock_mem_driver = {
.driver = {
.name = KBUILD_MODNAME,
.dev_groups = cxl_mock_mem_groups,
+ .groups = cxl_mock_mem_core_groups,
},
};
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 97dcdaa656f6..90a62cf75008 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -64,6 +64,7 @@ TARGETS += pstore
TARGETS += ptrace
TARGETS += openat2
TARGETS += resctrl
+TARGETS += riscv
TARGETS += rlimits
TARGETS += rseq
TARGETS += rtc
diff --git a/tools/testing/selftests/cgroup/test_cpuset_prs.sh b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
index 75c100de90ff..2b5215cc599f 100755
--- a/tools/testing/selftests/cgroup/test_cpuset_prs.sh
+++ b/tools/testing/selftests/cgroup/test_cpuset_prs.sh
@@ -15,13 +15,6 @@ skip_test() {
[[ $(id -u) -eq 0 ]] || skip_test "Test must be run as root!"
-# Set sched verbose flag, if available
-if [[ -d /sys/kernel/debug/sched ]]
-then
- # Used to restore the original setting during cleanup
- SCHED_DEBUG=$(cat /sys/kernel/debug/sched/verbose)
- echo Y > /sys/kernel/debug/sched/verbose
-fi
# Get wait_inotify location
WAIT_INOTIFY=$(cd $(dirname $0); pwd)/wait_inotify
@@ -37,10 +30,14 @@ CPUS=$(lscpu | grep "^CPU(s):" | sed -e "s/.*:[[:space:]]*//")
PROG=$1
VERBOSE=
DELAY_FACTOR=1
+SCHED_DEBUG=
while [[ "$1" = -* ]]
do
case "$1" in
-v) VERBOSE=1
+ # Enable sched/verbose can slow thing down
+ [[ $DELAY_FACTOR -eq 1 ]] &&
+ DELAY_FACTOR=2
break
;;
-d) DELAY_FACTOR=$2
@@ -54,6 +51,14 @@ do
shift
done
+# Set sched verbose flag if available when "-v" option is specified
+if [[ -n "$VERBOSE" && -d /sys/kernel/debug/sched ]]
+then
+ # Used to restore the original setting during cleanup
+ SCHED_DEBUG=$(cat /sys/kernel/debug/sched/verbose)
+ echo Y > /sys/kernel/debug/sched/verbose
+fi
+
cd $CGROUP2
echo +cpuset > cgroup.subtree_control
[[ -d test ]] || mkdir test
@@ -65,7 +70,8 @@ cleanup()
rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1
cd ..
rmdir test > /dev/null 2>&1
- echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
+ [[ -n "$SCHED_DEBUG" ]] &&
+ echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose
}
# Pause in ms
@@ -571,7 +577,6 @@ run_state_test()
echo "Test $TEST[$I] failed result check!"
eval echo \"\${$TEST[$I]}\"
dump_states
- online_cpus
exit 1
}
@@ -582,7 +587,6 @@ run_state_test()
eval echo \"\${$TEST[$I]}\"
echo
dump_states
- online_cpus
exit 1
}
}
@@ -594,7 +598,6 @@ run_state_test()
eval echo \"\${$TEST[$I]}\"
echo
dump_states
- online_cpus
exit 1
}
}
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 84a627c43795..7a5ff646e7e7 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -105,6 +105,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test
TEST_GEN_PROGS_x86_64 += x86_64/vmx_nested_tsc_scaling_test
TEST_GEN_PROGS_x86_64 += x86_64/xapic_ipi_test
TEST_GEN_PROGS_x86_64 += x86_64/xapic_state_test
+TEST_GEN_PROGS_x86_64 += x86_64/xcr0_cpuid_test
TEST_GEN_PROGS_x86_64 += x86_64/xss_msr_test
TEST_GEN_PROGS_x86_64 += x86_64/debug_regs
TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
@@ -141,6 +142,7 @@ TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list
TEST_GEN_PROGS_aarch64 += aarch64/hypercalls
TEST_GEN_PROGS_aarch64 += aarch64/page_fault_test
TEST_GEN_PROGS_aarch64 += aarch64/psci_test
+TEST_GEN_PROGS_aarch64 += aarch64/smccc_filter
TEST_GEN_PROGS_aarch64 += aarch64/vcpu_width_config
TEST_GEN_PROGS_aarch64 += aarch64/vgic_init
TEST_GEN_PROGS_aarch64 += aarch64/vgic_irq
diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c
index 26556a266021..8ef370924a02 100644
--- a/tools/testing/selftests/kvm/aarch64/arch_timer.c
+++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c
@@ -47,6 +47,7 @@ struct test_args {
int nr_iter;
int timer_period_ms;
int migration_freq_ms;
+ struct kvm_arm_counter_offset offset;
};
static struct test_args test_args = {
@@ -54,6 +55,7 @@ static struct test_args test_args = {
.nr_iter = NR_TEST_ITERS_DEF,
.timer_period_ms = TIMER_TEST_PERIOD_MS_DEF,
.migration_freq_ms = TIMER_TEST_MIGRATION_FREQ_MS,
+ .offset = { .reserved = 1 },
};
#define msecs_to_usecs(msec) ((msec) * 1000LL)
@@ -121,25 +123,35 @@ static void guest_validate_irq(unsigned int intid,
uint64_t xcnt = 0, xcnt_diff_us, cval = 0;
unsigned long xctl = 0;
unsigned int timer_irq = 0;
+ unsigned int accessor;
- if (stage == GUEST_STAGE_VTIMER_CVAL ||
- stage == GUEST_STAGE_VTIMER_TVAL) {
- xctl = timer_get_ctl(VIRTUAL);
- timer_set_ctl(VIRTUAL, CTL_IMASK);
- xcnt = timer_get_cntct(VIRTUAL);
- cval = timer_get_cval(VIRTUAL);
+ if (intid == IAR_SPURIOUS)
+ return;
+
+ switch (stage) {
+ case GUEST_STAGE_VTIMER_CVAL:
+ case GUEST_STAGE_VTIMER_TVAL:
+ accessor = VIRTUAL;
timer_irq = vtimer_irq;
- } else if (stage == GUEST_STAGE_PTIMER_CVAL ||
- stage == GUEST_STAGE_PTIMER_TVAL) {
- xctl = timer_get_ctl(PHYSICAL);
- timer_set_ctl(PHYSICAL, CTL_IMASK);
- xcnt = timer_get_cntct(PHYSICAL);
- cval = timer_get_cval(PHYSICAL);
+ break;
+ case GUEST_STAGE_PTIMER_CVAL:
+ case GUEST_STAGE_PTIMER_TVAL:
+ accessor = PHYSICAL;
timer_irq = ptimer_irq;
- } else {
+ break;
+ default:
GUEST_ASSERT(0);
+ return;
}
+ xctl = timer_get_ctl(accessor);
+ if ((xctl & CTL_IMASK) || !(xctl & CTL_ENABLE))
+ return;
+
+ timer_set_ctl(accessor, CTL_IMASK);
+ xcnt = timer_get_cntct(accessor);
+ cval = timer_get_cval(accessor);
+
xcnt_diff_us = cycles_to_usec(xcnt - shared_data->xcnt);
/* Make sure we are dealing with the correct timer IRQ */
@@ -148,6 +160,8 @@ static void guest_validate_irq(unsigned int intid,
/* Basic 'timer condition met' check */
GUEST_ASSERT_3(xcnt >= cval, xcnt, cval, xcnt_diff_us);
GUEST_ASSERT_1(xctl & CTL_ISTATUS, xctl);
+
+ WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
}
static void guest_irq_handler(struct ex_regs *regs)
@@ -158,8 +172,6 @@ static void guest_irq_handler(struct ex_regs *regs)
guest_validate_irq(intid, shared_data);
- WRITE_ONCE(shared_data->nr_iter, shared_data->nr_iter + 1);
-
gic_set_eoi(intid);
}
@@ -372,6 +384,13 @@ static struct kvm_vm *test_vm_create(void)
vm_init_descriptor_tables(vm);
vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler);
+ if (!test_args.offset.reserved) {
+ if (kvm_has_cap(KVM_CAP_COUNTER_OFFSET))
+ vm_ioctl(vm, KVM_ARM_SET_COUNTER_OFFSET, &test_args.offset);
+ else
+ TEST_FAIL("no support for global offset\n");
+ }
+
for (i = 0; i < nr_vcpus; i++)
vcpu_init_descriptor_tables(vcpus[i]);
@@ -403,6 +422,7 @@ static void test_print_help(char *name)
TIMER_TEST_PERIOD_MS_DEF);
pr_info("\t-m: Frequency (in ms) of vCPUs to migrate to different pCPU. 0 to turn off (default: %u)\n",
TIMER_TEST_MIGRATION_FREQ_MS);
+ pr_info("\t-o: Counter offset (in counter cycles, default: 0)\n");
pr_info("\t-h: print this help screen\n");
}
@@ -410,7 +430,7 @@ static bool parse_args(int argc, char *argv[])
{
int opt;
- while ((opt = getopt(argc, argv, "hn:i:p:m:")) != -1) {
+ while ((opt = getopt(argc, argv, "hn:i:p:m:o:")) != -1) {
switch (opt) {
case 'n':
test_args.nr_vcpus = atoi_positive("Number of vCPUs", optarg);
@@ -429,6 +449,10 @@ static bool parse_args(int argc, char *argv[])
case 'm':
test_args.migration_freq_ms = atoi_non_negative("Frequency", optarg);
break;
+ case 'o':
+ test_args.offset.counter_offset = strtol(optarg, NULL, 0);
+ test_args.offset.reserved = 0;
+ break;
case 'h':
default:
goto err;
diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
index d287dd2cac0a..d4e1f4af29d6 100644
--- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c
+++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c
@@ -651,7 +651,7 @@ int main(int ac, char **av)
* The current blessed list was primed with the output of kernel version
* v4.15 with --core-reg-fixup and then later updated with new registers.
*
- * The blessed list is up to date with kernel version v5.13-rc3
+ * The blessed list is up to date with kernel version v6.4 (or so we hope)
*/
static __u64 base_regs[] = {
KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | KVM_REG_ARM_CORE_REG(regs.regs[0]),
@@ -807,10 +807,10 @@ static __u64 base_regs[] = {
ARM64_SYS_REG(3, 0, 0, 3, 7),
ARM64_SYS_REG(3, 0, 0, 4, 0), /* ID_AA64PFR0_EL1 */
ARM64_SYS_REG(3, 0, 0, 4, 1), /* ID_AA64PFR1_EL1 */
- ARM64_SYS_REG(3, 0, 0, 4, 2),
+ ARM64_SYS_REG(3, 0, 0, 4, 2), /* ID_AA64PFR2_EL1 */
ARM64_SYS_REG(3, 0, 0, 4, 3),
ARM64_SYS_REG(3, 0, 0, 4, 4), /* ID_AA64ZFR0_EL1 */
- ARM64_SYS_REG(3, 0, 0, 4, 5),
+ ARM64_SYS_REG(3, 0, 0, 4, 5), /* ID_AA64SMFR0_EL1 */
ARM64_SYS_REG(3, 0, 0, 4, 6),
ARM64_SYS_REG(3, 0, 0, 4, 7),
ARM64_SYS_REG(3, 0, 0, 5, 0), /* ID_AA64DFR0_EL1 */
@@ -823,7 +823,7 @@ static __u64 base_regs[] = {
ARM64_SYS_REG(3, 0, 0, 5, 7),
ARM64_SYS_REG(3, 0, 0, 6, 0), /* ID_AA64ISAR0_EL1 */
ARM64_SYS_REG(3, 0, 0, 6, 1), /* ID_AA64ISAR1_EL1 */
- ARM64_SYS_REG(3, 0, 0, 6, 2),
+ ARM64_SYS_REG(3, 0, 0, 6, 2), /* ID_AA64ISAR2_EL1 */
ARM64_SYS_REG(3, 0, 0, 6, 3),
ARM64_SYS_REG(3, 0, 0, 6, 4),
ARM64_SYS_REG(3, 0, 0, 6, 5),
@@ -832,8 +832,8 @@ static __u64 base_regs[] = {
ARM64_SYS_REG(3, 0, 0, 7, 0), /* ID_AA64MMFR0_EL1 */
ARM64_SYS_REG(3, 0, 0, 7, 1), /* ID_AA64MMFR1_EL1 */
ARM64_SYS_REG(3, 0, 0, 7, 2), /* ID_AA64MMFR2_EL1 */
- ARM64_SYS_REG(3, 0, 0, 7, 3),
- ARM64_SYS_REG(3, 0, 0, 7, 4),
+ ARM64_SYS_REG(3, 0, 0, 7, 3), /* ID_AA64MMFR3_EL1 */
+ ARM64_SYS_REG(3, 0, 0, 7, 4), /* ID_AA64MMFR4_EL1 */
ARM64_SYS_REG(3, 0, 0, 7, 5),
ARM64_SYS_REG(3, 0, 0, 7, 6),
ARM64_SYS_REG(3, 0, 0, 7, 7),
@@ -858,6 +858,9 @@ static __u64 base_regs[] = {
ARM64_SYS_REG(3, 2, 0, 0, 0), /* CSSELR_EL1 */
ARM64_SYS_REG(3, 3, 13, 0, 2), /* TPIDR_EL0 */
ARM64_SYS_REG(3, 3, 13, 0, 3), /* TPIDRRO_EL0 */
+ ARM64_SYS_REG(3, 3, 14, 0, 1), /* CNTPCT_EL0 */
+ ARM64_SYS_REG(3, 3, 14, 2, 1), /* CNTP_CTL_EL0 */
+ ARM64_SYS_REG(3, 3, 14, 2, 2), /* CNTP_CVAL_EL0 */
ARM64_SYS_REG(3, 4, 3, 0, 0), /* DACR32_EL2 */
ARM64_SYS_REG(3, 4, 5, 0, 1), /* IFSR32_EL2 */
ARM64_SYS_REG(3, 4, 5, 3, 0), /* FPEXC32_EL2 */
diff --git a/tools/testing/selftests/kvm/aarch64/smccc_filter.c b/tools/testing/selftests/kvm/aarch64/smccc_filter.c
new file mode 100644
index 000000000000..f4ceae9c8925
--- /dev/null
+++ b/tools/testing/selftests/kvm/aarch64/smccc_filter.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * smccc_filter - Tests for the SMCCC filter UAPI.
+ *
+ * Copyright (c) 2023 Google LLC
+ *
+ * This test includes:
+ * - Tests that the UAPI constraints are upheld by KVM. For example, userspace
+ * is prevented from filtering the architecture range of SMCCC calls.
+ * - Test that the filter actions (DENIED, FWD_TO_USER) work as intended.
+ */
+
+#include <linux/arm-smccc.h>
+#include <linux/psci.h>
+#include <stdint.h>
+
+#include "processor.h"
+#include "test_util.h"
+
+enum smccc_conduit {
+ HVC_INSN,
+ SMC_INSN,
+};
+
+#define for_each_conduit(conduit) \
+ for (conduit = HVC_INSN; conduit <= SMC_INSN; conduit++)
+
+static void guest_main(uint32_t func_id, enum smccc_conduit conduit)
+{
+ struct arm_smccc_res res;
+
+ if (conduit == SMC_INSN)
+ smccc_smc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+ else
+ smccc_hvc(func_id, 0, 0, 0, 0, 0, 0, 0, &res);
+
+ GUEST_SYNC(res.a0);
+}
+
+static int __set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
+ enum kvm_smccc_filter_action action)
+{
+ struct kvm_smccc_filter filter = {
+ .base = start,
+ .nr_functions = nr_functions,
+ .action = action,
+ };
+
+ return __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
+ KVM_ARM_VM_SMCCC_FILTER, &filter);
+}
+
+static void set_smccc_filter(struct kvm_vm *vm, uint32_t start, uint32_t nr_functions,
+ enum kvm_smccc_filter_action action)
+{
+ int ret = __set_smccc_filter(vm, start, nr_functions, action);
+
+ TEST_ASSERT(!ret, "failed to configure SMCCC filter: %d", ret);
+}
+
+static struct kvm_vm *setup_vm(struct kvm_vcpu **vcpu)
+{
+ struct kvm_vcpu_init init;
+ struct kvm_vm *vm;
+
+ vm = vm_create(1);
+ vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init);
+
+ /*
+ * Enable in-kernel emulation of PSCI to ensure that calls are denied
+ * due to the SMCCC filter, not because of KVM.
+ */
+ init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2);
+
+ *vcpu = aarch64_vcpu_add(vm, 0, &init, guest_main);
+ return vm;
+}
+
+static void test_pad_must_be_zero(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ struct kvm_smccc_filter filter = {
+ .base = PSCI_0_2_FN_PSCI_VERSION,
+ .nr_functions = 1,
+ .action = KVM_SMCCC_FILTER_DENY,
+ .pad = { -1 },
+ };
+ int r;
+
+ r = __kvm_device_attr_set(vm->fd, KVM_ARM_VM_SMCCC_CTRL,
+ KVM_ARM_VM_SMCCC_FILTER, &filter);
+ TEST_ASSERT(r < 0 && errno == EINVAL,
+ "Setting filter with nonzero padding should return EINVAL");
+}
+
+/* Ensure that userspace cannot filter the Arm Architecture SMCCC range */
+static void test_filter_reserved_range(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ uint32_t smc64_fn;
+ int r;
+
+ r = __set_smccc_filter(vm, ARM_SMCCC_ARCH_WORKAROUND_1,
+ 1, KVM_SMCCC_FILTER_DENY);
+ TEST_ASSERT(r < 0 && errno == EEXIST,
+ "Attempt to filter reserved range should return EEXIST");
+
+ smc64_fn = ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, ARM_SMCCC_SMC_64,
+ 0, 0);
+
+ r = __set_smccc_filter(vm, smc64_fn, 1, KVM_SMCCC_FILTER_DENY);
+ TEST_ASSERT(r < 0 && errno == EEXIST,
+ "Attempt to filter reserved range should return EEXIST");
+
+ kvm_vm_free(vm);
+}
+
+static void test_invalid_nr_functions(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ int r;
+
+ r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 0, KVM_SMCCC_FILTER_DENY);
+ TEST_ASSERT(r < 0 && errno == EINVAL,
+ "Attempt to filter 0 functions should return EINVAL");
+
+ kvm_vm_free(vm);
+}
+
+static void test_overflow_nr_functions(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ int r;
+
+ r = __set_smccc_filter(vm, ~0, ~0, KVM_SMCCC_FILTER_DENY);
+ TEST_ASSERT(r < 0 && errno == EINVAL,
+ "Attempt to overflow filter range should return EINVAL");
+
+ kvm_vm_free(vm);
+}
+
+static void test_reserved_action(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ int r;
+
+ r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, -1);
+ TEST_ASSERT(r < 0 && errno == EINVAL,
+ "Attempt to use reserved filter action should return EINVAL");
+
+ kvm_vm_free(vm);
+}
+
+
+/* Test that overlapping configurations of the SMCCC filter are rejected */
+static void test_filter_overlap(void)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = setup_vm(&vcpu);
+ int r;
+
+ set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
+
+ r = __set_smccc_filter(vm, PSCI_0_2_FN64_CPU_ON, 1, KVM_SMCCC_FILTER_DENY);
+ TEST_ASSERT(r < 0 && errno == EEXIST,
+ "Attempt to filter already configured range should return EEXIST");
+
+ kvm_vm_free(vm);
+}
+
+static void expect_call_denied(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+
+ if (get_ucall(vcpu, &uc) != UCALL_SYNC)
+ TEST_FAIL("Unexpected ucall: %lu\n", uc.cmd);
+
+ TEST_ASSERT(uc.args[1] == SMCCC_RET_NOT_SUPPORTED,
+ "Unexpected SMCCC return code: %lu", uc.args[1]);
+}
+
+/* Denied SMCCC calls have a return code of SMCCC_RET_NOT_SUPPORTED */
+static void test_filter_denied(void)
+{
+ enum smccc_conduit conduit;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ for_each_conduit(conduit) {
+ vm = setup_vm(&vcpu);
+
+ set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_DENY);
+ vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
+
+ vcpu_run(vcpu);
+ expect_call_denied(vcpu);
+
+ kvm_vm_free(vm);
+ }
+}
+
+static void expect_call_fwd_to_user(struct kvm_vcpu *vcpu, uint32_t func_id,
+ enum smccc_conduit conduit)
+{
+ struct kvm_run *run = vcpu->run;
+
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_HYPERCALL,
+ "Unexpected exit reason: %u", run->exit_reason);
+ TEST_ASSERT(run->hypercall.nr == func_id,
+ "Unexpected SMCCC function: %llu", run->hypercall.nr);
+
+ if (conduit == SMC_INSN)
+ TEST_ASSERT(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC,
+ "KVM_HYPERCALL_EXIT_SMC is not set");
+ else
+ TEST_ASSERT(!(run->hypercall.flags & KVM_HYPERCALL_EXIT_SMC),
+ "KVM_HYPERCALL_EXIT_SMC is set");
+}
+
+/* SMCCC calls forwarded to userspace cause KVM_EXIT_HYPERCALL exits */
+static void test_filter_fwd_to_user(void)
+{
+ enum smccc_conduit conduit;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+
+ for_each_conduit(conduit) {
+ vm = setup_vm(&vcpu);
+
+ set_smccc_filter(vm, PSCI_0_2_FN_PSCI_VERSION, 1, KVM_SMCCC_FILTER_FWD_TO_USER);
+ vcpu_args_set(vcpu, 2, PSCI_0_2_FN_PSCI_VERSION, conduit);
+
+ vcpu_run(vcpu);
+ expect_call_fwd_to_user(vcpu, PSCI_0_2_FN_PSCI_VERSION, conduit);
+
+ kvm_vm_free(vm);
+ }
+}
+
+static bool kvm_supports_smccc_filter(void)
+{
+ struct kvm_vm *vm = vm_create_barebones();
+ int r;
+
+ r = __kvm_has_device_attr(vm->fd, KVM_ARM_VM_SMCCC_CTRL, KVM_ARM_VM_SMCCC_FILTER);
+
+ kvm_vm_free(vm);
+ return !r;
+}
+
+int main(void)
+{
+ TEST_REQUIRE(kvm_supports_smccc_filter());
+
+ test_pad_must_be_zero();
+ test_invalid_nr_functions();
+ test_overflow_nr_functions();
+ test_reserved_action();
+ test_filter_reserved_range();
+ test_filter_overlap();
+ test_filter_denied();
+ test_filter_fwd_to_user();
+}
diff --git a/tools/testing/selftests/kvm/config b/tools/testing/selftests/kvm/config
index d011b38e259e..8835fed09e9f 100644
--- a/tools/testing/selftests/kvm/config
+++ b/tools/testing/selftests/kvm/config
@@ -2,3 +2,4 @@ CONFIG_KVM=y
CONFIG_KVM_INTEL=y
CONFIG_KVM_AMD=y
CONFIG_USERFAULTFD=y
+CONFIG_IDLE_PAGE_TRACKING=y
diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c
index b0e1fc4de9e2..2439c4043fed 100644
--- a/tools/testing/selftests/kvm/demand_paging_test.c
+++ b/tools/testing/selftests/kvm/demand_paging_test.c
@@ -194,7 +194,7 @@ static void run_test(enum vm_guest_mode mode, void *arg)
ts_diff.tv_sec, ts_diff.tv_nsec);
pr_info("Overall demand paging rate: %f pgs/sec\n",
memstress_args.vcpu_args[0].pages * nr_vcpus /
- ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / 100000000.0));
+ ((double)ts_diff.tv_sec + (double)ts_diff.tv_nsec / NSEC_PER_SEC));
memstress_destroy_vm(vm);
diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h
index 5f977528e09c..cb537253a6b9 100644
--- a/tools/testing/selftests/kvm/include/aarch64/processor.h
+++ b/tools/testing/selftests/kvm/include/aarch64/processor.h
@@ -214,6 +214,19 @@ void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
uint64_t arg6, struct arm_smccc_res *res);
+/**
+ * smccc_smc - Invoke a SMCCC function using the smc conduit
+ * @function_id: the SMCCC function to be called
+ * @arg0-arg6: SMCCC function arguments, corresponding to registers x1-x7
+ * @res: pointer to write the return values from registers x0-x3
+ *
+ */
+void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
+ uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
+ uint64_t arg6, struct arm_smccc_res *res);
+
+
+
uint32_t guest_get_vcpuid(void);
#endif /* SELFTEST_KVM_PROCESSOR_H */
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index fbc2a79369b8..a089c356f354 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -213,6 +213,7 @@ extern const struct vm_guest_mode_params vm_guest_mode_params[];
int open_path_or_exit(const char *path, int flags);
int open_kvm_dev_path_or_exit(void);
+bool get_kvm_param_bool(const char *param);
bool get_kvm_intel_param_bool(const char *param);
bool get_kvm_amd_param_bool(const char *param);
diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h
index 90387ddcb2a9..aa434c8f19c5 100644
--- a/tools/testing/selftests/kvm/include/x86_64/processor.h
+++ b/tools/testing/selftests/kvm/include/x86_64/processor.h
@@ -48,6 +48,35 @@ extern bool host_cpu_is_amd;
#define X86_CR4_SMAP (1ul << 21)
#define X86_CR4_PKE (1ul << 22)
+struct xstate_header {
+ u64 xstate_bv;
+ u64 xcomp_bv;
+ u64 reserved[6];
+} __attribute__((packed));
+
+struct xstate {
+ u8 i387[512];
+ struct xstate_header header;
+ u8 extended_state_area[0];
+} __attribute__ ((packed, aligned (64)));
+
+#define XFEATURE_MASK_FP BIT_ULL(0)
+#define XFEATURE_MASK_SSE BIT_ULL(1)
+#define XFEATURE_MASK_YMM BIT_ULL(2)
+#define XFEATURE_MASK_BNDREGS BIT_ULL(3)
+#define XFEATURE_MASK_BNDCSR BIT_ULL(4)
+#define XFEATURE_MASK_OPMASK BIT_ULL(5)
+#define XFEATURE_MASK_ZMM_Hi256 BIT_ULL(6)
+#define XFEATURE_MASK_Hi16_ZMM BIT_ULL(7)
+#define XFEATURE_MASK_XTILE_CFG BIT_ULL(17)
+#define XFEATURE_MASK_XTILE_DATA BIT_ULL(18)
+
+#define XFEATURE_MASK_AVX512 (XFEATURE_MASK_OPMASK | \
+ XFEATURE_MASK_ZMM_Hi256 | \
+ XFEATURE_MASK_Hi16_ZMM)
+#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILE_DATA | \
+ XFEATURE_MASK_XTILE_CFG)
+
/* Note, these are ordered alphabetically to match kvm_cpuid_entry2. Eww. */
enum cpuid_output_regs {
KVM_CPUID_EAX,
@@ -131,6 +160,7 @@ struct kvm_x86_cpu_feature {
#define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18)
#define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3)
#define X86_FEATURE_XFD KVM_X86_CPU_FEATURE(0xD, 1, EAX, 4)
+#define X86_FEATURE_XTILEDATA_XFD KVM_X86_CPU_FEATURE(0xD, 18, ECX, 2)
/*
* Extended Leafs, a.k.a. AMD defined
@@ -211,10 +241,14 @@ struct kvm_x86_cpu_property {
#define X86_PROPERTY_PMU_NR_GP_COUNTERS KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 8, 15)
#define X86_PROPERTY_PMU_EBX_BIT_VECTOR_LENGTH KVM_X86_CPU_PROPERTY(0xa, 0, EAX, 24, 31)
+#define X86_PROPERTY_SUPPORTED_XCR0_LO KVM_X86_CPU_PROPERTY(0xd, 0, EAX, 0, 31)
#define X86_PROPERTY_XSTATE_MAX_SIZE_XCR0 KVM_X86_CPU_PROPERTY(0xd, 0, EBX, 0, 31)
#define X86_PROPERTY_XSTATE_MAX_SIZE KVM_X86_CPU_PROPERTY(0xd, 0, ECX, 0, 31)
+#define X86_PROPERTY_SUPPORTED_XCR0_HI KVM_X86_CPU_PROPERTY(0xd, 0, EDX, 0, 31)
+
#define X86_PROPERTY_XSTATE_TILE_SIZE KVM_X86_CPU_PROPERTY(0xd, 18, EAX, 0, 31)
#define X86_PROPERTY_XSTATE_TILE_OFFSET KVM_X86_CPU_PROPERTY(0xd, 18, EBX, 0, 31)
+#define X86_PROPERTY_AMX_MAX_PALETTE_TABLES KVM_X86_CPU_PROPERTY(0x1d, 0, EAX, 0, 31)
#define X86_PROPERTY_AMX_TOTAL_TILE_BYTES KVM_X86_CPU_PROPERTY(0x1d, 1, EAX, 0, 15)
#define X86_PROPERTY_AMX_BYTES_PER_TILE KVM_X86_CPU_PROPERTY(0x1d, 1, EAX, 16, 31)
#define X86_PROPERTY_AMX_BYTES_PER_ROW KVM_X86_CPU_PROPERTY(0x1d, 1, EBX, 0, 15)
@@ -496,6 +530,24 @@ static inline void set_cr4(uint64_t val)
__asm__ __volatile__("mov %0, %%cr4" : : "r" (val) : "memory");
}
+static inline u64 xgetbv(u32 index)
+{
+ u32 eax, edx;
+
+ __asm__ __volatile__("xgetbv;"
+ : "=a" (eax), "=d" (edx)
+ : "c" (index));
+ return eax | ((u64)edx << 32);
+}
+
+static inline void xsetbv(u32 index, u64 value)
+{
+ u32 eax = value;
+ u32 edx = value >> 32;
+
+ __asm__ __volatile__("xsetbv" :: "a" (eax), "d" (edx), "c" (index));
+}
+
static inline struct desc_ptr get_gdt(void)
{
struct desc_ptr gdt;
@@ -632,6 +684,15 @@ static inline bool this_pmu_has(struct kvm_x86_pmu_feature feature)
!this_cpu_has(feature.anti_feature);
}
+static __always_inline uint64_t this_cpu_supported_xcr0(void)
+{
+ if (!this_cpu_has_p(X86_PROPERTY_SUPPORTED_XCR0_LO))
+ return 0;
+
+ return this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_LO) |
+ ((uint64_t)this_cpu_property(X86_PROPERTY_SUPPORTED_XCR0_HI) << 32);
+}
+
typedef u32 __attribute__((vector_size(16))) sse128_t;
#define __sse128_u union { sse128_t vec; u64 as_u64[2]; u32 as_u32[4]; }
#define sse128_lo(x) ({ __sse128_u t; t.vec = x; t.as_u64[0]; })
@@ -928,14 +989,45 @@ static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu,
uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index);
int _vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, uint64_t msr_value);
-static inline void vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index,
- uint64_t msr_value)
-{
- int r = _vcpu_set_msr(vcpu, msr_index, msr_value);
+/*
+ * Assert on an MSR access(es) and pretty print the MSR name when possible.
+ * Note, the caller provides the stringified name so that the name of macro is
+ * printed, not the value the macro resolves to (due to macro expansion).
+ */
+#define TEST_ASSERT_MSR(cond, fmt, msr, str, args...) \
+do { \
+ if (__builtin_constant_p(msr)) { \
+ TEST_ASSERT(cond, fmt, str, args); \
+ } else if (!(cond)) { \
+ char buf[16]; \
+ \
+ snprintf(buf, sizeof(buf), "MSR 0x%x", msr); \
+ TEST_ASSERT(cond, fmt, buf, args); \
+ } \
+} while (0)
- TEST_ASSERT(r == 1, KVM_IOCTL_ERROR(KVM_SET_MSRS, r));
+/*
+ * Returns true if KVM should return the last written value when reading an MSR
+ * from userspace, e.g. the MSR isn't a command MSR, doesn't emulate state that
+ * is changing, etc. This is NOT an exhaustive list! The intent is to filter
+ * out MSRs that are not durable _and_ that a selftest wants to write.
+ */
+static inline bool is_durable_msr(uint32_t msr)
+{
+ return msr != MSR_IA32_TSC;
}
+#define vcpu_set_msr(vcpu, msr, val) \
+do { \
+ uint64_t r, v = val; \
+ \
+ TEST_ASSERT_MSR(_vcpu_set_msr(vcpu, msr, v) == 1, \
+ "KVM_SET_MSRS failed on %s, value = 0x%lx", msr, #msr, v); \
+ if (!is_durable_msr(msr)) \
+ break; \
+ r = vcpu_get_msr(vcpu, msr); \
+ TEST_ASSERT_MSR(r == v, "Set %s to '0x%lx', got back '0x%lx'", msr, #msr, v, r);\
+} while (0)
void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits);
bool vm_is_unrestricted_guest(struct kvm_vm *vm);
@@ -1055,6 +1147,14 @@ static inline uint8_t wrmsr_safe(uint32_t msr, uint64_t val)
return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr));
}
+static inline uint8_t xsetbv_safe(uint32_t index, uint64_t value)
+{
+ u32 eax = value;
+ u32 edx = value >> 32;
+
+ return kvm_asm_safe("xsetbv", "a" (eax), "d" (edx), "c" (index));
+}
+
bool kvm_is_tdp_enabled(void);
uint64_t *__vm_get_page_table_entry(struct kvm_vm *vm, uint64_t vaddr,
@@ -1066,10 +1166,10 @@ uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2,
uint64_t __xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
void xen_hypercall(uint64_t nr, uint64_t a0, void *a1);
-void __vm_xsave_require_permission(int bit, const char *name);
+void __vm_xsave_require_permission(uint64_t xfeature, const char *name);
-#define vm_xsave_require_permission(perm) \
- __vm_xsave_require_permission(perm, #perm)
+#define vm_xsave_require_permission(xfeature) \
+ __vm_xsave_require_permission(xfeature, #xfeature)
enum pg_level {
PG_LEVEL_NONE,
@@ -1106,14 +1206,6 @@ void virt_map_level(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
#define X86_CR0_CD (1UL<<30) /* Cache Disable */
#define X86_CR0_PG (1UL<<31) /* Paging */
-#define XSTATE_XTILE_CFG_BIT 17
-#define XSTATE_XTILE_DATA_BIT 18
-
-#define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT)
-#define XSTATE_XTILE_DATA_MASK (1ULL << XSTATE_XTILE_DATA_BIT)
-#define XFEATURE_XTILE_MASK (XSTATE_XTILE_CFG_MASK | \
- XSTATE_XTILE_DATA_MASK)
-
#define PFERR_PRESENT_BIT 0
#define PFERR_WRITE_BIT 1
#define PFERR_USER_BIT 2
diff --git a/tools/testing/selftests/kvm/lib/aarch64/processor.c b/tools/testing/selftests/kvm/lib/aarch64/processor.c
index 5972a23b2765..3a0259e25335 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/processor.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/processor.c
@@ -58,10 +58,27 @@ static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva)
return (gva >> vm->page_shift) & mask;
}
-static uint64_t pte_addr(struct kvm_vm *vm, uint64_t entry)
+static uint64_t addr_pte(struct kvm_vm *vm, uint64_t pa, uint64_t attrs)
{
- uint64_t mask = ((1UL << (vm->va_bits - vm->page_shift)) - 1) << vm->page_shift;
- return entry & mask;
+ uint64_t pte;
+
+ pte = pa & GENMASK(47, vm->page_shift);
+ if (vm->page_shift == 16)
+ pte |= FIELD_GET(GENMASK(51, 48), pa) << 12;
+ pte |= attrs;
+
+ return pte;
+}
+
+static uint64_t pte_addr(struct kvm_vm *vm, uint64_t pte)
+{
+ uint64_t pa;
+
+ pa = pte & GENMASK(47, vm->page_shift);
+ if (vm->page_shift == 16)
+ pa |= FIELD_GET(GENMASK(15, 12), pte) << 48;
+
+ return pa;
}
static uint64_t ptrs_per_pgd(struct kvm_vm *vm)
@@ -110,18 +127,18 @@ static void _virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
ptep = addr_gpa2hva(vm, vm->pgd) + pgd_index(vm, vaddr) * 8;
if (!*ptep)
- *ptep = vm_alloc_page_table(vm) | 3;
+ *ptep = addr_pte(vm, vm_alloc_page_table(vm), 3);
switch (vm->pgtable_levels) {
case 4:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pud_index(vm, vaddr) * 8;
if (!*ptep)
- *ptep = vm_alloc_page_table(vm) | 3;
+ *ptep = addr_pte(vm, vm_alloc_page_table(vm), 3);
/* fall through */
case 3:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pmd_index(vm, vaddr) * 8;
if (!*ptep)
- *ptep = vm_alloc_page_table(vm) | 3;
+ *ptep = addr_pte(vm, vm_alloc_page_table(vm), 3);
/* fall through */
case 2:
ptep = addr_gpa2hva(vm, pte_addr(vm, *ptep)) + pte_index(vm, vaddr) * 8;
@@ -130,8 +147,7 @@ static void _virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
TEST_FAIL("Page table levels must be 2, 3, or 4");
}
- *ptep = paddr | 3;
- *ptep |= (attr_idx << 2) | (1 << 10) /* Access Flag */;
+ *ptep = addr_pte(vm, paddr, (attr_idx << 2) | (1 << 10) | 3); /* AF */
}
void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
@@ -226,7 +242,7 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
{
struct kvm_vcpu_init default_init = { .target = -1, };
struct kvm_vm *vm = vcpu->vm;
- uint64_t sctlr_el1, tcr_el1;
+ uint64_t sctlr_el1, tcr_el1, ttbr0_el1;
if (!init)
init = &default_init;
@@ -277,10 +293,13 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
TEST_FAIL("Unknown guest mode, mode: 0x%x", vm->mode);
}
+ ttbr0_el1 = vm->pgd & GENMASK(47, vm->page_shift);
+
/* Configure output size */
switch (vm->mode) {
case VM_MODE_P52V48_64K:
tcr_el1 |= 6ul << 32; /* IPS = 52 bits */
+ ttbr0_el1 |= FIELD_GET(GENMASK(51, 48), vm->pgd) << 2;
break;
case VM_MODE_P48V48_4K:
case VM_MODE_P48V48_16K:
@@ -310,7 +329,7 @@ void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init)
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), sctlr_el1);
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TCR_EL1), tcr_el1);
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MAIR_EL1), DEFAULT_MAIR_EL1);
- vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TTBR0_EL1), vm->pgd);
+ vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TTBR0_EL1), ttbr0_el1);
vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TPIDR_EL1), vcpu->id);
}
@@ -508,29 +527,43 @@ void aarch64_get_supported_page_sizes(uint32_t ipa,
close(kvm_fd);
}
+#define __smccc_call(insn, function_id, arg0, arg1, arg2, arg3, arg4, arg5, \
+ arg6, res) \
+ asm volatile("mov w0, %w[function_id]\n" \
+ "mov x1, %[arg0]\n" \
+ "mov x2, %[arg1]\n" \
+ "mov x3, %[arg2]\n" \
+ "mov x4, %[arg3]\n" \
+ "mov x5, %[arg4]\n" \
+ "mov x6, %[arg5]\n" \
+ "mov x7, %[arg6]\n" \
+ #insn "#0\n" \
+ "mov %[res0], x0\n" \
+ "mov %[res1], x1\n" \
+ "mov %[res2], x2\n" \
+ "mov %[res3], x3\n" \
+ : [res0] "=r"(res->a0), [res1] "=r"(res->a1), \
+ [res2] "=r"(res->a2), [res3] "=r"(res->a3) \
+ : [function_id] "r"(function_id), [arg0] "r"(arg0), \
+ [arg1] "r"(arg1), [arg2] "r"(arg2), [arg3] "r"(arg3), \
+ [arg4] "r"(arg4), [arg5] "r"(arg5), [arg6] "r"(arg6) \
+ : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7")
+
+
void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
uint64_t arg6, struct arm_smccc_res *res)
{
- asm volatile("mov w0, %w[function_id]\n"
- "mov x1, %[arg0]\n"
- "mov x2, %[arg1]\n"
- "mov x3, %[arg2]\n"
- "mov x4, %[arg3]\n"
- "mov x5, %[arg4]\n"
- "mov x6, %[arg5]\n"
- "mov x7, %[arg6]\n"
- "hvc #0\n"
- "mov %[res0], x0\n"
- "mov %[res1], x1\n"
- "mov %[res2], x2\n"
- "mov %[res3], x3\n"
- : [res0] "=r"(res->a0), [res1] "=r"(res->a1),
- [res2] "=r"(res->a2), [res3] "=r"(res->a3)
- : [function_id] "r"(function_id), [arg0] "r"(arg0),
- [arg1] "r"(arg1), [arg2] "r"(arg2), [arg3] "r"(arg3),
- [arg4] "r"(arg4), [arg5] "r"(arg5), [arg6] "r"(arg6)
- : "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7");
+ __smccc_call(hvc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, res);
+}
+
+void smccc_smc(uint32_t function_id, uint64_t arg0, uint64_t arg1,
+ uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5,
+ uint64_t arg6, struct arm_smccc_res *res)
+{
+ __smccc_call(smc, function_id, arg0, arg1, arg2, arg3, arg4, arg5,
+ arg6, res);
}
void kvm_selftest_arch_init(void)
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 8ec20ac33de0..298c4372fb1a 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -80,6 +80,11 @@ static bool get_module_param_bool(const char *module_name, const char *param)
TEST_FAIL("Unrecognized value '%c' for boolean module param", value);
}
+bool get_kvm_param_bool(const char *param)
+{
+ return get_module_param_bool("kvm", param);
+}
+
bool get_kvm_intel_param_bool(const char *param)
{
return get_module_param_bool("kvm_intel", param);
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index c39a4353ba19..d4a0b504b1e0 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -5,6 +5,7 @@
* Copyright (C) 2018, Google LLC.
*/
+#include "linux/bitmap.h"
#include "test_util.h"
#include "kvm_util.h"
#include "processor.h"
@@ -573,6 +574,21 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
DEFAULT_GUEST_STACK_VADDR_MIN,
MEM_REGION_DATA);
+ stack_vaddr += DEFAULT_STACK_PGS * getpagesize();
+
+ /*
+ * Align stack to match calling sequence requirements in section "The
+ * Stack Frame" of the System V ABI AMD64 Architecture Processor
+ * Supplement, which requires the value (%rsp + 8) to be a multiple of
+ * 16 when control is transferred to the function entry point.
+ *
+ * If this code is ever used to launch a vCPU with 32-bit entry point it
+ * may need to subtract 4 bytes instead of 8 bytes.
+ */
+ TEST_ASSERT(IS_ALIGNED(stack_vaddr, PAGE_SIZE),
+ "__vm_vaddr_alloc() did not provide a page-aligned address");
+ stack_vaddr -= 8;
+
vcpu = __vm_vcpu_add(vm, vcpu_id);
vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid());
vcpu_setup(vm, vcpu);
@@ -580,7 +596,7 @@ struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id,
/* Setup guest general purpose registers */
vcpu_regs_get(vcpu, &regs);
regs.rflags = regs.rflags | 0x2;
- regs.rsp = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize());
+ regs.rsp = stack_vaddr;
regs.rip = (unsigned long) guest_code;
vcpu_regs_set(vcpu, &regs);
@@ -681,7 +697,7 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index)
return buffer.entry.data;
}
-void __vm_xsave_require_permission(int bit, const char *name)
+void __vm_xsave_require_permission(uint64_t xfeature, const char *name)
{
int kvm_fd;
u64 bitmask;
@@ -689,12 +705,15 @@ void __vm_xsave_require_permission(int bit, const char *name)
struct kvm_device_attr attr = {
.group = 0,
.attr = KVM_X86_XCOMP_GUEST_SUPP,
- .addr = (unsigned long) &bitmask
+ .addr = (unsigned long) &bitmask,
};
TEST_ASSERT(!kvm_supported_cpuid,
"kvm_get_supported_cpuid() cannot be used before ARCH_REQ_XCOMP_GUEST_PERM");
+ TEST_ASSERT(is_power_of_2(xfeature),
+ "Dynamic XFeatures must be enabled one at a time");
+
kvm_fd = open_kvm_dev_path_or_exit();
rc = __kvm_ioctl(kvm_fd, KVM_GET_DEVICE_ATTR, &attr);
close(kvm_fd);
@@ -704,16 +723,16 @@ void __vm_xsave_require_permission(int bit, const char *name)
TEST_ASSERT(rc == 0, "KVM_GET_DEVICE_ATTR(0, KVM_X86_XCOMP_GUEST_SUPP) error: %ld", rc);
- __TEST_REQUIRE(bitmask & (1ULL << bit),
+ __TEST_REQUIRE(bitmask & xfeature,
"Required XSAVE feature '%s' not supported", name);
- TEST_REQUIRE(!syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, bit));
+ TEST_REQUIRE(!syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, ilog2(xfeature)));
rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_GUEST_PERM, &bitmask);
TEST_ASSERT(rc == 0, "prctl(ARCH_GET_XCOMP_GUEST_PERM) error: %ld", rc);
- TEST_ASSERT(bitmask & (1ULL << bit),
- "prctl(ARCH_REQ_XCOMP_GUEST_PERM) failure bitmask=0x%lx",
- bitmask);
+ TEST_ASSERT(bitmask & xfeature,
+ "'%s' (0x%lx) not permitted after prctl(ARCH_REQ_XCOMP_GUEST_PERM) permitted=0x%lx",
+ name, xfeature, bitmask);
}
void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid)
@@ -954,6 +973,7 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu)
vcpu_run_complete_io(vcpu);
state = malloc(sizeof(*state) + msr_list->nmsrs * sizeof(state->msrs.entries[0]));
+ TEST_ASSERT(state, "-ENOMEM when allocating kvm state");
vcpu_events_get(vcpu, &state->events);
vcpu_mp_state_get(vcpu, &state->mp_state);
diff --git a/tools/testing/selftests/kvm/x86_64/amx_test.c b/tools/testing/selftests/kvm/x86_64/amx_test.c
index b646cdb5055a..11329e5ff945 100644
--- a/tools/testing/selftests/kvm/x86_64/amx_test.c
+++ b/tools/testing/selftests/kvm/x86_64/amx_test.c
@@ -30,21 +30,12 @@
#define XSAVE_SIZE ((NUM_TILES * TILE_SIZE) + PAGE_SIZE)
/* Tile configuration associated: */
+#define PALETTE_TABLE_INDEX 1
#define MAX_TILES 16
#define RESERVED_BYTES 14
-#define XFEATURE_XTILECFG 17
-#define XFEATURE_XTILEDATA 18
-#define XFEATURE_MASK_XTILECFG (1 << XFEATURE_XTILECFG)
-#define XFEATURE_MASK_XTILEDATA (1 << XFEATURE_XTILEDATA)
-#define XFEATURE_MASK_XTILE (XFEATURE_MASK_XTILECFG | XFEATURE_MASK_XTILEDATA)
-
#define XSAVE_HDR_OFFSET 512
-struct xsave_data {
- u8 area[XSAVE_SIZE];
-} __aligned(64);
-
struct tile_config {
u8 palette_id;
u8 start_row;
@@ -68,24 +59,6 @@ struct xtile_info {
static struct xtile_info xtile;
-static inline u64 __xgetbv(u32 index)
-{
- u32 eax, edx;
-
- asm volatile("xgetbv;"
- : "=a" (eax), "=d" (edx)
- : "c" (index));
- return eax + ((u64)edx << 32);
-}
-
-static inline void __xsetbv(u32 index, u64 value)
-{
- u32 eax = value;
- u32 edx = value >> 32;
-
- asm volatile("xsetbv" :: "a" (eax), "d" (edx), "c" (index));
-}
-
static inline void __ldtilecfg(void *cfg)
{
asm volatile(".byte 0xc4,0xe2,0x78,0x49,0x00"
@@ -103,27 +76,16 @@ static inline void __tilerelease(void)
asm volatile(".byte 0xc4, 0xe2, 0x78, 0x49, 0xc0" ::);
}
-static inline void __xsavec(struct xsave_data *data, uint64_t rfbm)
+static inline void __xsavec(struct xstate *xstate, uint64_t rfbm)
{
uint32_t rfbm_lo = rfbm;
uint32_t rfbm_hi = rfbm >> 32;
asm volatile("xsavec (%%rdi)"
- : : "D" (data), "a" (rfbm_lo), "d" (rfbm_hi)
+ : : "D" (xstate), "a" (rfbm_lo), "d" (rfbm_hi)
: "memory");
}
-static inline void check_cpuid_xsave(void)
-{
- GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE));
- GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE));
-}
-
-static bool check_xsave_supports_xtile(void)
-{
- return __xgetbv(0) & XFEATURE_MASK_XTILE;
-}
-
static void check_xtile_info(void)
{
GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_XSTATE_MAX_SIZE_XCR0));
@@ -135,6 +97,10 @@ static void check_xtile_info(void)
GUEST_ASSERT(xtile.xsave_size == 8192);
GUEST_ASSERT(sizeof(struct tile_data) >= xtile.xsave_size);
+ GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_MAX_PALETTE_TABLES));
+ GUEST_ASSERT(this_cpu_property(X86_PROPERTY_AMX_MAX_PALETTE_TABLES) >=
+ PALETTE_TABLE_INDEX);
+
GUEST_ASSERT(this_cpu_has_p(X86_PROPERTY_AMX_NR_TILE_REGS));
xtile.max_names = this_cpu_property(X86_PROPERTY_AMX_NR_TILE_REGS);
GUEST_ASSERT(xtile.max_names == 8);
@@ -158,37 +124,29 @@ static void set_tilecfg(struct tile_config *cfg)
}
}
-static void set_xstatebv(void *data, uint64_t bv)
-{
- *(uint64_t *)(data + XSAVE_HDR_OFFSET) = bv;
-}
-
-static u64 get_xstatebv(void *data)
-{
- return *(u64 *)(data + XSAVE_HDR_OFFSET);
-}
-
static void init_regs(void)
{
uint64_t cr4, xcr0;
+ GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE));
+
/* turn on CR4.OSXSAVE */
cr4 = get_cr4();
cr4 |= X86_CR4_OSXSAVE;
set_cr4(cr4);
+ GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE));
- xcr0 = __xgetbv(0);
+ xcr0 = xgetbv(0);
xcr0 |= XFEATURE_MASK_XTILE;
- __xsetbv(0x0, xcr0);
+ xsetbv(0x0, xcr0);
+ GUEST_ASSERT((xgetbv(0) & XFEATURE_MASK_XTILE) == XFEATURE_MASK_XTILE);
}
static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
struct tile_data *tiledata,
- struct xsave_data *xsave_data)
+ struct xstate *xstate)
{
init_regs();
- check_cpuid_xsave();
- check_xsave_supports_xtile();
check_xtile_info();
GUEST_SYNC(1);
@@ -204,15 +162,29 @@ static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
GUEST_SYNC(4);
__tilerelease();
GUEST_SYNC(5);
- /* bit 18 not in the XCOMP_BV after xsavec() */
- set_xstatebv(xsave_data, XFEATURE_MASK_XTILEDATA);
- __xsavec(xsave_data, XFEATURE_MASK_XTILEDATA);
- GUEST_ASSERT((get_xstatebv(xsave_data) & XFEATURE_MASK_XTILEDATA) == 0);
+ /*
+ * After XSAVEC, XTILEDATA is cleared in the xstate_bv but is set in
+ * the xcomp_bv.
+ */
+ xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;
+ __xsavec(xstate, XFEATURE_MASK_XTILE_DATA);
+ GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));
+ GUEST_ASSERT(xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA);
/* xfd=0x40000, disable amx tiledata */
- wrmsr(MSR_IA32_XFD, XFEATURE_MASK_XTILEDATA);
+ wrmsr(MSR_IA32_XFD, XFEATURE_MASK_XTILE_DATA);
+
+ /*
+ * XTILEDATA is cleared in xstate_bv but set in xcomp_bv, this property
+ * remains the same even when amx tiledata is disabled by IA32_XFD.
+ */
+ xstate->header.xstate_bv = XFEATURE_MASK_XTILE_DATA;
+ __xsavec(xstate, XFEATURE_MASK_XTILE_DATA);
+ GUEST_ASSERT(!(xstate->header.xstate_bv & XFEATURE_MASK_XTILE_DATA));
+ GUEST_ASSERT((xstate->header.xcomp_bv & XFEATURE_MASK_XTILE_DATA));
+
GUEST_SYNC(6);
- GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILEDATA);
+ GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
set_tilecfg(amx_cfg);
__ldtilecfg(amx_cfg);
/* Trigger #NM exception */
@@ -224,11 +196,14 @@ static void __attribute__((__flatten__)) guest_code(struct tile_config *amx_cfg,
void guest_nm_handler(struct ex_regs *regs)
{
- /* Check if #NM is triggered by XFEATURE_MASK_XTILEDATA */
+ /* Check if #NM is triggered by XFEATURE_MASK_XTILE_DATA */
GUEST_SYNC(7);
- GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILEDATA);
+ GUEST_ASSERT(!(get_cr0() & X86_CR0_TS));
+ GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);
+ GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
GUEST_SYNC(8);
- GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILEDATA);
+ GUEST_ASSERT(rdmsr(MSR_IA32_XFD_ERR) == XFEATURE_MASK_XTILE_DATA);
+ GUEST_ASSERT(rdmsr(MSR_IA32_XFD) == XFEATURE_MASK_XTILE_DATA);
/* Clear xfd_err */
wrmsr(MSR_IA32_XFD_ERR, 0);
/* xfd=0, enable amx */
@@ -243,7 +218,7 @@ int main(int argc, char *argv[])
struct kvm_vm *vm;
struct kvm_x86_state *state;
int xsave_restore_size;
- vm_vaddr_t amx_cfg, tiledata, xsavedata;
+ vm_vaddr_t amx_cfg, tiledata, xstate;
struct ucall uc;
u32 amx_offset;
int stage, ret;
@@ -252,13 +227,14 @@ int main(int argc, char *argv[])
* Note, all off-by-default features must be enabled before anything
* caches KVM_GET_SUPPORTED_CPUID, e.g. before using kvm_cpu_has().
*/
- vm_xsave_require_permission(XSTATE_XTILE_DATA_BIT);
+ vm_xsave_require_permission(XFEATURE_MASK_XTILE_DATA);
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XFD));
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_AMX_TILE));
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILECFG));
TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA));
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA_XFD));
/* Create VM */
vm = vm_create_with_one_vcpu(&vcpu, guest_code);
@@ -282,10 +258,10 @@ int main(int argc, char *argv[])
tiledata = vm_vaddr_alloc_pages(vm, 2);
memset(addr_gva2hva(vm, tiledata), rand() | 1, 2 * getpagesize());
- /* xsave data for guest_code */
- xsavedata = vm_vaddr_alloc_pages(vm, 3);
- memset(addr_gva2hva(vm, xsavedata), 0, 3 * getpagesize());
- vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xsavedata);
+ /* XSAVE state for guest_code */
+ xstate = vm_vaddr_alloc_pages(vm, DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));
+ memset(addr_gva2hva(vm, xstate), 0, PAGE_SIZE * DIV_ROUND_UP(XSAVE_SIZE, PAGE_SIZE));
+ vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xstate);
for (stage = 1; ; stage++) {
vcpu_run(vcpu);
diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
index 2feef25ba691..40507ed9fe8a 100644
--- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
+++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c
@@ -54,6 +54,21 @@
#define AMD_ZEN_BR_RETIRED EVENT(0xc2, 0)
+
+/*
+ * "Retired instructions", from Processor Programming Reference
+ * (PPR) for AMD Family 17h Model 01h, Revision B1 Processors,
+ * Preliminary Processor Programming Reference (PPR) for AMD Family
+ * 17h Model 31h, Revision B0 Processors, and Preliminary Processor
+ * Programming Reference (PPR) for AMD Family 19h Model 01h, Revision
+ * B1 Processors Volume 1 of 2.
+ * --- and ---
+ * "Instructions retired", from the Intel SDM, volume 3,
+ * "Pre-defined Architectural Performance Events."
+ */
+
+#define INST_RETIRED EVENT(0xc0, 0)
+
/*
* This event list comprises Intel's eight architectural events plus
* AMD's "retired branch instructions" for Zen[123] (and possibly
@@ -61,7 +76,7 @@
*/
static const uint64_t event_list[] = {
EVENT(0x3c, 0),
- EVENT(0xc0, 0),
+ INST_RETIRED,
EVENT(0x3c, 1),
EVENT(0x2e, 0x4f),
EVENT(0x2e, 0x41),
@@ -71,13 +86,21 @@ static const uint64_t event_list[] = {
AMD_ZEN_BR_RETIRED,
};
+struct {
+ uint64_t loads;
+ uint64_t stores;
+ uint64_t loads_stores;
+ uint64_t branches_retired;
+ uint64_t instructions_retired;
+} pmc_results;
+
/*
* If we encounter a #GP during the guest PMU sanity check, then the guest
* PMU is not functional. Inform the hypervisor via GUEST_SYNC(0).
*/
static void guest_gp_handler(struct ex_regs *regs)
{
- GUEST_SYNC(0);
+ GUEST_SYNC(-EFAULT);
}
/*
@@ -92,12 +115,23 @@ static void check_msr(uint32_t msr, uint64_t bits_to_flip)
wrmsr(msr, v);
if (rdmsr(msr) != v)
- GUEST_SYNC(0);
+ GUEST_SYNC(-EIO);
v ^= bits_to_flip;
wrmsr(msr, v);
if (rdmsr(msr) != v)
- GUEST_SYNC(0);
+ GUEST_SYNC(-EIO);
+}
+
+static void run_and_measure_loop(uint32_t msr_base)
+{
+ const uint64_t branches_retired = rdmsr(msr_base + 0);
+ const uint64_t insn_retired = rdmsr(msr_base + 1);
+
+ __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES}));
+
+ pmc_results.branches_retired = rdmsr(msr_base + 0) - branches_retired;
+ pmc_results.instructions_retired = rdmsr(msr_base + 1) - insn_retired;
}
static void intel_guest_code(void)
@@ -105,19 +139,18 @@ static void intel_guest_code(void)
check_msr(MSR_CORE_PERF_GLOBAL_CTRL, 1);
check_msr(MSR_P6_EVNTSEL0, 0xffff);
check_msr(MSR_IA32_PMC0, 0xffff);
- GUEST_SYNC(1);
+ GUEST_SYNC(0);
for (;;) {
- uint64_t br0, br1;
-
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
wrmsr(MSR_P6_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
ARCH_PERFMON_EVENTSEL_OS | INTEL_BR_RETIRED);
- wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 1);
- br0 = rdmsr(MSR_IA32_PMC0);
- __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES}));
- br1 = rdmsr(MSR_IA32_PMC0);
- GUEST_SYNC(br1 - br0);
+ wrmsr(MSR_P6_EVNTSEL1, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | INST_RETIRED);
+ wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0x3);
+
+ run_and_measure_loop(MSR_IA32_PMC0);
+ GUEST_SYNC(0);
}
}
@@ -130,18 +163,17 @@ static void amd_guest_code(void)
{
check_msr(MSR_K7_EVNTSEL0, 0xffff);
check_msr(MSR_K7_PERFCTR0, 0xffff);
- GUEST_SYNC(1);
+ GUEST_SYNC(0);
for (;;) {
- uint64_t br0, br1;
-
wrmsr(MSR_K7_EVNTSEL0, 0);
wrmsr(MSR_K7_EVNTSEL0, ARCH_PERFMON_EVENTSEL_ENABLE |
ARCH_PERFMON_EVENTSEL_OS | AMD_ZEN_BR_RETIRED);
- br0 = rdmsr(MSR_K7_PERFCTR0);
- __asm__ __volatile__("loop ." : "+c"((int){NUM_BRANCHES}));
- br1 = rdmsr(MSR_K7_PERFCTR0);
- GUEST_SYNC(br1 - br0);
+ wrmsr(MSR_K7_EVNTSEL1, ARCH_PERFMON_EVENTSEL_ENABLE |
+ ARCH_PERFMON_EVENTSEL_OS | INST_RETIRED);
+
+ run_and_measure_loop(MSR_K7_PERFCTR0);
+ GUEST_SYNC(0);
}
}
@@ -161,6 +193,19 @@ static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu)
return uc.args[1];
}
+static void run_vcpu_and_sync_pmc_results(struct kvm_vcpu *vcpu)
+{
+ uint64_t r;
+
+ memset(&pmc_results, 0, sizeof(pmc_results));
+ sync_global_to_guest(vcpu->vm, pmc_results);
+
+ r = run_vcpu_to_sync(vcpu);
+ TEST_ASSERT(!r, "Unexpected sync value: 0x%lx", r);
+
+ sync_global_from_guest(vcpu->vm, pmc_results);
+}
+
/*
* In a nested environment or if the vPMU is disabled, the guest PMU
* might not work as architected (accessing the PMU MSRs may raise
@@ -171,13 +216,13 @@ static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu)
*/
static bool sanity_check_pmu(struct kvm_vcpu *vcpu)
{
- bool success;
+ uint64_t r;
vm_install_exception_handler(vcpu->vm, GP_VECTOR, guest_gp_handler);
- success = run_vcpu_to_sync(vcpu);
+ r = run_vcpu_to_sync(vcpu);
vm_install_exception_handler(vcpu->vm, GP_VECTOR, NULL);
- return success;
+ return !r;
}
static struct kvm_pmu_event_filter *alloc_pmu_event_filter(uint32_t nevents)
@@ -237,91 +282,101 @@ static struct kvm_pmu_event_filter *remove_event(struct kvm_pmu_event_filter *f,
return f;
}
+#define ASSERT_PMC_COUNTING_INSTRUCTIONS() \
+do { \
+ uint64_t br = pmc_results.branches_retired; \
+ uint64_t ir = pmc_results.instructions_retired; \
+ \
+ if (br && br != NUM_BRANCHES) \
+ pr_info("%s: Branch instructions retired = %lu (expected %u)\n", \
+ __func__, br, NUM_BRANCHES); \
+ TEST_ASSERT(br, "%s: Branch instructions retired = %lu (expected > 0)", \
+ __func__, br); \
+ TEST_ASSERT(ir, "%s: Instructions retired = %lu (expected > 0)", \
+ __func__, ir); \
+} while (0)
+
+#define ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS() \
+do { \
+ uint64_t br = pmc_results.branches_retired; \
+ uint64_t ir = pmc_results.instructions_retired; \
+ \
+ TEST_ASSERT(!br, "%s: Branch instructions retired = %lu (expected 0)", \
+ __func__, br); \
+ TEST_ASSERT(!ir, "%s: Instructions retired = %lu (expected 0)", \
+ __func__, ir); \
+} while (0)
+
static void test_without_filter(struct kvm_vcpu *vcpu)
{
- uint64_t count = run_vcpu_to_sync(vcpu);
+ run_vcpu_and_sync_pmc_results(vcpu);
- if (count != NUM_BRANCHES)
- pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
- __func__, count, NUM_BRANCHES);
- TEST_ASSERT(count, "Allowed PMU event is not counting");
+ ASSERT_PMC_COUNTING_INSTRUCTIONS();
}
-static uint64_t test_with_filter(struct kvm_vcpu *vcpu,
- struct kvm_pmu_event_filter *f)
+static void test_with_filter(struct kvm_vcpu *vcpu,
+ struct kvm_pmu_event_filter *f)
{
vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f);
- return run_vcpu_to_sync(vcpu);
+ run_vcpu_and_sync_pmc_results(vcpu);
}
static void test_amd_deny_list(struct kvm_vcpu *vcpu)
{
uint64_t event = EVENT(0x1C2, 0);
struct kvm_pmu_event_filter *f;
- uint64_t count;
f = create_pmu_event_filter(&event, 1, KVM_PMU_EVENT_DENY, 0);
- count = test_with_filter(vcpu, f);
-
+ test_with_filter(vcpu, f);
free(f);
- if (count != NUM_BRANCHES)
- pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
- __func__, count, NUM_BRANCHES);
- TEST_ASSERT(count, "Allowed PMU event is not counting");
+
+ ASSERT_PMC_COUNTING_INSTRUCTIONS();
}
static void test_member_deny_list(struct kvm_vcpu *vcpu)
{
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY);
- uint64_t count = test_with_filter(vcpu, f);
+ test_with_filter(vcpu, f);
free(f);
- if (count)
- pr_info("%s: Branch instructions retired = %lu (expected 0)\n",
- __func__, count);
- TEST_ASSERT(!count, "Disallowed PMU Event is counting");
+
+ ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS();
}
static void test_member_allow_list(struct kvm_vcpu *vcpu)
{
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW);
- uint64_t count = test_with_filter(vcpu, f);
+ test_with_filter(vcpu, f);
free(f);
- if (count != NUM_BRANCHES)
- pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
- __func__, count, NUM_BRANCHES);
- TEST_ASSERT(count, "Allowed PMU event is not counting");
+
+ ASSERT_PMC_COUNTING_INSTRUCTIONS();
}
static void test_not_member_deny_list(struct kvm_vcpu *vcpu)
{
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY);
- uint64_t count;
+ remove_event(f, INST_RETIRED);
remove_event(f, INTEL_BR_RETIRED);
remove_event(f, AMD_ZEN_BR_RETIRED);
- count = test_with_filter(vcpu, f);
+ test_with_filter(vcpu, f);
free(f);
- if (count != NUM_BRANCHES)
- pr_info("%s: Branch instructions retired = %lu (expected %u)\n",
- __func__, count, NUM_BRANCHES);
- TEST_ASSERT(count, "Allowed PMU event is not counting");
+
+ ASSERT_PMC_COUNTING_INSTRUCTIONS();
}
static void test_not_member_allow_list(struct kvm_vcpu *vcpu)
{
struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW);
- uint64_t count;
+ remove_event(f, INST_RETIRED);
remove_event(f, INTEL_BR_RETIRED);
remove_event(f, AMD_ZEN_BR_RETIRED);
- count = test_with_filter(vcpu, f);
+ test_with_filter(vcpu, f);
free(f);
- if (count)
- pr_info("%s: Branch instructions retired = %lu (expected 0)\n",
- __func__, count);
- TEST_ASSERT(!count, "Disallowed PMU Event is counting");
+
+ ASSERT_PMC_NOT_COUNTING_INSTRUCTIONS();
}
/*
@@ -450,51 +505,30 @@ static bool supports_event_mem_inst_retired(void)
#define EXCLUDE_MASKED_ENTRY(event_select, mask, match) \
KVM_PMU_ENCODE_MASKED_ENTRY(event_select, mask, match, true)
-struct perf_counter {
- union {
- uint64_t raw;
- struct {
- uint64_t loads:22;
- uint64_t stores:22;
- uint64_t loads_stores:20;
- };
- };
-};
-
-static uint64_t masked_events_guest_test(uint32_t msr_base)
+static void masked_events_guest_test(uint32_t msr_base)
{
- uint64_t ld0, ld1, st0, st1, ls0, ls1;
- struct perf_counter c;
- int val;
-
/*
- * The acutal value of the counters don't determine the outcome of
+ * The actual value of the counters don't determine the outcome of
* the test. Only that they are zero or non-zero.
*/
- ld0 = rdmsr(msr_base + 0);
- st0 = rdmsr(msr_base + 1);
- ls0 = rdmsr(msr_base + 2);
+ const uint64_t loads = rdmsr(msr_base + 0);
+ const uint64_t stores = rdmsr(msr_base + 1);
+ const uint64_t loads_stores = rdmsr(msr_base + 2);
+ int val;
+
__asm__ __volatile__("movl $0, %[v];"
"movl %[v], %%eax;"
"incl %[v];"
: [v]"+m"(val) :: "eax");
- ld1 = rdmsr(msr_base + 0);
- st1 = rdmsr(msr_base + 1);
- ls1 = rdmsr(msr_base + 2);
-
- c.loads = ld1 - ld0;
- c.stores = st1 - st0;
- c.loads_stores = ls1 - ls0;
-
- return c.raw;
+ pmc_results.loads = rdmsr(msr_base + 0) - loads;
+ pmc_results.stores = rdmsr(msr_base + 1) - stores;
+ pmc_results.loads_stores = rdmsr(msr_base + 2) - loads_stores;
}
static void intel_masked_events_guest_code(void)
{
- uint64_t r;
-
for (;;) {
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0);
@@ -507,16 +541,13 @@ static void intel_masked_events_guest_code(void)
wrmsr(MSR_CORE_PERF_GLOBAL_CTRL, 0x7);
- r = masked_events_guest_test(MSR_IA32_PMC0);
-
- GUEST_SYNC(r);
+ masked_events_guest_test(MSR_IA32_PMC0);
+ GUEST_SYNC(0);
}
}
static void amd_masked_events_guest_code(void)
{
- uint64_t r;
-
for (;;) {
wrmsr(MSR_K7_EVNTSEL0, 0);
wrmsr(MSR_K7_EVNTSEL1, 0);
@@ -529,26 +560,22 @@ static void amd_masked_events_guest_code(void)
wrmsr(MSR_K7_EVNTSEL2, ARCH_PERFMON_EVENTSEL_ENABLE |
ARCH_PERFMON_EVENTSEL_OS | LS_DISPATCH_LOAD_STORE);
- r = masked_events_guest_test(MSR_K7_PERFCTR0);
-
- GUEST_SYNC(r);
+ masked_events_guest_test(MSR_K7_PERFCTR0);
+ GUEST_SYNC(0);
}
}
-static struct perf_counter run_masked_events_test(struct kvm_vcpu *vcpu,
- const uint64_t masked_events[],
- const int nmasked_events)
+static void run_masked_events_test(struct kvm_vcpu *vcpu,
+ const uint64_t masked_events[],
+ const int nmasked_events)
{
struct kvm_pmu_event_filter *f;
- struct perf_counter r;
f = create_pmu_event_filter(masked_events, nmasked_events,
KVM_PMU_EVENT_ALLOW,
KVM_PMU_EVENT_FLAG_MASKED_EVENTS);
- r.raw = test_with_filter(vcpu, f);
+ test_with_filter(vcpu, f);
free(f);
-
- return r;
}
/* Matches KVM_PMU_EVENT_FILTER_MAX_EVENTS in pmu.c */
@@ -673,7 +700,6 @@ static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events,
int nevents)
{
int ntests = ARRAY_SIZE(test_cases);
- struct perf_counter c;
int i, n;
for (i = 0; i < ntests; i++) {
@@ -685,13 +711,15 @@ static void run_masked_events_tests(struct kvm_vcpu *vcpu, uint64_t *events,
n = append_test_events(test, events, nevents);
- c = run_masked_events_test(vcpu, events, n);
- TEST_ASSERT(bool_eq(c.loads, test->flags & ALLOW_LOADS) &&
- bool_eq(c.stores, test->flags & ALLOW_STORES) &&
- bool_eq(c.loads_stores,
+ run_masked_events_test(vcpu, events, n);
+
+ TEST_ASSERT(bool_eq(pmc_results.loads, test->flags & ALLOW_LOADS) &&
+ bool_eq(pmc_results.stores, test->flags & ALLOW_STORES) &&
+ bool_eq(pmc_results.loads_stores,
test->flags & ALLOW_LOADS_STORES),
- "%s loads: %u, stores: %u, loads + stores: %u",
- test->msg, c.loads, c.stores, c.loads_stores);
+ "%s loads: %lu, stores: %lu, loads + stores: %lu",
+ test->msg, pmc_results.loads, pmc_results.stores,
+ pmc_results.loads_stores);
}
}
@@ -764,6 +792,7 @@ int main(int argc, char *argv[])
struct kvm_vcpu *vcpu, *vcpu2 = NULL;
struct kvm_vm *vm;
+ TEST_REQUIRE(get_kvm_param_bool("enable_pmu"));
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER));
TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_MASKED_EVENTS));
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
index d427eb146bc5..fa03c8d1ce4e 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c
@@ -126,12 +126,16 @@ static void stable_tsc_check_supported(void)
goto skip_test;
if (fgets(buf, sizeof(buf), fp) == NULL)
- goto skip_test;
+ goto close_fp;
if (strncmp(buf, "tsc", sizeof(buf)))
- goto skip_test;
+ goto close_fp;
+ fclose(fp);
return;
+
+close_fp:
+ fclose(fp);
skip_test:
print_skip("Kernel does not use TSC clocksource - assuming that host TSC is not stable");
exit(KSFT_SKIP);
diff --git a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
index c280ba1e6572..4c90f76930f9 100644
--- a/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
+++ b/tools/testing/selftests/kvm/x86_64/vmx_pmu_caps_test.c
@@ -14,12 +14,11 @@
#define _GNU_SOURCE /* for program_invocation_short_name */
#include <sys/ioctl.h>
+#include <linux/bitmap.h>
+
#include "kvm_util.h"
#include "vmx.h"
-#define PMU_CAP_FW_WRITES (1ULL << 13)
-#define PMU_CAP_LBR_FMT 0x3f
-
union perf_capabilities {
struct {
u64 lbr_format:6;
@@ -36,59 +35,221 @@ union perf_capabilities {
u64 capabilities;
};
-static void guest_code(void)
+/*
+ * The LBR format and most PEBS features are immutable, all other features are
+ * fungible (if supported by the host and KVM).
+ */
+static const union perf_capabilities immutable_caps = {
+ .lbr_format = -1,
+ .pebs_trap = 1,
+ .pebs_arch_reg = 1,
+ .pebs_format = -1,
+ .pebs_baseline = 1,
+};
+
+static const union perf_capabilities format_caps = {
+ .lbr_format = -1,
+ .pebs_format = -1,
+};
+
+static void guest_code(uint64_t current_val)
{
- wrmsr(MSR_IA32_PERF_CAPABILITIES, PMU_CAP_LBR_FMT);
+ uint8_t vector;
+ int i;
+
+ vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, current_val);
+ GUEST_ASSERT_2(vector == GP_VECTOR, current_val, vector);
+
+ vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES, 0);
+ GUEST_ASSERT_2(vector == GP_VECTOR, 0, vector);
+
+ for (i = 0; i < 64; i++) {
+ vector = wrmsr_safe(MSR_IA32_PERF_CAPABILITIES,
+ current_val ^ BIT_ULL(i));
+ GUEST_ASSERT_2(vector == GP_VECTOR,
+ current_val ^ BIT_ULL(i), vector);
+ }
+
+ GUEST_DONE();
}
-int main(int argc, char *argv[])
+/*
+ * Verify that guest WRMSRs to PERF_CAPABILITIES #GP regardless of the value
+ * written, that the guest always sees the userspace controlled value, and that
+ * PERF_CAPABILITIES is immutable after KVM_RUN.
+ */
+static void test_guest_wrmsr_perf_capabilities(union perf_capabilities host_cap)
{
- struct kvm_vm *vm;
struct kvm_vcpu *vcpu;
- int ret;
- union perf_capabilities host_cap;
- uint64_t val;
+ struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ struct ucall uc;
+ int r, i;
- host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
- host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT);
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
- /* Create VM */
- vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
- TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM));
+ vcpu_args_set(vcpu, 1, host_cap.capabilities);
+ vcpu_run(vcpu);
- TEST_REQUIRE(kvm_cpu_has_p(X86_PROPERTY_PMU_VERSION));
- TEST_REQUIRE(kvm_cpu_property(X86_PROPERTY_PMU_VERSION) > 0);
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT_2(uc, "val = 0x%lx, vector = %lu");
+ break;
+ case UCALL_DONE:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall: %lu", uc.cmd);
+ }
- /* testcase 1, set capabilities when we have PDCM bit */
- vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, PMU_CAP_FW_WRITES);
+ ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), host_cap.capabilities);
- /* check capabilities can be retrieved with KVM_GET_MSR */
- ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES);
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
- /* check whatever we write with KVM_SET_MSR is _not_ modified */
- vcpu_run(vcpu);
- ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES);
+ r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0);
+ TEST_ASSERT(!r, "Post-KVM_RUN write '0' didn't fail");
+
+ for (i = 0; i < 64; i++) {
+ r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES,
+ host_cap.capabilities ^ BIT_ULL(i));
+ TEST_ASSERT(!r, "Post-KVM_RUN write '0x%llx'didn't fail",
+ host_cap.capabilities ^ BIT_ULL(i));
+ }
+
+ kvm_vm_free(vm);
+}
+
+/*
+ * Verify KVM allows writing PERF_CAPABILITIES with all KVM-supported features
+ * enabled, as well as '0' (to disable all features).
+ */
+static void test_basic_perf_capabilities(union perf_capabilities host_cap)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL);
- /* testcase 2, check valid LBR formats are accepted */
vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0);
- ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), 0);
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
+
+ kvm_vm_free(vm);
+}
- vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.lbr_format);
- ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format);
+static void test_fungible_perf_capabilities(union perf_capabilities host_cap)
+{
+ const uint64_t fungible_caps = host_cap.capabilities & ~immutable_caps.capabilities;
+
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ int bit;
+
+ for_each_set_bit(bit, &fungible_caps, 64) {
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, BIT_ULL(bit));
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES,
+ host_cap.capabilities & ~BIT_ULL(bit));
+ }
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
+
+ kvm_vm_free(vm);
+}
+
+/*
+ * Verify KVM rejects attempts to set unsupported and/or immutable features in
+ * PERF_CAPABILITIES. Note, LBR format and PEBS format need to be validated
+ * separately as they are multi-bit values, e.g. toggling or setting a single
+ * bit can generate a false positive without dedicated safeguards.
+ */
+static void test_immutable_perf_capabilities(union perf_capabilities host_cap)
+{
+ const uint64_t reserved_caps = (~host_cap.capabilities |
+ immutable_caps.capabilities) &
+ ~format_caps.capabilities;
+
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm = vm_create_with_one_vcpu(&vcpu, NULL);
+ union perf_capabilities val = host_cap;
+ int r, bit;
+
+ for_each_set_bit(bit, &reserved_caps, 64) {
+ r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES,
+ host_cap.capabilities ^ BIT_ULL(bit));
+ TEST_ASSERT(!r, "%s immutable feature 0x%llx (bit %d) didn't fail",
+ host_cap.capabilities & BIT_ULL(bit) ? "Setting" : "Clearing",
+ BIT_ULL(bit), bit);
+ }
/*
- * Testcase 3, check that an "invalid" LBR format is rejected. Only an
- * exact match of the host's format (and 0/disabled) is allowed.
+ * KVM only supports the host's native LBR format, as well as '0' (to
+ * disable LBR support). Verify KVM rejects all other LBR formats.
*/
- for (val = 1; val <= PMU_CAP_LBR_FMT; val++) {
- if (val == (host_cap.capabilities & PMU_CAP_LBR_FMT))
+ for (val.lbr_format = 1; val.lbr_format; val.lbr_format++) {
+ if (val.lbr_format == host_cap.lbr_format)
continue;
- ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val);
- TEST_ASSERT(!ret, "Bad LBR FMT = 0x%lx didn't fail", val);
+ r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val.capabilities);
+ TEST_ASSERT(!r, "Bad LBR FMT = 0x%x didn't fail, host = 0x%x",
+ val.lbr_format, host_cap.lbr_format);
}
- printf("Completed perf capability tests.\n");
+ /* Ditto for the PEBS format. */
+ for (val.pebs_format = 1; val.pebs_format; val.pebs_format++) {
+ if (val.pebs_format == host_cap.pebs_format)
+ continue;
+
+ r = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, val.capabilities);
+ TEST_ASSERT(!r, "Bad PEBS FMT = 0x%x didn't fail, host = 0x%x",
+ val.pebs_format, host_cap.pebs_format);
+ }
+
+ kvm_vm_free(vm);
+}
+
+/*
+ * Test that LBR MSRs are writable when LBRs are enabled, and then verify that
+ * disabling the vPMU via CPUID also disables LBR support. Set bits 2:0 of
+ * LBR_TOS as those bits are writable across all uarch implementations (arch
+ * LBRs will need to poke a different MSR).
+ */
+static void test_lbr_perf_capabilities(union perf_capabilities host_cap)
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int r;
+
+ if (!host_cap.lbr_format)
+ return;
+
+ vm = vm_create_with_one_vcpu(&vcpu, NULL);
+
+ vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, host_cap.capabilities);
+ vcpu_set_msr(vcpu, MSR_LBR_TOS, 7);
+
+ vcpu_clear_cpuid_entry(vcpu, X86_PROPERTY_PMU_VERSION.function);
+
+ r = _vcpu_set_msr(vcpu, MSR_LBR_TOS, 7);
+ TEST_ASSERT(!r, "Writing LBR_TOS should fail after disabling vPMU");
+
kvm_vm_free(vm);
}
+
+int main(int argc, char *argv[])
+{
+ union perf_capabilities host_cap;
+
+ TEST_REQUIRE(get_kvm_param_bool("enable_pmu"));
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM));
+
+ TEST_REQUIRE(kvm_cpu_has_p(X86_PROPERTY_PMU_VERSION));
+ TEST_REQUIRE(kvm_cpu_property(X86_PROPERTY_PMU_VERSION) > 0);
+
+ host_cap.capabilities = kvm_get_feature_msr(MSR_IA32_PERF_CAPABILITIES);
+
+ TEST_ASSERT(host_cap.full_width_write,
+ "Full-width writes should always be supported");
+
+ test_basic_perf_capabilities(host_cap);
+ test_fungible_perf_capabilities(host_cap);
+ test_immutable_perf_capabilities(host_cap);
+ test_guest_wrmsr_perf_capabilities(host_cap);
+ test_lbr_perf_capabilities(host_cap);
+}
diff --git a/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c b/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c
new file mode 100644
index 000000000000..905bd5ae4431
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/xcr0_cpuid_test.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * XCR0 cpuid test
+ *
+ * Copyright (C) 2022, Google LLC.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "test_util.h"
+
+#include "kvm_util.h"
+#include "processor.h"
+
+/*
+ * Assert that architectural dependency rules are satisfied, e.g. that AVX is
+ * supported if and only if SSE is supported.
+ */
+#define ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0, xfeatures, dependencies) \
+do { \
+ uint64_t __supported = (supported_xcr0) & ((xfeatures) | (dependencies)); \
+ \
+ GUEST_ASSERT_3((__supported & (xfeatures)) != (xfeatures) || \
+ __supported == ((xfeatures) | (dependencies)), \
+ __supported, (xfeatures), (dependencies)); \
+} while (0)
+
+/*
+ * Assert that KVM reports a sane, usable as-is XCR0. Architecturally, a CPU
+ * isn't strictly required to _support_ all XFeatures related to a feature, but
+ * at the same time XSETBV will #GP if bundled XFeatures aren't enabled and
+ * disabled coherently. E.g. a CPU can technically enumerate supported for
+ * XTILE_CFG but not XTILE_DATA, but attempting to enable XTILE_CFG without
+ * XTILE_DATA will #GP.
+ */
+#define ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0, xfeatures) \
+do { \
+ uint64_t __supported = (supported_xcr0) & (xfeatures); \
+ \
+ GUEST_ASSERT_2(!__supported || __supported == (xfeatures), \
+ __supported, (xfeatures)); \
+} while (0)
+
+static void guest_code(void)
+{
+ uint64_t xcr0_reset;
+ uint64_t supported_xcr0;
+ int i, vector;
+
+ set_cr4(get_cr4() | X86_CR4_OSXSAVE);
+
+ xcr0_reset = xgetbv(0);
+ supported_xcr0 = this_cpu_supported_xcr0();
+
+ GUEST_ASSERT(xcr0_reset == XFEATURE_MASK_FP);
+
+ /* Check AVX */
+ ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
+ XFEATURE_MASK_YMM,
+ XFEATURE_MASK_SSE);
+
+ /* Check MPX */
+ ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
+ XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR);
+
+ /* Check AVX-512 */
+ ASSERT_XFEATURE_DEPENDENCIES(supported_xcr0,
+ XFEATURE_MASK_AVX512,
+ XFEATURE_MASK_SSE | XFEATURE_MASK_YMM);
+ ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
+ XFEATURE_MASK_AVX512);
+
+ /* Check AMX */
+ ASSERT_ALL_OR_NONE_XFEATURE(supported_xcr0,
+ XFEATURE_MASK_XTILE);
+
+ vector = xsetbv_safe(0, supported_xcr0);
+ GUEST_ASSERT_2(!vector, supported_xcr0, vector);
+
+ for (i = 0; i < 64; i++) {
+ if (supported_xcr0 & BIT_ULL(i))
+ continue;
+
+ vector = xsetbv_safe(0, supported_xcr0 | BIT_ULL(i));
+ GUEST_ASSERT_3(vector == GP_VECTOR, supported_xcr0, vector, BIT_ULL(i));
+ }
+
+ GUEST_DONE();
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct ucall uc;
+
+ TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE));
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code);
+ run = vcpu->run;
+
+ vm_init_descriptor_tables(vm);
+ vcpu_init_descriptor_tables(vcpu);
+
+ while (1) {
+ vcpu_run(vcpu);
+
+ TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
+ "Unexpected exit reason: %u (%s),\n",
+ run->exit_reason,
+ exit_reason_str(run->exit_reason));
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT_3(uc, "0x%lx 0x%lx 0x%lx");
+ break;
+ case UCALL_DONE:
+ goto done;
+ default:
+ TEST_FAIL("Unknown ucall %lu", uc.cmd);
+ }
+ }
+
+done:
+ kvm_vm_free(vm);
+ return 0;
+}
diff --git a/tools/testing/selftests/mm/ksm_functional_tests.c b/tools/testing/selftests/mm/ksm_functional_tests.c
index 7bc9fc17c9f0..26853badae70 100644
--- a/tools/testing/selftests/mm/ksm_functional_tests.c
+++ b/tools/testing/selftests/mm/ksm_functional_tests.c
@@ -91,9 +91,10 @@ static int ksm_merge(void)
return 0;
}
-static char *mmap_and_merge_range(char val, unsigned long size)
+static char *mmap_and_merge_range(char val, unsigned long size, bool use_prctl)
{
char *map;
+ int ret;
map = mmap(NULL, size, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANON, -1, 0);
@@ -110,7 +111,17 @@ static char *mmap_and_merge_range(char val, unsigned long size)
/* Make sure each page contains the same values to merge them. */
memset(map, val, size);
- if (madvise(map, size, MADV_MERGEABLE)) {
+
+ if (use_prctl) {
+ ret = prctl(PR_SET_MEMORY_MERGE, 1, 0, 0, 0);
+ if (ret < 0 && errno == EINVAL) {
+ ksft_test_result_skip("PR_SET_MEMORY_MERGE not supported\n");
+ goto unmap;
+ } else if (ret) {
+ ksft_test_result_fail("PR_SET_MEMORY_MERGE=1 failed\n");
+ goto unmap;
+ }
+ } else if (madvise(map, size, MADV_MERGEABLE)) {
ksft_test_result_fail("MADV_MERGEABLE failed\n");
goto unmap;
}
@@ -133,7 +144,7 @@ static void test_unmerge(void)
ksft_print_msg("[RUN] %s\n", __func__);
- map = mmap_and_merge_range(0xcf, size);
+ map = mmap_and_merge_range(0xcf, size, false);
if (map == MAP_FAILED)
return;
@@ -155,7 +166,7 @@ static void test_unmerge_discarded(void)
ksft_print_msg("[RUN] %s\n", __func__);
- map = mmap_and_merge_range(0xcf, size);
+ map = mmap_and_merge_range(0xcf, size, false);
if (map == MAP_FAILED)
return;
@@ -187,7 +198,7 @@ static void test_unmerge_uffd_wp(void)
ksft_print_msg("[RUN] %s\n", __func__);
- map = mmap_and_merge_range(0xcf, size);
+ map = mmap_and_merge_range(0xcf, size, false);
if (map == MAP_FAILED)
return;
@@ -323,9 +334,31 @@ static void test_prctl_fork(void)
ksft_test_result_pass("PR_SET_MEMORY_MERGE value is inherited\n");
}
+static void test_prctl_unmerge(void)
+{
+ const unsigned int size = 2 * MiB;
+ char *map;
+
+ ksft_print_msg("[RUN] %s\n", __func__);
+
+ map = mmap_and_merge_range(0xcf, size, true);
+ if (map == MAP_FAILED)
+ return;
+
+ if (prctl(PR_SET_MEMORY_MERGE, 0, 0, 0, 0)) {
+ ksft_test_result_fail("PR_SET_MEMORY_MERGE=0 failed\n");
+ goto unmap;
+ }
+
+ ksft_test_result(!range_maps_duplicates(map, size),
+ "Pages were unmerged\n");
+unmap:
+ munmap(map, size);
+}
+
int main(int argc, char **argv)
{
- unsigned int tests = 4;
+ unsigned int tests = 5;
int err;
#ifdef __NR_userfaultfd
@@ -355,6 +388,7 @@ int main(int argc, char **argv)
test_prctl();
test_prctl_fork();
+ test_prctl_unmerge();
err = ksft_get_fail_cnt();
if (err)
diff --git a/tools/testing/selftests/mm/protection_keys.c b/tools/testing/selftests/mm/protection_keys.c
index 95f403a0c46d..0381c34fdd56 100644
--- a/tools/testing/selftests/mm/protection_keys.c
+++ b/tools/testing/selftests/mm/protection_keys.c
@@ -98,7 +98,7 @@ int tracing_root_ok(void)
void tracing_on(void)
{
#if CONTROL_TRACING > 0
-#define TRACEDIR "/sys/kernel/debug/tracing"
+#define TRACEDIR "/sys/kernel/tracing"
char pidstr[32];
if (!tracing_root_ok())
@@ -124,7 +124,7 @@ void tracing_off(void)
#if CONTROL_TRACING > 0
if (!tracing_root_ok())
return;
- cat_into_file("0", "/sys/kernel/debug/tracing/tracing_on");
+ cat_into_file("0", "/sys/kernel/tracing/tracing_on");
#endif
}
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile
index 6ba95cd19e42..ae2bfc0d822f 100644
--- a/tools/testing/selftests/powerpc/Makefile
+++ b/tools/testing/selftests/powerpc/Makefile
@@ -45,28 +45,28 @@ $(SUB_DIRS):
include ../lib.mk
override define RUN_TESTS
- @for TARGET in $(SUB_DIRS); do \
+ +@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests;\
done;
endef
override define INSTALL_RULE
- @for TARGET in $(SUB_DIRS); do \
+ +@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install;\
done;
endef
override define EMIT_TESTS
- @for TARGET in $(SUB_DIRS); do \
+ +@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests;\
done;
endef
override define CLEAN
- @for TARGET in $(SUB_DIRS); do \
+ +@for TARGET in $(SUB_DIRS); do \
BUILD_TARGET=$(OUTPUT)/$$TARGET; \
$(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean; \
done;
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
index 003e1b3d9300..a89f1fbf86ec 100644
--- a/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
+++ b/tools/testing/selftests/powerpc/copyloops/asm/ppc_asm.h
@@ -27,6 +27,7 @@
#define _GLOBAL_TOC(A) _GLOBAL(A)
#define _GLOBAL_TOC_KASAN(A) _GLOBAL(A)
#define _GLOBAL_KASAN(A) _GLOBAL(A)
+#define CFUNC(name) name
#define PPC_MTOCRF(A, B) mtocrf A, B
diff --git a/tools/testing/selftests/powerpc/dscr/Makefile b/tools/testing/selftests/powerpc/dscr/Makefile
index 845db6273a1b..9289d5febe1e 100644
--- a/tools/testing/selftests/powerpc/dscr/Makefile
+++ b/tools/testing/selftests/powerpc/dscr/Makefile
@@ -3,11 +3,10 @@ TEST_GEN_PROGS := dscr_default_test dscr_explicit_test dscr_user_test \
dscr_inherit_test dscr_inherit_exec_test dscr_sysfs_test \
dscr_sysfs_thread_test
-TEST_FILES := settings
-
top_srcdir = ../../../../..
include ../../lib.mk
$(OUTPUT)/dscr_default_test: LDLIBS += -lpthread
+$(OUTPUT)/dscr_explicit_test: LDLIBS += -lpthread
$(TEST_GEN_PROGS): ../harness.c ../utils.c
diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h
index 2c54998d4715..b281659071e8 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr.h
+++ b/tools/testing/selftests/powerpc/dscr/dscr.h
@@ -86,8 +86,4 @@ void set_default_dscr(unsigned long val)
}
}
-double uniform_deviate(int seed)
-{
- return seed * (1.0 / (RAND_MAX + 1.0));
-}
#endif /* _SELFTESTS_POWERPC_DSCR_DSCR_H */
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
index e76611e608af..60ab02525b79 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c
@@ -9,118 +9,161 @@
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
*/
-#include "dscr.h"
-
-static unsigned long dscr; /* System DSCR default */
-static unsigned long sequence;
-static unsigned long result[THREADS];
-
-static void *do_test(void *in)
-{
- unsigned long thread = (unsigned long)in;
- unsigned long i;
- for (i = 0; i < COUNT; i++) {
- unsigned long d, cur_dscr, cur_dscr_usr;
- unsigned long s1, s2;
+#define _GNU_SOURCE
- s1 = READ_ONCE(sequence);
- if (s1 & 1)
- continue;
- rmb();
+#include "dscr.h"
- d = dscr;
- cur_dscr = get_dscr();
- cur_dscr_usr = get_dscr_usr();
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
- rmb();
- s2 = sequence;
+static void *dscr_default_lockstep_writer(void *arg)
+{
+ sem_t *reader_sem = (sem_t *)arg;
+ sem_t *writer_sem = (sem_t *)arg + 1;
+ unsigned long expected_dscr = 0;
- if (s1 != s2)
- continue;
+ for (int i = 0; i < COUNT; i++) {
+ FAIL_IF_EXIT(sem_wait(writer_sem));
- if (cur_dscr != d) {
- fprintf(stderr, "thread %ld kernel DSCR should be %ld "
- "but is %ld\n", thread, d, cur_dscr);
- result[thread] = 1;
- pthread_exit(&result[thread]);
- }
+ set_default_dscr(expected_dscr);
+ expected_dscr = (expected_dscr + 1) % DSCR_MAX;
- if (cur_dscr_usr != d) {
- fprintf(stderr, "thread %ld user DSCR should be %ld "
- "but is %ld\n", thread, d, cur_dscr_usr);
- result[thread] = 1;
- pthread_exit(&result[thread]);
- }
+ FAIL_IF_EXIT(sem_post(reader_sem));
}
- result[thread] = 0;
- pthread_exit(&result[thread]);
+
+ return NULL;
}
-int dscr_default(void)
+int dscr_default_lockstep_test(void)
{
- pthread_t threads[THREADS];
- unsigned long i, *status[THREADS];
- unsigned long orig_dscr_default;
+ pthread_t writer;
+ sem_t rw_semaphores[2];
+ sem_t *reader_sem = &rw_semaphores[0];
+ sem_t *writer_sem = &rw_semaphores[1];
+ unsigned long expected_dscr = 0;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
- orig_dscr_default = get_default_dscr();
+ FAIL_IF(sem_init(reader_sem, 0, 0));
+ FAIL_IF(sem_init(writer_sem, 0, 1)); /* writer starts first */
+ FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
+ FAIL_IF(pthread_create(&writer, NULL, dscr_default_lockstep_writer, (void *)rw_semaphores));
- /* Initial DSCR default */
- dscr = 1;
- set_default_dscr(dscr);
+ for (int i = 0; i < COUNT ; i++) {
+ FAIL_IF(sem_wait(reader_sem));
- /* Spawn all testing threads */
- for (i = 0; i < THREADS; i++) {
- if (pthread_create(&threads[i], NULL, do_test, (void *)i)) {
- perror("pthread_create() failed");
- goto fail;
- }
- }
+ FAIL_IF(get_dscr() != expected_dscr);
+ FAIL_IF(get_dscr_usr() != expected_dscr);
- srand(getpid());
+ expected_dscr = (expected_dscr + 1) % DSCR_MAX;
- /* Keep changing the DSCR default */
- for (i = 0; i < COUNT; i++) {
- double ret = uniform_deviate(rand());
+ FAIL_IF(sem_post(writer_sem));
+ }
- if (ret < 0.0001) {
- sequence++;
- wmb();
+ FAIL_IF(pthread_join(writer, NULL));
+ FAIL_IF(sem_destroy(reader_sem));
+ FAIL_IF(sem_destroy(writer_sem));
- dscr++;
- if (dscr > DSCR_MAX)
- dscr = 0;
+ return 0;
+}
- set_default_dscr(dscr);
+struct random_thread_args {
+ pthread_t thread_id;
+ unsigned long *expected_system_dscr;
+ pthread_rwlock_t *rw_lock;
+ pthread_barrier_t *barrier;
+};
- wmb();
- sequence++;
+static void *dscr_default_random_thread(void *in)
+{
+ struct random_thread_args *args = (struct random_thread_args *)in;
+ unsigned long *expected_dscr_p = args->expected_system_dscr;
+ pthread_rwlock_t *rw_lock = args->rw_lock;
+ int err;
+
+ srand(gettid());
+
+ err = pthread_barrier_wait(args->barrier);
+ FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
+
+ for (int i = 0; i < COUNT; i++) {
+ unsigned long expected_dscr;
+ unsigned long current_dscr;
+ unsigned long current_dscr_usr;
+
+ FAIL_IF_EXIT(pthread_rwlock_rdlock(rw_lock));
+ expected_dscr = *expected_dscr_p;
+ current_dscr = get_dscr();
+ current_dscr_usr = get_dscr_usr();
+ FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
+
+ FAIL_IF_EXIT(current_dscr != expected_dscr);
+ FAIL_IF_EXIT(current_dscr_usr != expected_dscr);
+
+ if (rand() % 10 == 0) {
+ unsigned long next_dscr;
+
+ FAIL_IF_EXIT(pthread_rwlock_wrlock(rw_lock));
+ next_dscr = (*expected_dscr_p + 1) % DSCR_MAX;
+ set_default_dscr(next_dscr);
+ *expected_dscr_p = next_dscr;
+ FAIL_IF_EXIT(pthread_rwlock_unlock(rw_lock));
}
}
- /* Individual testing thread exit status */
- for (i = 0; i < THREADS; i++) {
- if (pthread_join(threads[i], (void **)&(status[i]))) {
- perror("pthread_join() failed");
- goto fail;
- }
+ pthread_exit((void *)0);
+}
- if (*status[i]) {
- printf("%ldth thread failed to join with %ld status\n",
- i, *status[i]);
- goto fail;
- }
+int dscr_default_random_test(void)
+{
+ struct random_thread_args threads[THREADS];
+ unsigned long expected_system_dscr = 0;
+ pthread_rwlockattr_t rwlock_attr;
+ pthread_rwlock_t rw_lock;
+ pthread_barrier_t barrier;
+
+ SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
+
+ FAIL_IF(pthread_rwlockattr_setkind_np(&rwlock_attr,
+ PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP));
+ FAIL_IF(pthread_rwlock_init(&rw_lock, &rwlock_attr));
+ FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));
+
+ set_default_dscr(expected_system_dscr);
+
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].expected_system_dscr = &expected_system_dscr;
+ threads[i].rw_lock = &rw_lock;
+ threads[i].barrier = &barrier;
+
+ FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
+ dscr_default_random_thread, (void *)&threads[i]));
}
- set_default_dscr(orig_dscr_default);
+
+ for (int i = 0; i < THREADS; i++)
+ FAIL_IF(pthread_join(threads[i].thread_id, NULL));
+
+ FAIL_IF(pthread_barrier_destroy(&barrier));
+ FAIL_IF(pthread_rwlock_destroy(&rw_lock));
+
return 0;
-fail:
- set_default_dscr(orig_dscr_default);
- return 1;
}
int main(int argc, char *argv[])
{
- return test_harness(dscr_default, "dscr_default_test");
+ unsigned long orig_dscr_default = 0;
+ int err = 0;
+
+ if (have_hwcap2(PPC_FEATURE2_DSCR))
+ orig_dscr_default = get_default_dscr();
+
+ err |= test_harness(dscr_default_lockstep_test, "dscr_default_lockstep_test");
+ err |= test_harness(dscr_default_random_test, "dscr_default_random_test");
+
+ if (have_hwcap2(PPC_FEATURE2_DSCR))
+ set_default_dscr(orig_dscr_default);
+
+ return err;
}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
index 32fcf2b324b1..e2268e9183a8 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_explicit_test.c
@@ -7,64 +7,167 @@
* privilege state SPR and the problem state SPR for this purpose.
*
* When using the privilege state SPR, the instructions such as
- * mfspr or mtspr are priviledged and the kernel emulates them
- * for us. Instructions using problem state SPR can be exuecuted
+ * mfspr or mtspr are privileged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be executed
* directly without any emulation if the HW supports them. Else
* they also get emulated by the kernel.
*
* Copyright 2012, Anton Blanchard, IBM Corporation.
* Copyright 2015, Anshuman Khandual, IBM Corporation.
*/
+
+#define _GNU_SOURCE
+
#include "dscr.h"
+#include "utils.h"
+
+#include <pthread.h>
+#include <sched.h>
+#include <semaphore.h>
-int dscr_explicit(void)
+void *dscr_explicit_lockstep_thread(void *args)
{
- unsigned long i, dscr = 0;
+ sem_t *prev = (sem_t *)args;
+ sem_t *next = (sem_t *)args + 1;
+ unsigned long expected_dscr = 0;
+
+ set_dscr(expected_dscr);
+ srand(gettid());
+
+ for (int i = 0; i < COUNT; i++) {
+ FAIL_IF_EXIT(sem_wait(prev));
+
+ FAIL_IF_EXIT(expected_dscr != get_dscr());
+ FAIL_IF_EXIT(expected_dscr != get_dscr_usr());
+
+ expected_dscr = (expected_dscr + 1) % DSCR_MAX;
+ set_dscr(expected_dscr);
+
+ FAIL_IF_EXIT(sem_post(next));
+ }
+
+ return NULL;
+}
+
+int dscr_explicit_lockstep_test(void)
+{
+ pthread_t thread;
+ sem_t semaphores[2];
+ sem_t *prev = &semaphores[1]; /* reversed prev/next than for the other thread */
+ sem_t *next = &semaphores[0];
+ unsigned long expected_dscr = 0;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
- srand(getpid());
- set_dscr(dscr);
+ srand(gettid());
+ set_dscr(expected_dscr);
- for (i = 0; i < COUNT; i++) {
- unsigned long cur_dscr, cur_dscr_usr;
- double ret = uniform_deviate(rand());
+ FAIL_IF(sem_init(prev, 0, 0));
+ FAIL_IF(sem_init(next, 0, 1)); /* other thread starts first */
+ FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
+ FAIL_IF(pthread_create(&thread, NULL, dscr_explicit_lockstep_thread, (void *)semaphores));
- if (ret < 0.001) {
- dscr++;
- if (dscr > DSCR_MAX)
- dscr = 0;
+ for (int i = 0; i < COUNT; i++) {
+ FAIL_IF(sem_wait(prev));
- set_dscr(dscr);
- }
+ FAIL_IF(expected_dscr != get_dscr());
+ FAIL_IF(expected_dscr != get_dscr_usr());
- cur_dscr = get_dscr();
- if (cur_dscr != dscr) {
- fprintf(stderr, "Kernel DSCR should be %ld but "
- "is %ld\n", dscr, cur_dscr);
- return 1;
- }
+ expected_dscr = (expected_dscr - 1) % DSCR_MAX;
+ set_dscr(expected_dscr);
+
+ FAIL_IF(sem_post(next));
+ }
+
+ FAIL_IF(pthread_join(thread, NULL));
+ FAIL_IF(sem_destroy(prev));
+ FAIL_IF(sem_destroy(next));
+
+ return 0;
+}
+
+struct random_thread_args {
+ pthread_t thread_id;
+ bool do_yields;
+ pthread_barrier_t *barrier;
+};
+
+void *dscr_explicit_random_thread(void *in)
+{
+ struct random_thread_args *args = (struct random_thread_args *)in;
+ unsigned long expected_dscr = 0;
+ int err;
+
+ srand(gettid());
+
+ err = pthread_barrier_wait(args->barrier);
+ FAIL_IF_EXIT(err != 0 && err != PTHREAD_BARRIER_SERIAL_THREAD);
- ret = uniform_deviate(rand());
- if (ret < 0.001) {
- dscr++;
- if (dscr > DSCR_MAX)
- dscr = 0;
+ for (int i = 0; i < COUNT; i++) {
+ expected_dscr = rand() % DSCR_MAX;
+ set_dscr(expected_dscr);
- set_dscr_usr(dscr);
+ for (int j = rand() % 5; j > 0; --j) {
+ FAIL_IF_EXIT(get_dscr() != expected_dscr);
+ FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);
+
+ if (args->do_yields && rand() % 2)
+ sched_yield();
}
- cur_dscr_usr = get_dscr_usr();
- if (cur_dscr_usr != dscr) {
- fprintf(stderr, "User DSCR should be %ld but "
- "is %ld\n", dscr, cur_dscr_usr);
- return 1;
+ expected_dscr = rand() % DSCR_MAX;
+ set_dscr_usr(expected_dscr);
+
+ for (int j = rand() % 5; j > 0; --j) {
+ FAIL_IF_EXIT(get_dscr() != expected_dscr);
+ FAIL_IF_EXIT(get_dscr_usr() != expected_dscr);
+
+ if (args->do_yields && rand() % 2)
+ sched_yield();
}
}
+
+ return NULL;
+}
+
+int dscr_explicit_random_test(void)
+{
+ struct random_thread_args threads[THREADS];
+ pthread_barrier_t barrier;
+
+ SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
+
+ FAIL_IF(pthread_barrier_init(&barrier, NULL, THREADS));
+
+ for (int i = 0; i < THREADS; i++) {
+ threads[i].do_yields = i % 2 == 0;
+ threads[i].barrier = &barrier;
+
+ FAIL_IF(pthread_create(&threads[i].thread_id, NULL,
+ dscr_explicit_random_thread, (void *)&threads[i]));
+ }
+
+ for (int i = 0; i < THREADS; i++)
+ FAIL_IF(pthread_join(threads[i].thread_id, NULL));
+
+ FAIL_IF(pthread_barrier_destroy(&barrier));
+
return 0;
}
int main(int argc, char *argv[])
{
- return test_harness(dscr_explicit, "dscr_explicit_test");
+ unsigned long orig_dscr_default = 0;
+ int err = 0;
+
+ if (have_hwcap2(PPC_FEATURE2_DSCR))
+ orig_dscr_default = get_default_dscr();
+
+ err |= test_harness(dscr_explicit_lockstep_test, "dscr_explicit_lockstep_test");
+ err |= test_harness(dscr_explicit_random_test, "dscr_explicit_random_test");
+
+ if (have_hwcap2(PPC_FEATURE2_DSCR))
+ set_default_dscr(orig_dscr_default);
+
+ return err;
}
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
index f9dfd3d3c2d5..68ce328e813e 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_inherit_test.c
@@ -7,8 +7,8 @@
* value using mfspr.
*
* When using the privilege state SPR, the instructions such as
- * mfspr or mtspr are priviledged and the kernel emulates them
- * for us. Instructions using problem state SPR can be exuecuted
+ * mfspr or mtspr are privileged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be executed
* directly without any emulation if the HW supports them. Else
* they also get emulated by the kernel.
*
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
index 4f1fef6198fc..e7cd0d6b1fad 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c
@@ -67,17 +67,14 @@ static int check_all_cpu_dscr_defaults(unsigned long val)
int dscr_sysfs(void)
{
unsigned long orig_dscr_default;
- int i, j;
SKIP_IF(!have_hwcap2(PPC_FEATURE2_DSCR));
orig_dscr_default = get_default_dscr();
- for (i = 0; i < COUNT; i++) {
- for (j = 0; j < DSCR_MAX; j++) {
- set_default_dscr(j);
- if (check_all_cpu_dscr_defaults(j))
- goto fail;
- }
+ for (int i = 0; i < DSCR_MAX; i++) {
+ set_default_dscr(i);
+ if (check_all_cpu_dscr_defaults(i))
+ goto fail;
}
set_default_dscr(orig_dscr_default);
return 0;
diff --git a/tools/testing/selftests/powerpc/dscr/dscr_user_test.c b/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
index e09072446dd3..67bb872a246a 100644
--- a/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
+++ b/tools/testing/selftests/powerpc/dscr/dscr_user_test.c
@@ -8,8 +8,8 @@
* numbers.
*
* When using the privilege state SPR, the instructions such as
- * mfspr or mtspr are priviledged and the kernel emulates them
- * for us. Instructions using problem state SPR can be exuecuted
+ * mfspr or mtspr are privileged and the kernel emulates them
+ * for us. Instructions using problem state SPR can be executed
* directly without any emulation if the HW supports them. Else
* they also get emulated by the kernel.
*
diff --git a/tools/testing/selftests/powerpc/dscr/settings b/tools/testing/selftests/powerpc/dscr/settings
deleted file mode 100644
index e7b9417537fb..000000000000
--- a/tools/testing/selftests/powerpc/dscr/settings
+++ /dev/null
@@ -1 +0,0 @@
-timeout=0
diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h
index eed7dd7582b2..44bfd48b93d6 100644
--- a/tools/testing/selftests/powerpc/include/utils.h
+++ b/tools/testing/selftests/powerpc/include/utils.h
@@ -31,7 +31,10 @@ int read_auxv(char *buf, ssize_t buf_size);
void *find_auxv_entry(int type, char *auxv);
void *get_auxv_entry(int type);
+#define BIND_CPU_ANY (-1)
+
int pick_online_cpu(void);
+int bind_to_cpu(int cpu);
int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base);
int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base);
diff --git a/tools/testing/selftests/powerpc/math/vmx_signal.c b/tools/testing/selftests/powerpc/math/vmx_signal.c
index b340a5c4e79d..c307dff19c12 100644
--- a/tools/testing/selftests/powerpc/math/vmx_signal.c
+++ b/tools/testing/selftests/powerpc/math/vmx_signal.c
@@ -151,5 +151,6 @@ int test_signal_vmx(void)
int main(int argc, char *argv[])
{
+ test_harness_set_timeout(360);
return test_harness(test_signal_vmx, "vmx_signal");
}
diff --git a/tools/testing/selftests/powerpc/mm/Makefile b/tools/testing/selftests/powerpc/mm/Makefile
index 19dd0b2ea397..4a6608beef0e 100644
--- a/tools/testing/selftests/powerpc/mm/Makefile
+++ b/tools/testing/selftests/powerpc/mm/Makefile
@@ -32,7 +32,7 @@ $(OUTPUT)/stack_expansion_ldst: CFLAGS += -fno-stack-protector
$(OUTPUT)/stack_expansion_ldst: ../utils.c
$(OUTPUT)/tempfile:
- dd if=/dev/zero of=$@ bs=64k count=1
+ dd if=/dev/zero of=$@ bs=64k count=1 status=none
$(OUTPUT)/tlbie_test: LDLIBS += -lpthread
$(OUTPUT)/pkey_siginfo: LDLIBS += -lpthread
diff --git a/tools/testing/selftests/powerpc/pmu/Makefile b/tools/testing/selftests/powerpc/pmu/Makefile
index 30803353bd7c..2b95e44d20ff 100644
--- a/tools/testing/selftests/powerpc/pmu/Makefile
+++ b/tools/testing/selftests/powerpc/pmu/Makefile
@@ -25,32 +25,35 @@ $(OUTPUT)/per_event_excludes: ../utils.c
DEFAULT_RUN_TESTS := $(RUN_TESTS)
override define RUN_TESTS
$(DEFAULT_RUN_TESTS)
- TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
- TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
- TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
+ +TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
+ +TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
+ +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests
endef
DEFAULT_EMIT_TESTS := $(EMIT_TESTS)
override define EMIT_TESTS
$(DEFAULT_EMIT_TESTS)
- TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
- TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
- TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
+ +TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
+ +TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
+ +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -s -C $$TARGET emit_tests
endef
DEFAULT_INSTALL_RULE := $(INSTALL_RULE)
override define INSTALL_RULE
$(DEFAULT_INSTALL_RULE)
- TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
- TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
- TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
+ +TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
+ +TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
+ +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET install
endef
-clean:
+DEFAULT_CLEAN := $(CLEAN)
+override define CLEAN
+ $(DEFAULT_CLEAN)
$(RM) $(TEST_GEN_PROGS) $(OUTPUT)/loop.o
- TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
- TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
- TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
+ +TARGET=ebb; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
+ +TARGET=sampling_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
+ +TARGET=event_code_tests; BUILD_TARGET=$$OUTPUT/$$TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -C $$TARGET clean
+endef
ebb:
TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all
@@ -61,4 +64,4 @@ sampling_tests:
event_code_tests:
TARGET=$@; BUILD_TARGET=$$OUTPUT/$$TARGET; mkdir -p $$BUILD_TARGET; $(MAKE) OUTPUT=$$BUILD_TARGET -k -C $$TARGET all
-.PHONY: all run_tests clean ebb sampling_tests event_code_tests
+.PHONY: all run_tests ebb sampling_tests event_code_tests
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
index 3cd33eb51e5e..fab7f34d7ce1 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_pinned_vs_ebb_test.c
@@ -45,9 +45,8 @@ int cpu_event_pinned_vs_ebb(void)
SKIP_IF(!ebb_is_supported());
- cpu = pick_online_cpu();
+ cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
- FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
index 8466ef9d7de8..7c54c262036e 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/cpu_event_vs_ebb_test.c
@@ -43,9 +43,8 @@ int cpu_event_vs_ebb(void)
SKIP_IF(!ebb_is_supported());
- cpu = pick_online_cpu();
+ cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
- FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
index 4d822cb3589c..d7064b54c64f 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/ebb_vs_cpu_event_test.c
@@ -43,9 +43,8 @@ int ebb_vs_cpu_event(void)
SKIP_IF(!ebb_is_supported());
- cpu = pick_online_cpu();
+ cpu = bind_to_cpu(BIND_CPU_ANY);
FAIL_IF(cpu < 0);
- FAIL_IF(bind_to_cpu(cpu));
FAIL_IF(pipe(read_pipe.fds) == -1);
FAIL_IF(pipe(write_pipe.fds) == -1);
diff --git a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
index 9b0f70d59702..4ac22b2e774f 100644
--- a/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
+++ b/tools/testing/selftests/powerpc/pmu/ebb/multi_ebb_procs_test.c
@@ -75,13 +75,11 @@ static int cycles_child(void)
int multi_ebb_procs(void)
{
pid_t pids[NR_CHILDREN];
- int cpu, rc, i;
+ int rc, i;
SKIP_IF(!ebb_is_supported());
- cpu = pick_online_cpu();
- FAIL_IF(cpu < 0);
- FAIL_IF(bind_to_cpu(cpu));
+ FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
for (i = 0; i < NR_CHILDREN; i++) {
pids[i] = fork();
diff --git a/tools/testing/selftests/powerpc/pmu/lib.c b/tools/testing/selftests/powerpc/pmu/lib.c
index 719f94f10d41..321357987408 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.c
+++ b/tools/testing/selftests/powerpc/pmu/lib.c
@@ -14,19 +14,6 @@
#include "utils.h"
#include "lib.h"
-
-int bind_to_cpu(int cpu)
-{
- cpu_set_t mask;
-
- printf("Binding to cpu %d\n", cpu);
-
- CPU_ZERO(&mask);
- CPU_SET(cpu, &mask);
-
- return sched_setaffinity(0, sizeof(mask), &mask);
-}
-
#define PARENT_TOKEN 0xAA
#define CHILD_TOKEN 0x55
@@ -116,12 +103,10 @@ static int eat_cpu_child(union pipe read_pipe, union pipe write_pipe)
pid_t eat_cpu(int (test_function)(void))
{
union pipe read_pipe, write_pipe;
- int cpu, rc;
+ int rc;
pid_t pid;
- cpu = pick_online_cpu();
- FAIL_IF(cpu < 0);
- FAIL_IF(bind_to_cpu(cpu));
+ FAIL_IF(bind_to_cpu(BIND_CPU_ANY) < 0);
if (pipe(read_pipe.fds) == -1)
return -1;
diff --git a/tools/testing/selftests/powerpc/pmu/lib.h b/tools/testing/selftests/powerpc/pmu/lib.h
index bf1bec013bbb..1d62403ae6ea 100644
--- a/tools/testing/selftests/powerpc/pmu/lib.h
+++ b/tools/testing/selftests/powerpc/pmu/lib.h
@@ -20,7 +20,6 @@ union pipe {
int fds[2];
};
-extern int bind_to_cpu(int cpu);
extern int kill_child_and_wait(pid_t child_pid);
extern int wait_for_child(pid_t child_pid);
extern int sync_with_child(union pipe read_pipe, union pipe write_pipe);
diff --git a/tools/testing/selftests/powerpc/pmu/sampling_tests/mmcra_thresh_marked_sample_test.c b/tools/testing/selftests/powerpc/pmu/sampling_tests/mmcra_thresh_marked_sample_test.c
index 022cc1655eb5..75527876ad3c 100644
--- a/tools/testing/selftests/powerpc/pmu/sampling_tests/mmcra_thresh_marked_sample_test.c
+++ b/tools/testing/selftests/powerpc/pmu/sampling_tests/mmcra_thresh_marked_sample_test.c
@@ -63,9 +63,9 @@ static int mmcra_thresh_marked_sample(void)
get_mmcra_thd_stop(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, marked) !=
get_mmcra_marked(get_reg_value(intr_regs, "MMCRA"), 4));
- FAIL_IF(EV_CODE_EXTRACT(event.attr.config, sample >> 2) !=
+ FAIL_IF((EV_CODE_EXTRACT(event.attr.config, sample) >> 2) !=
get_mmcra_rand_samp_elig(get_reg_value(intr_regs, "MMCRA"), 4));
- FAIL_IF(EV_CODE_EXTRACT(event.attr.config, sample & 0x3) !=
+ FAIL_IF((EV_CODE_EXTRACT(event.attr.config, sample) & 0x3) !=
get_mmcra_sample_mode(get_reg_value(intr_regs, "MMCRA"), 4));
FAIL_IF(EV_CODE_EXTRACT(event.attr.config, sm) !=
get_mmcra_sm(get_reg_value(intr_regs, "MMCRA"), 4));
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h b/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
index 2b488b78c4f2..e713b69d694a 100644
--- a/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
+++ b/tools/testing/selftests/powerpc/stringloops/asm/ppc_asm.h
@@ -9,6 +9,7 @@
#define _GLOBAL(A) FUNC_START(test_ ## A)
#define _GLOBAL_TOC(A) FUNC_START(test_ ## A)
+#define CFUNC(name) name
#define CONFIG_ALTIVEC
diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c
index 7c8cfedb012a..252fb4a95e90 100644
--- a/tools/testing/selftests/powerpc/utils.c
+++ b/tools/testing/selftests/powerpc/utils.c
@@ -452,6 +452,29 @@ done:
return cpu;
}
+int bind_to_cpu(int cpu)
+{
+ cpu_set_t mask;
+ int err;
+
+ if (cpu == BIND_CPU_ANY) {
+ cpu = pick_online_cpu();
+ if (cpu < 0)
+ return cpu;
+ }
+
+ printf("Binding to cpu %d\n", cpu);
+
+ CPU_ZERO(&mask);
+ CPU_SET(cpu, &mask);
+
+ err = sched_setaffinity(0, sizeof(mask), &mask);
+ if (err)
+ return err;
+
+ return cpu;
+}
+
bool is_ppc64le(void)
{
struct utsname uts;
diff --git a/tools/testing/selftests/riscv/Makefile b/tools/testing/selftests/riscv/Makefile
new file mode 100644
index 000000000000..32a72902d045
--- /dev/null
+++ b/tools/testing/selftests/riscv/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0
+# Originally tools/testing/arm64/Makefile
+
+# When ARCH not overridden for crosscompiling, lookup machine
+ARCH ?= $(shell uname -m 2>/dev/null || echo not)
+
+ifneq (,$(filter $(ARCH),riscv))
+RISCV_SUBTARGETS ?= hwprobe
+else
+RISCV_SUBTARGETS :=
+endif
+
+CFLAGS := -Wall -O2 -g
+
+# A proper top_srcdir is needed by KSFT(lib.mk)
+top_srcdir = $(realpath ../../../../)
+
+# Additional include paths needed by kselftest.h and local headers
+CFLAGS += -I$(top_srcdir)/tools/testing/selftests/
+
+CFLAGS += $(KHDR_INCLUDES)
+
+export CFLAGS
+export top_srcdir
+
+all:
+ @for DIR in $(RISCV_SUBTARGETS); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ mkdir -p $$BUILD_TARGET; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
+install: all
+ @for DIR in $(RISCV_SUBTARGETS); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
+run_tests: all
+ @for DIR in $(RISCV_SUBTARGETS); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
+# Avoid any output on non riscv on emit_tests
+emit_tests: all
+ @for DIR in $(RISCV_SUBTARGETS); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
+clean:
+ @for DIR in $(RISCV_SUBTARGETS); do \
+ BUILD_TARGET=$(OUTPUT)/$$DIR; \
+ $(MAKE) OUTPUT=$$BUILD_TARGET -C $$DIR $@; \
+ done
+
+.PHONY: all clean install run_tests emit_tests
diff --git a/tools/testing/selftests/riscv/hwprobe/Makefile b/tools/testing/selftests/riscv/hwprobe/Makefile
new file mode 100644
index 000000000000..ebdbb3c22e54
--- /dev/null
+++ b/tools/testing/selftests/riscv/hwprobe/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 ARM Limited
+# Originally tools/testing/arm64/abi/Makefile
+
+TEST_GEN_PROGS := hwprobe
+
+include ../../lib.mk
+
+$(OUTPUT)/hwprobe: hwprobe.c sys_hwprobe.S
+ $(CC) -o$@ $(CFLAGS) $(LDFLAGS) $^
diff --git a/tools/testing/selftests/riscv/hwprobe/hwprobe.c b/tools/testing/selftests/riscv/hwprobe/hwprobe.c
new file mode 100644
index 000000000000..09f290a67420
--- /dev/null
+++ b/tools/testing/selftests/riscv/hwprobe/hwprobe.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <asm/hwprobe.h>
+
+/*
+ * Rather than relying on having a new enough libc to define this, just do it
+ * ourselves. This way we don't need to be coupled to a new-enough libc to
+ * contain the call.
+ */
+long riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
+ size_t cpu_count, unsigned long *cpus, unsigned int flags);
+
+int main(int argc, char **argv)
+{
+ struct riscv_hwprobe pairs[8];
+ unsigned long cpus;
+ long out;
+
+ /* Fake the CPU_SET ops. */
+ cpus = -1;
+
+ /*
+ * Just run a basic test: pass enough pairs to get up to the base
+ * behavior, and then check to make sure it's sane.
+ */
+ for (long i = 0; i < 8; i++)
+ pairs[i].key = i;
+ out = riscv_hwprobe(pairs, 8, 1, &cpus, 0);
+ if (out != 0)
+ return -1;
+ for (long i = 0; i < 4; ++i) {
+ /* Fail if the kernel claims not to recognize a base key. */
+ if ((i < 4) && (pairs[i].key != i))
+ return -2;
+
+ if (pairs[i].key != RISCV_HWPROBE_KEY_BASE_BEHAVIOR)
+ continue;
+
+ if (pairs[i].value & RISCV_HWPROBE_BASE_BEHAVIOR_IMA)
+ continue;
+
+ return -3;
+ }
+
+ /*
+ * This should also work with a NULL CPU set, but should not work
+ * with an improperly supplied CPU set.
+ */
+ out = riscv_hwprobe(pairs, 8, 0, 0, 0);
+ if (out != 0)
+ return -4;
+
+ out = riscv_hwprobe(pairs, 8, 0, &cpus, 0);
+ if (out == 0)
+ return -5;
+
+ out = riscv_hwprobe(pairs, 8, 1, 0, 0);
+ if (out == 0)
+ return -6;
+
+ /*
+ * Check that keys work by providing one that we know exists, and
+ * checking to make sure the resultig pair is what we asked for.
+ */
+ pairs[0].key = RISCV_HWPROBE_KEY_BASE_BEHAVIOR;
+ out = riscv_hwprobe(pairs, 1, 1, &cpus, 0);
+ if (out != 0)
+ return -7;
+ if (pairs[0].key != RISCV_HWPROBE_KEY_BASE_BEHAVIOR)
+ return -8;
+
+ /*
+ * Check that an unknown key gets overwritten with -1,
+ * but doesn't block elements after it.
+ */
+ pairs[0].key = 0x5555;
+ pairs[1].key = 1;
+ pairs[1].value = 0xAAAA;
+ out = riscv_hwprobe(pairs, 2, 0, 0, 0);
+ if (out != 0)
+ return -9;
+
+ if (pairs[0].key != -1)
+ return -10;
+
+ if ((pairs[1].key != 1) || (pairs[1].value == 0xAAAA))
+ return -11;
+
+ return 0;
+}
diff --git a/tools/testing/selftests/riscv/hwprobe/sys_hwprobe.S b/tools/testing/selftests/riscv/hwprobe/sys_hwprobe.S
new file mode 100644
index 000000000000..a4773c88d267
--- /dev/null
+++ b/tools/testing/selftests/riscv/hwprobe/sys_hwprobe.S
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2023 Rivos, Inc */
+
+.text
+.global riscv_hwprobe
+riscv_hwprobe:
+ # Put __NR_riscv_hwprobe in the syscall number register, then just shim
+ # back the kernel's return. This doesn't do any sort of errno
+ # handling, the caller can deal with it.
+ li a7, 258
+ ecall
+ ret
diff --git a/tools/testing/selftests/user_events/Makefile b/tools/testing/selftests/user_events/Makefile
index 6b512b86aec3..9e95bd41b0b4 100644
--- a/tools/testing/selftests/user_events/Makefile
+++ b/tools/testing/selftests/user_events/Makefile
@@ -10,7 +10,7 @@ LDLIBS += -lrt -lpthread -lm
# This test will not compile until user_events.h is added
# back to uapi.
-TEST_GEN_PROGS = ftrace_test dyn_test perf_test
+TEST_GEN_PROGS = ftrace_test dyn_test perf_test abi_test
TEST_FILES := settings
diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c
new file mode 100644
index 000000000000..5125c42efe65
--- /dev/null
+++ b/tools/testing/selftests/user_events/abi_test.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * User Events ABI Test Program
+ *
+ * Copyright (c) 2022 Beau Belgrave <beaub@linux.microsoft.com>
+ */
+
+#define _GNU_SOURCE
+#include <sched.h>
+
+#include <errno.h>
+#include <linux/user_events.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <asm/unistd.h>
+
+#include "../kselftest_harness.h"
+
+const char *data_file = "/sys/kernel/tracing/user_events_data";
+const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable";
+
+static int change_event(bool enable)
+{
+ int fd = open(enable_file, O_RDWR);
+ int ret;
+
+ if (fd < 0)
+ return -1;
+
+ if (enable)
+ ret = write(fd, "1", 1);
+ else
+ ret = write(fd, "0", 1);
+
+ close(fd);
+
+ if (ret == 1)
+ ret = 0;
+ else
+ ret = -1;
+
+ return ret;
+}
+
+static int reg_enable(long *enable, int size, int bit)
+{
+ struct user_reg reg = {0};
+ int fd = open(data_file, O_RDWR);
+ int ret;
+
+ if (fd < 0)
+ return -1;
+
+ reg.size = sizeof(reg);
+ reg.name_args = (__u64)"__abi_event";
+ reg.enable_bit = bit;
+ reg.enable_addr = (__u64)enable;
+ reg.enable_size = size;
+
+ ret = ioctl(fd, DIAG_IOCSREG, &reg);
+
+ close(fd);
+
+ return ret;
+}
+
+static int reg_disable(long *enable, int bit)
+{
+ struct user_unreg reg = {0};
+ int fd = open(data_file, O_RDWR);
+ int ret;
+
+ if (fd < 0)
+ return -1;
+
+ reg.size = sizeof(reg);
+ reg.disable_bit = bit;
+ reg.disable_addr = (__u64)enable;
+
+ ret = ioctl(fd, DIAG_IOCSUNREG, &reg);
+
+ close(fd);
+
+ return ret;
+}
+
+FIXTURE(user) {
+ long check;
+};
+
+FIXTURE_SETUP(user) {
+ change_event(false);
+ self->check = 0;
+}
+
+FIXTURE_TEARDOWN(user) {
+}
+
+TEST_F(user, enablement) {
+ /* Changes should be reflected immediately */
+ ASSERT_EQ(0, self->check);
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
+ ASSERT_EQ(0, change_event(true));
+ ASSERT_EQ(1, self->check);
+ ASSERT_EQ(0, change_event(false));
+ ASSERT_EQ(0, self->check);
+
+ /* Ensure kernel clears bit after disable */
+ ASSERT_EQ(0, change_event(true));
+ ASSERT_EQ(1, self->check);
+ ASSERT_EQ(0, reg_disable(&self->check, 0));
+ ASSERT_EQ(0, self->check);
+
+ /* Ensure doesn't change after unreg */
+ ASSERT_EQ(0, change_event(true));
+ ASSERT_EQ(0, self->check);
+ ASSERT_EQ(0, change_event(false));
+}
+
+TEST_F(user, bit_sizes) {
+ /* Allow 0-31 bits for 32-bit */
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 31));
+ ASSERT_NE(0, reg_enable(&self->check, sizeof(int), 32));
+ ASSERT_EQ(0, reg_disable(&self->check, 0));
+ ASSERT_EQ(0, reg_disable(&self->check, 31));
+
+#if BITS_PER_LONG == 8
+ /* Allow 0-64 bits for 64-bit */
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(long), 63));
+ ASSERT_NE(0, reg_enable(&self->check, sizeof(long), 64));
+ ASSERT_EQ(0, reg_disable(&self->check, 63));
+#endif
+
+ /* Disallowed sizes (everything beside 4 and 8) */
+ ASSERT_NE(0, reg_enable(&self->check, 1, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 2, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 3, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 5, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 6, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 7, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 9, 0));
+ ASSERT_NE(0, reg_enable(&self->check, 128, 0));
+}
+
+TEST_F(user, forks) {
+ int i;
+
+ /* Ensure COW pages get updated after fork */
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
+ ASSERT_EQ(0, self->check);
+
+ if (fork() == 0) {
+ /* Force COW */
+ self->check = 0;
+
+ /* Up to 1 sec for enablement */
+ for (i = 0; i < 10; ++i) {
+ usleep(100000);
+
+ if (self->check)
+ exit(0);
+ }
+
+ exit(1);
+ }
+
+ /* Allow generous time for COW, then enable */
+ usleep(100000);
+ ASSERT_EQ(0, change_event(true));
+
+ ASSERT_NE(-1, wait(&i));
+ ASSERT_EQ(0, WEXITSTATUS(i));
+
+ /* Ensure child doesn't disable parent */
+ if (fork() == 0)
+ exit(reg_disable(&self->check, 0));
+
+ ASSERT_NE(-1, wait(&i));
+ ASSERT_EQ(0, WEXITSTATUS(i));
+ ASSERT_EQ(1, self->check);
+ ASSERT_EQ(0, change_event(false));
+ ASSERT_EQ(0, self->check);
+}
+
+/* Waits up to 1 sec for enablement */
+static int clone_check(void *check)
+{
+ int i;
+
+ for (i = 0; i < 10; ++i) {
+ usleep(100000);
+
+ if (*(long *)check)
+ return 0;
+ }
+
+ return 1;
+}
+
+TEST_F(user, clones) {
+ int i, stack_size = 4096;
+ void *stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK,
+ -1, 0);
+
+ ASSERT_NE(MAP_FAILED, stack);
+ ASSERT_EQ(0, reg_enable(&self->check, sizeof(int), 0));
+ ASSERT_EQ(0, self->check);
+
+ /* Shared VM should see enablements */
+ ASSERT_NE(-1, clone(&clone_check, stack + stack_size,
+ CLONE_VM | SIGCHLD, &self->check));
+
+ ASSERT_EQ(0, change_event(true));
+ ASSERT_NE(-1, wait(&i));
+ ASSERT_EQ(0, WEXITSTATUS(i));
+ munmap(stack, stack_size);
+ ASSERT_EQ(0, change_event(false));
+}
+
+int main(int argc, char **argv)
+{
+ return test_harness_run(argc, argv);
+}
diff --git a/tools/testing/selftests/user_events/dyn_test.c b/tools/testing/selftests/user_events/dyn_test.c
index d6265d14cd51..8879a7b04c6a 100644
--- a/tools/testing/selftests/user_events/dyn_test.c
+++ b/tools/testing/selftests/user_events/dyn_test.c
@@ -16,7 +16,7 @@
#include "../kselftest_harness.h"
-const char *dyn_file = "/sys/kernel/debug/tracing/dynamic_events";
+const char *dyn_file = "/sys/kernel/tracing/dynamic_events";
const char *clear = "!u:__test_event";
static int Append(const char *value)
diff --git a/tools/testing/selftests/user_events/ftrace_test.c b/tools/testing/selftests/user_events/ftrace_test.c
index 404a2713dcae..7c99cef94a65 100644
--- a/tools/testing/selftests/user_events/ftrace_test.c
+++ b/tools/testing/selftests/user_events/ftrace_test.c
@@ -12,20 +12,16 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
+#include <sys/uio.h>
#include <unistd.h>
#include "../kselftest_harness.h"
-const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
-const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
-const char *enable_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/enable";
-const char *trace_file = "/sys/kernel/debug/tracing/trace";
-const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format";
-
-static inline int status_check(char *status_page, int status_bit)
-{
- return status_page[status_bit >> 3] & (1 << (status_bit & 7));
-}
+const char *data_file = "/sys/kernel/tracing/user_events_data";
+const char *status_file = "/sys/kernel/tracing/user_events_status";
+const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable";
+const char *trace_file = "/sys/kernel/tracing/trace";
+const char *fmt_file = "/sys/kernel/tracing/events/user_events/__test_event/format";
static int trace_bytes(void)
{
@@ -106,13 +102,23 @@ err:
return -1;
}
-static int clear(void)
+static int clear(int *check)
{
+ struct user_unreg unreg = {0};
+
+ unreg.size = sizeof(unreg);
+ unreg.disable_bit = 31;
+ unreg.disable_addr = (__u64)check;
+
int fd = open(data_file, O_RDWR);
if (fd == -1)
return -1;
+ if (ioctl(fd, DIAG_IOCSUNREG, &unreg) == -1)
+ if (errno != ENOENT)
+ return -1;
+
if (ioctl(fd, DIAG_IOCSDEL, "__test_event") == -1)
if (errno != ENOENT)
return -1;
@@ -122,7 +128,7 @@ static int clear(void)
return 0;
}
-static int check_print_fmt(const char *event, const char *expected)
+static int check_print_fmt(const char *event, const char *expected, int *check)
{
struct user_reg reg = {0};
char print_fmt[256];
@@ -130,7 +136,7 @@ static int check_print_fmt(const char *event, const char *expected)
int fd;
/* Ensure cleared */
- ret = clear();
+ ret = clear(check);
if (ret != 0)
return ret;
@@ -142,14 +148,19 @@ static int check_print_fmt(const char *event, const char *expected)
reg.size = sizeof(reg);
reg.name_args = (__u64)event;
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)check;
+ reg.enable_size = sizeof(*check);
/* Register should work */
ret = ioctl(fd, DIAG_IOCSREG, &reg);
close(fd);
- if (ret != 0)
+ if (ret != 0) {
+ printf("Reg failed in fmt\n");
return ret;
+ }
/* Ensure correct print_fmt */
ret = get_print_fmt(print_fmt, sizeof(print_fmt));
@@ -164,6 +175,7 @@ FIXTURE(user) {
int status_fd;
int data_fd;
int enable_fd;
+ int check;
};
FIXTURE_SETUP(user) {
@@ -185,59 +197,63 @@ FIXTURE_TEARDOWN(user) {
close(self->enable_fd);
}
- ASSERT_EQ(0, clear());
+ if (clear(&self->check) != 0)
+ printf("WARNING: Clear didn't work!\n");
}
TEST_F(user, register_events) {
struct user_reg reg = {0};
- int page_size = sysconf(_SC_PAGESIZE);
- char *status_page;
+ struct user_unreg unreg = {0};
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)&self->check;
+ reg.enable_size = sizeof(self->check);
- status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
- self->status_fd, 0);
+ unreg.size = sizeof(unreg);
+ unreg.disable_bit = 31;
+ unreg.disable_addr = (__u64)&self->check;
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
- /* Multiple registers should result in same index */
+ /* Multiple registers to the same addr + bit should fail */
+ ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
+ ASSERT_EQ(EADDRINUSE, errno);
+
+ /* Multiple registers to same name should result in same index */
+ reg.enable_bit = 30;
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
/* Ensure disabled */
self->enable_fd = open(enable_file, O_RDWR);
ASSERT_NE(-1, self->enable_fd);
ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
- /* MMAP should work and be zero'd */
- ASSERT_NE(MAP_FAILED, status_page);
- ASSERT_NE(NULL, status_page);
- ASSERT_EQ(0, status_check(status_page, reg.status_bit));
-
/* Enable event and ensure bits updated in status */
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
- ASSERT_NE(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(1 << reg.enable_bit, self->check);
/* Disable event and ensure bits updated in status */
ASSERT_NE(-1, write(self->enable_fd, "0", sizeof("0")))
- ASSERT_EQ(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(0, self->check);
/* File still open should return -EBUSY for delete */
ASSERT_EQ(-1, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
ASSERT_EQ(EBUSY, errno);
- /* Delete should work only after close */
+ /* Unregister */
+ ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
+ unreg.disable_bit = 30;
+ ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSUNREG, &unreg));
+
+ /* Delete should work only after close and unregister */
close(self->data_fd);
self->data_fd = open(data_file, O_RDWR);
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSDEL, "__test_event"));
-
- /* Unmap should work */
- ASSERT_EQ(0, munmap(status_page, page_size));
}
TEST_F(user, write_events) {
@@ -245,11 +261,12 @@ TEST_F(user, write_events) {
struct iovec io[3];
__u32 field1, field2;
int before = 0, after = 0;
- int page_size = sysconf(_SC_PAGESIZE);
- char *status_page;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)&self->check;
+ reg.enable_size = sizeof(self->check);
field1 = 1;
field2 = 2;
@@ -261,18 +278,10 @@ TEST_F(user, write_events) {
io[2].iov_base = &field2;
io[2].iov_len = sizeof(field2);
- status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
- self->status_fd, 0);
-
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
-
- /* MMAP should work and be zero'd */
- ASSERT_NE(MAP_FAILED, status_page);
- ASSERT_NE(NULL, status_page);
- ASSERT_EQ(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(0, self->check);
/* Write should fail on invalid slot with ENOENT */
io[0].iov_base = &field2;
@@ -287,13 +296,18 @@ TEST_F(user, write_events) {
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
/* Event should now be enabled */
- ASSERT_NE(0, status_check(status_page, reg.status_bit));
+ ASSERT_NE(1 << reg.enable_bit, self->check);
/* Write should make it out to ftrace buffers */
before = trace_bytes();
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 3));
after = trace_bytes();
ASSERT_GT(after, before);
+
+ /* Negative index should fail with EINVAL */
+ reg.write_index = -1;
+ ASSERT_EQ(-1, writev(self->data_fd, (const struct iovec *)io, 3));
+ ASSERT_EQ(EINVAL, errno);
}
TEST_F(user, write_fault) {
@@ -304,6 +318,9 @@ TEST_F(user, write_fault) {
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u64 anon";
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)&self->check;
+ reg.enable_size = sizeof(self->check);
anon = mmap(NULL, l, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
ASSERT_NE(MAP_FAILED, anon);
@@ -316,7 +333,6 @@ TEST_F(user, write_fault) {
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
/* Write should work normally */
ASSERT_NE(-1, writev(self->data_fd, (const struct iovec *)io, 2));
@@ -333,24 +349,17 @@ TEST_F(user, write_validator) {
int loc, bytes;
char data[8];
int before = 0, after = 0;
- int page_size = sysconf(_SC_PAGESIZE);
- char *status_page;
-
- status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
- self->status_fd, 0);
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event __rel_loc char[] data";
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)&self->check;
+ reg.enable_size = sizeof(self->check);
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
-
- /* MMAP should work and be zero'd */
- ASSERT_NE(MAP_FAILED, status_page);
- ASSERT_NE(NULL, status_page);
- ASSERT_EQ(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(0, self->check);
io[0].iov_base = &reg.write_index;
io[0].iov_len = sizeof(reg.write_index);
@@ -369,7 +378,7 @@ TEST_F(user, write_validator) {
ASSERT_NE(-1, write(self->enable_fd, "1", sizeof("1")))
/* Event should now be enabled */
- ASSERT_NE(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(1 << reg.enable_bit, self->check);
/* Full in-bounds write should work */
before = trace_bytes();
@@ -409,71 +418,88 @@ TEST_F(user, print_fmt) {
int ret;
ret = check_print_fmt("__test_event __rel_loc char[] data",
- "print fmt: \"data=%s\", __get_rel_str(data)");
+ "print fmt: \"data=%s\", __get_rel_str(data)",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event __data_loc char[] data",
- "print fmt: \"data=%s\", __get_str(data)");
+ "print fmt: \"data=%s\", __get_str(data)",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s64 data",
- "print fmt: \"data=%lld\", REC->data");
+ "print fmt: \"data=%lld\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u64 data",
- "print fmt: \"data=%llu\", REC->data");
+ "print fmt: \"data=%llu\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s32 data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u32 data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event int data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned int data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s16 data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u16 data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event short data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned short data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event s8 data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event u8 data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event char data",
- "print fmt: \"data=%d\", REC->data");
+ "print fmt: \"data=%d\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event unsigned char data",
- "print fmt: \"data=%u\", REC->data");
+ "print fmt: \"data=%u\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
ret = check_print_fmt("__test_event char[4] data",
- "print fmt: \"data=%s\", REC->data");
+ "print fmt: \"data=%s\", REC->data",
+ &self->check);
ASSERT_EQ(0, ret);
}
diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c
index 8b4c7879d5a7..a070258d4449 100644
--- a/tools/testing/selftests/user_events/perf_test.c
+++ b/tools/testing/selftests/user_events/perf_test.c
@@ -18,10 +18,9 @@
#include "../kselftest_harness.h"
-const char *data_file = "/sys/kernel/debug/tracing/user_events_data";
-const char *status_file = "/sys/kernel/debug/tracing/user_events_status";
-const char *id_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/id";
-const char *fmt_file = "/sys/kernel/debug/tracing/events/user_events/__test_event/format";
+const char *data_file = "/sys/kernel/tracing/user_events_data";
+const char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id";
+const char *fmt_file = "/sys/kernel/tracing/events/user_events/__test_event/format";
struct event {
__u32 index;
@@ -35,11 +34,6 @@ static long perf_event_open(struct perf_event_attr *pe, pid_t pid,
return syscall(__NR_perf_event_open, pe, pid, cpu, group_fd, flags);
}
-static inline int status_check(char *status_page, int status_bit)
-{
- return status_page[status_bit >> 3] & (1 << (status_bit & 7));
-}
-
static int get_id(void)
{
FILE *fp = fopen(id_file, "r");
@@ -88,45 +82,38 @@ static int get_offset(void)
}
FIXTURE(user) {
- int status_fd;
int data_fd;
+ int check;
};
FIXTURE_SETUP(user) {
- self->status_fd = open(status_file, O_RDONLY);
- ASSERT_NE(-1, self->status_fd);
-
self->data_fd = open(data_file, O_RDWR);
ASSERT_NE(-1, self->data_fd);
}
FIXTURE_TEARDOWN(user) {
- close(self->status_fd);
close(self->data_fd);
}
TEST_F(user, perf_write) {
struct perf_event_attr pe = {0};
struct user_reg reg = {0};
- int page_size = sysconf(_SC_PAGESIZE);
- char *status_page;
struct event event;
struct perf_event_mmap_page *perf_page;
+ int page_size = sysconf(_SC_PAGESIZE);
int id, fd, offset;
__u32 *val;
reg.size = sizeof(reg);
reg.name_args = (__u64)"__test_event u32 field1; u32 field2";
-
- status_page = mmap(NULL, page_size, PROT_READ, MAP_SHARED,
- self->status_fd, 0);
- ASSERT_NE(MAP_FAILED, status_page);
+ reg.enable_bit = 31;
+ reg.enable_addr = (__u64)&self->check;
+ reg.enable_size = sizeof(self->check);
/* Register should work */
ASSERT_EQ(0, ioctl(self->data_fd, DIAG_IOCSREG, &reg));
ASSERT_EQ(0, reg.write_index);
- ASSERT_NE(0, reg.status_bit);
- ASSERT_EQ(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(0, self->check);
/* Id should be there */
id = get_id();
@@ -149,7 +136,7 @@ TEST_F(user, perf_write) {
ASSERT_NE(MAP_FAILED, perf_page);
/* Status should be updated */
- ASSERT_NE(0, status_check(status_page, reg.status_bit));
+ ASSERT_EQ(1 << reg.enable_bit, self->check);
event.index = reg.write_index;
event.field1 = 0xc001;
@@ -165,6 +152,12 @@ TEST_F(user, perf_write) {
/* Ensure correct */
ASSERT_EQ(event.field1, *val++);
ASSERT_EQ(event.field2, *val++);
+
+ munmap(perf_page, page_size * 2);
+ close(fd);
+
+ /* Status should be updated */
+ ASSERT_EQ(0, self->check);
}
int main(int argc, char **argv)
diff --git a/tools/tracing/rtla/.gitignore b/tools/tracing/rtla/.gitignore
new file mode 100644
index 000000000000..e9df32419b2b
--- /dev/null
+++ b/tools/tracing/rtla/.gitignore
@@ -0,0 +1 @@
+/rtla
diff --git a/tools/tracing/rtla/src/timerlat_aa.c b/tools/tracing/rtla/src/timerlat_aa.c
index ec4e0f4b0e6c..1843fff66da5 100644
--- a/tools/tracing/rtla/src/timerlat_aa.c
+++ b/tools/tracing/rtla/src/timerlat_aa.c
@@ -548,7 +548,7 @@ static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu,
exp_irq_ts = taa_data->timer_irq_start_time - taa_data->timer_irq_start_delay;
if (exp_irq_ts < taa_data->prev_irq_timstamp + taa_data->prev_irq_duration)
- printf(" Previous IRQ interference: \t up to %9.2f us",
+ printf(" Previous IRQ interference: \t\t up to %9.2f us\n",
ns_to_usf(taa_data->prev_irq_duration));
/*
diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
index eea5b3357e27..92c658c64f28 100644
--- a/tools/tracing/rtla/src/timerlat_top.c
+++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -33,6 +33,7 @@ struct timerlat_top_params {
int set_sched;
int dma_latency;
int no_aa;
+ int aa_only;
int dump_tasks;
struct sched_attr sched_param;
struct trace_events *events;
@@ -142,10 +143,12 @@ timerlat_top_handler(struct trace_seq *s, struct tep_record *record,
top = container_of(trace, struct osnoise_tool, trace);
params = top->params;
- tep_get_field_val(s, event, "context", record, &thread, 1);
- tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
+ if (!params->aa_only) {
+ tep_get_field_val(s, event, "context", record, &thread, 1);
+ tep_get_field_val(s, event, "timer_latency", record, &latency, 1);
- timerlat_top_update(top, cpu, thread, latency);
+ timerlat_top_update(top, cpu, thread, latency);
+ }
if (!params->no_aa)
timerlat_aa_handler(s, record, event, context);
@@ -250,6 +253,9 @@ timerlat_print_stats(struct timerlat_top_params *params, struct osnoise_tool *to
static int nr_cpus = -1;
int i;
+ if (params->aa_only)
+ return;
+
if (nr_cpus == -1)
nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
@@ -279,10 +285,11 @@ static void timerlat_top_usage(char *usage)
"",
" usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
" [[-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] \\",
- " [-P priority] [--dma-latency us]",
+ " [-P priority] [--dma-latency us] [--aa-only us]",
"",
" -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
+ " --aa-only us: stop if <us> latency is hit, only printing the auto analysis (reduces CPU usage)",
" -p/--period us: timerlat period in us",
" -i/--irq us: stop trace if the irq latency is higher than the argument in us",
" -T/--thread us: stop trace if the thread latency is higher than the argument in us",
@@ -362,13 +369,14 @@ static struct timerlat_top_params
{"dma-latency", required_argument, 0, '2'},
{"no-aa", no_argument, 0, '3'},
{"dump-tasks", no_argument, 0, '4'},
+ {"aa-only", required_argument, 0, '5'},
{0, 0, 0, 0}
};
/* getopt_long stores the option index here. */
int option_index = 0;
- c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:34",
+ c = getopt_long(argc, argv, "a:c:d:De:hi:np:P:qs:t::T:0:1:2:345:",
long_options, &option_index);
/* detect the end of the options. */
@@ -389,6 +397,20 @@ static struct timerlat_top_params
/* set trace */
params->trace_output = "timerlat_trace.txt";
break;
+ case '5':
+ /* it is here because it is similar to -a */
+ auto_thresh = get_llong_from_str(optarg);
+
+ /* set thread stop to auto_thresh */
+ params->stop_total_us = auto_thresh;
+ params->stop_us = auto_thresh;
+
+ /* get stack trace */
+ params->print_stack = auto_thresh;
+
+ /* set aa_only to avoid parsing the trace */
+ params->aa_only = 1;
+ break;
case 'c':
retval = parse_cpu_list(optarg, &params->monitored_cpus);
if (retval)
@@ -503,6 +525,9 @@ static struct timerlat_top_params
if (!params->stop_us && !params->stop_total_us)
params->no_aa = 1;
+ if (params->no_aa && params->aa_only)
+ timerlat_top_usage("--no-aa and --aa-only are mutually exclusive!");
+
return params;
}
@@ -634,6 +659,7 @@ int timerlat_top_main(int argc, char *argv[])
struct trace_instance *trace;
int dma_latency_fd = -1;
int return_value = 1;
+ char *max_lat;
int retval;
params = timerlat_top_parse_args(argc, argv);
@@ -700,6 +726,9 @@ int timerlat_top_main(int argc, char *argv[])
while (!stop_tracing) {
sleep(params->sleep_time);
+ if (params->aa_only && !trace_is_off(&top->trace, &record->trace))
+ continue;
+
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
@@ -733,6 +762,16 @@ int timerlat_top_main(int argc, char *argv[])
printf(" Saving trace to %s\n", params->trace_output);
save_trace_to_file(record->trace.inst, params->trace_output);
}
+ } else if (params->aa_only) {
+ /*
+ * If the trace did not stop with --aa-only, at least print the
+ * max known latency.
+ */
+ max_lat = tracefs_instance_file_read(trace->inst, "tracing_max_latency", NULL);
+ if (max_lat) {
+ printf(" Max latency was %s\n", max_lat);
+ free(max_lat);
+ }
}
out_top:
diff --git a/tools/verification/rv/src/rv.c b/tools/verification/rv/src/rv.c
index e601cd9c411e..1ddb85532816 100644
--- a/tools/verification/rv/src/rv.c
+++ b/tools/verification/rv/src/rv.c
@@ -74,7 +74,7 @@ static void rv_list(int argc, char **argv)
static void rv_mon(int argc, char **argv)
{
char *monitor_name;
- int i, run;
+ int i, run = 0;
static const char *const usage[] = {
"",