diff options
45 files changed, 722 insertions, 256 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index b86de61b8caa..d0d42c5c4141 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -153,12 +153,15 @@ config S390  	select HAVE_DEBUG_KMEMLEAK  	select HAVE_DMA_CONTIGUOUS  	select HAVE_DYNAMIC_FTRACE +	select HAVE_DYNAMIC_FTRACE_WITH_ARGS +	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS  	select HAVE_DYNAMIC_FTRACE_WITH_REGS  	select HAVE_EBPF_JIT if PACK_STACK && HAVE_MARCH_Z196_FEATURES  	select HAVE_EFFICIENT_UNALIGNED_ACCESS  	select HAVE_FAST_GUP  	select HAVE_FENTRY  	select HAVE_FTRACE_MCOUNT_RECORD +	select HAVE_FUNCTION_ARG_ACCESS_API  	select HAVE_FUNCTION_ERROR_INJECTION  	select HAVE_FUNCTION_GRAPH_TRACER  	select HAVE_FUNCTION_TRACER @@ -190,6 +193,7 @@ config S390  	select HAVE_REGS_AND_STACK_ACCESS_API  	select HAVE_RELIABLE_STACKTRACE  	select HAVE_RSEQ +	select HAVE_SAMPLE_FTRACE_DIRECT  	select HAVE_SOFTIRQ_ON_OWN_STACK  	select HAVE_SYSCALL_TRACEPOINTS  	select HAVE_VIRT_CPU_ACCOUNTING @@ -938,6 +942,8 @@ menu "Selftests"  config S390_UNWIND_SELFTEST  	def_tristate n +	depends on KUNIT +	default KUNIT_ALL_TESTS  	prompt "Test unwind functions"  	help  	  This option enables s390 specific stack unwinder testing kernel @@ -946,4 +952,16 @@ config S390_UNWIND_SELFTEST  	  Say N if you are unsure. +config S390_KPROBES_SANITY_TEST +	def_tristate n +	prompt "Enable s390 specific kprobes tests" +	depends on KPROBES +	depends on KUNIT +	help +	  This option enables an s390 specific kprobes test module. This option +	  is not useful for distributions or general kernels, but only for kernel +	  developers working on architecture code. + +	  Say N if you are unsure. +  endmenu diff --git a/arch/s390/boot/compressed/decompressor.h b/arch/s390/boot/compressed/decompressor.h index a59f75c5b049..f75cc31a77dd 100644 --- a/arch/s390/boot/compressed/decompressor.h +++ b/arch/s390/boot/compressed/decompressor.h @@ -24,6 +24,7 @@ struct vmlinux_info {  	unsigned long dynsym_start;  	unsigned long rela_dyn_start;  	unsigned long rela_dyn_end; +	unsigned long amode31_size;  };  /* Symbols defined by linker scripts */ diff --git a/arch/s390/boot/head.S b/arch/s390/boot/head.S index 40f4cff538b8..7e843d8e794e 100644 --- a/arch/s390/boot/head.S +++ b/arch/s390/boot/head.S @@ -317,6 +317,7 @@ SYM_CODE_START_LOCAL(startup_normal)  	xc	0x300(256),0x300  	xc	0xe00(256),0xe00  	xc	0xf00(256),0xf00 +	lctlg	%c0,%c15,.Lctl-.LPG0(%r13)	# load control registers  	stcke	__LC_BOOT_CLOCK  	mvc	__LC_LAST_UPDATE_CLOCK(8),__LC_BOOT_CLOCK+1  	spt	6f-.LPG0(%r13) @@ -335,6 +336,22 @@ SYM_CODE_END(startup_normal)  	.quad	0x0000000180000000,startup_pgm_check_handler  .Lio_new_psw:  	.quad	0x0002000180000000,0x1f0	# disabled wait +.Lctl:	.quad	0x04040000		# cr0: AFP registers & secondary space +	.quad	0			# cr1: primary space segment table +	.quad	0			# cr2: dispatchable unit control table +	.quad	0			# cr3: instruction authorization +	.quad	0xffff			# cr4: instruction authorization +	.quad	0			# cr5: primary-aste origin +	.quad	0			# cr6:	I/O interrupts +	.quad	0			# cr7:	secondary space segment table +	.quad	0x0000000000008000	# cr8:	access registers translation +	.quad	0			# cr9:	tracing off +	.quad	0			# cr10: tracing off +	.quad	0			# cr11: tracing off +	.quad	0			# cr12: tracing off +	.quad	0			# cr13: home space segment table +	.quad	0xc0000000		# cr14: machine check handling off +	.quad	0			# cr15: linkage stack operations  #include "head_kdump.S" diff --git a/arch/s390/boot/startup.c b/arch/s390/boot/startup.c index 6dc8d0a53864..7571dee72a0c 100644 --- a/arch/s390/boot/startup.c +++ b/arch/s390/boot/startup.c @@ -15,6 +15,7 @@  #include "uv.h"  unsigned long __bootdata_preserved(__kaslr_offset); +unsigned long __bootdata(__amode31_base);  unsigned long __bootdata_preserved(VMALLOC_START);  unsigned long __bootdata_preserved(VMALLOC_END);  struct page *__bootdata_preserved(vmemmap); @@ -259,6 +260,12 @@ static void offset_vmlinux_info(unsigned long offset)  	vmlinux.dynsym_start += offset;  } +static unsigned long reserve_amode31(unsigned long safe_addr) +{ +	__amode31_base = PAGE_ALIGN(safe_addr); +	return safe_addr + vmlinux.amode31_size; +} +  void startup_kernel(void)  {  	unsigned long random_lma; @@ -273,6 +280,7 @@ void startup_kernel(void)  	setup_lpp();  	store_ipl_parmblock();  	safe_addr = mem_safe_offset(); +	safe_addr = reserve_amode31(safe_addr);  	safe_addr = read_ipl_report(safe_addr);  	uv_query_info();  	rescue_initrd(safe_addr); diff --git a/arch/s390/configs/debug_defconfig b/arch/s390/configs/debug_defconfig index 6aad18ee131d..fd825097cf04 100644 --- a/arch/s390/configs/debug_defconfig +++ b/arch/s390/configs/debug_defconfig @@ -61,7 +61,8 @@ CONFIG_PROTECTED_VIRTUALIZATION_GUEST=y  CONFIG_CMM=m  CONFIG_APPLDATA_BASE=y  CONFIG_KVM=m -CONFIG_S390_UNWIND_SELFTEST=y +CONFIG_S390_UNWIND_SELFTEST=m +CONFIG_S390_KPROBES_SANITY_TEST=m  CONFIG_KPROBES=y  CONFIG_JUMP_LABEL=y  CONFIG_STATIC_KEYS_SELFTEST=y @@ -776,7 +777,6 @@ CONFIG_CRC8=m  CONFIG_RANDOM32_SELFTEST=y  CONFIG_DMA_CMA=y  CONFIG_CMA_SIZE_MBYTES=0 -CONFIG_DMA_API_DEBUG=y  CONFIG_PRINTK_TIME=y  CONFIG_DYNAMIC_DEBUG=y  CONFIG_DEBUG_INFO=y @@ -839,8 +839,13 @@ CONFIG_BPF_KPROBE_OVERRIDE=y  CONFIG_HIST_TRIGGERS=y  CONFIG_FTRACE_STARTUP_TEST=y  # CONFIG_EVENT_TRACE_STARTUP_TEST is not set +CONFIG_SAMPLES=y +CONFIG_SAMPLE_TRACE_PRINTK=m +CONFIG_SAMPLE_FTRACE_DIRECT=m  CONFIG_DEBUG_ENTRY=y  CONFIG_CIO_INJECT=y +CONFIG_KUNIT=m +CONFIG_KUNIT_DEBUGFS=y  CONFIG_NOTIFIER_ERROR_INJECTION=m  CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m  CONFIG_FAULT_INJECTION=y diff --git a/arch/s390/configs/defconfig b/arch/s390/configs/defconfig index f08b161c9446..c9c3cedff2d8 100644 --- a/arch/s390/configs/defconfig +++ b/arch/s390/configs/defconfig @@ -60,6 +60,7 @@ CONFIG_CMM=m  CONFIG_APPLDATA_BASE=y  CONFIG_KVM=m  CONFIG_S390_UNWIND_SELFTEST=m +CONFIG_S390_KPROBES_SANITY_TEST=m  CONFIG_KPROBES=y  CONFIG_JUMP_LABEL=y  # CONFIG_GCC_PLUGINS is not set @@ -788,6 +789,11 @@ CONFIG_FTRACE_SYSCALLS=y  CONFIG_BLK_DEV_IO_TRACE=y  CONFIG_BPF_KPROBE_OVERRIDE=y  CONFIG_HIST_TRIGGERS=y +CONFIG_SAMPLES=y +CONFIG_SAMPLE_TRACE_PRINTK=m +CONFIG_SAMPLE_FTRACE_DIRECT=m +CONFIG_KUNIT=m +CONFIG_KUNIT_DEBUGFS=y  CONFIG_LKDTM=m  CONFIG_PERCPU_TEST=m  CONFIG_ATOMIC64_SELFTEST=y diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h index f9eddbca79d2..2c057e1f3200 100644 --- a/arch/s390/include/asm/barrier.h +++ b/arch/s390/include/asm/barrier.h @@ -16,20 +16,24 @@  #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES  /* Fast-BCR without checkpoint synchronization */ -#define __ASM_BARRIER "bcr 14,0\n" +#define __ASM_BCR_SERIALIZE "bcr 14,0\n"  #else -#define __ASM_BARRIER "bcr 15,0\n" +#define __ASM_BCR_SERIALIZE "bcr 15,0\n"  #endif -#define mb() do {  asm volatile(__ASM_BARRIER : : : "memory"); } while (0) +static __always_inline void bcr_serialize(void) +{ +	asm volatile(__ASM_BCR_SERIALIZE : : : "memory"); +} -#define rmb()				barrier() -#define wmb()				barrier() -#define dma_rmb()			mb() -#define dma_wmb()			mb() -#define __smp_mb()			mb() -#define __smp_rmb()			rmb() -#define __smp_wmb()			wmb() +#define mb()		bcr_serialize() +#define rmb()		barrier() +#define wmb()		barrier() +#define dma_rmb()	mb() +#define dma_wmb()	mb() +#define __smp_mb()	mb() +#define __smp_rmb()	rmb() +#define __smp_wmb()	wmb()  #define __smp_store_release(p, v)					\  do {									\ diff --git a/arch/s390/include/asm/debug.h b/arch/s390/include/asm/debug.h index 19a55e1e3a0c..77f24262c25c 100644 --- a/arch/s390/include/asm/debug.h +++ b/arch/s390/include/asm/debug.h @@ -462,7 +462,7 @@ arch_initcall(VNAME(var, reg))   *   * @var: Name of debug_info_t variable   * @name: Name of debug log (e.g. used for debugfs entry) - * @pages_per_area: Number of pages per area + * @pages: Number of pages per area   * @nr_areas: Number of debug areas   * @buf_size: Size of data area in each debug entry   * @view: Pointer to debug view struct diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h index e8b460f39c58..267f70f4393f 100644 --- a/arch/s390/include/asm/ftrace.h +++ b/arch/s390/include/asm/ftrace.h @@ -17,7 +17,6 @@  void ftrace_caller(void); -extern char ftrace_graph_caller_end;  extern void *ftrace_func;  struct dyn_arch_ftrace { }; @@ -42,6 +41,35 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr)  	return addr;  } +struct ftrace_regs { +	struct pt_regs regs; +}; + +static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs) +{ +	return &fregs->regs; +} + +static __always_inline void ftrace_instruction_pointer_set(struct ftrace_regs *fregs, +							   unsigned long ip) +{ +	struct pt_regs *regs = arch_ftrace_get_regs(fregs); + +	regs->psw.addr = ip; +} + +/* + * When an ftrace registered caller is tracing a function that is + * also set by a register_ftrace_direct() call, it needs to be + * differentiated in the ftrace_caller trampoline. To do this, + * place the direct caller in the ORIG_GPR2 part of pt_regs. This + * tells the ftrace_caller that there's a direct caller. + */ +static inline void arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr) +{ +	regs->orig_gpr2 = addr; +} +  /*   * Even though the system call numbers are identical for s390/s390x a   * different system call table is used for compat tasks. This may lead @@ -68,4 +96,32 @@ static inline bool arch_syscall_match_sym_name(const char *sym,  }  #endif /* __ASSEMBLY__ */ + +#ifdef CONFIG_FUNCTION_TRACER + +#define FTRACE_NOP_INSN .word 0xc004, 0x0000, 0x0000 /* brcl 0,0 */ + +#ifndef CC_USING_HOTPATCH + +#define FTRACE_GEN_MCOUNT_RECORD(name)		\ +	.section __mcount_loc, "a", @progbits;	\ +	.quad name;				\ +	.previous; + +#else /* !CC_USING_HOTPATCH */ + +#define FTRACE_GEN_MCOUNT_RECORD(name) + +#endif /* !CC_USING_HOTPATCH */ + +#define FTRACE_GEN_NOP_ASM(name)		\ +	FTRACE_GEN_MCOUNT_RECORD(name)		\ +	FTRACE_NOP_INSN + +#else /* CONFIG_FUNCTION_TRACER */ + +#define FTRACE_GEN_NOP_ASM(name) + +#endif /* CONFIG_FUNCTION_TRACER */ +  #endif /* _ASM_S390_FTRACE_H */ diff --git a/arch/s390/include/asm/jump_label.h b/arch/s390/include/asm/jump_label.h index dcb1bba4f406..916cfcb36d8a 100644 --- a/arch/s390/include/asm/jump_label.h +++ b/arch/s390/include/asm/jump_label.h @@ -2,6 +2,8 @@  #ifndef _ASM_S390_JUMP_LABEL_H  #define _ASM_S390_JUMP_LABEL_H +#define HAVE_JUMP_LABEL_BATCH +  #ifndef __ASSEMBLY__  #include <linux/types.h> diff --git a/arch/s390/include/asm/livepatch.h b/arch/s390/include/asm/livepatch.h index d578a8c76676..5209f223331a 100644 --- a/arch/s390/include/asm/livepatch.h +++ b/arch/s390/include/asm/livepatch.h @@ -16,9 +16,7 @@  static inline void klp_arch_set_pc(struct ftrace_regs *fregs, unsigned long ip)  { -	struct pt_regs *regs = ftrace_get_regs(fregs); - -	regs->psw.addr = ip; +	ftrace_instruction_pointer_set(fregs, ip);  }  #endif diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 61b22aa990e7..82fc11907451 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h @@ -76,8 +76,7 @@ enum {   * The pt_regs struct defines the way the registers are stored on   * the stack during a system call.   */ -struct pt_regs  -{ +struct pt_regs {  	union {  		user_pt_regs user_regs;  		struct { @@ -197,6 +196,25 @@ const char *regs_query_register_name(unsigned int offset);  unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset);  unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n); +/** + * regs_get_kernel_argument() - get Nth function argument in kernel + * @regs:	pt_regs of that context + * @n:		function argument number (start from 0) + * + * regs_get_kernel_argument() returns @n th argument of the function call. + */ +static inline unsigned long regs_get_kernel_argument(struct pt_regs *regs, +						     unsigned int n) +{ +	unsigned int argoffset = STACK_FRAME_OVERHEAD / sizeof(long); + +#define NR_REG_ARGUMENTS 5 +	if (n < NR_REG_ARGUMENTS) +		return regs_get_register(regs, 2 + n); +	n -= NR_REG_ARGUMENTS; +	return regs_get_kernel_stack_nth(regs, argoffset + n); +} +  static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)  {  	return regs->gprs[15]; diff --git a/arch/s390/include/asm/text-patching.h b/arch/s390/include/asm/text-patching.h new file mode 100644 index 000000000000..b219056a8817 --- /dev/null +++ b/arch/s390/include/asm/text-patching.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _ASM_S390_TEXT_PATCHING_H +#define _ASM_S390_TEXT_PATCHING_H + +#include <asm/barrier.h> + +static __always_inline void sync_core(void) +{ +	bcr_serialize(); +} + +void text_poke_sync(void); +void text_poke_sync_lock(void); + +#endif /* _ASM_S390_TEXT_PATCHING_H */ diff --git a/arch/s390/kernel/alternative.c b/arch/s390/kernel/alternative.c index c22ea1c3ef84..cce0ddee2d02 100644 --- a/arch/s390/kernel/alternative.c +++ b/arch/s390/kernel/alternative.c @@ -1,5 +1,8 @@  // SPDX-License-Identifier: GPL-2.0  #include <linux/module.h> +#include <linux/cpu.h> +#include <linux/smp.h> +#include <asm/text-patching.h>  #include <asm/alternative.h>  #include <asm/facility.h>  #include <asm/nospec-branch.h> @@ -110,3 +113,20 @@ void __init apply_alternative_instructions(void)  {  	apply_alternatives(__alt_instructions, __alt_instructions_end);  } + +static void do_sync_core(void *info) +{ +	sync_core(); +} + +void text_poke_sync(void) +{ +	on_each_cpu(do_sync_core, NULL, 1); +} + +void text_poke_sync_lock(void) +{ +	cpus_read_lock(); +	text_poke_sync(); +	cpus_read_unlock(); +} diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index b57da9338588..b6ee3fd7fe0c 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -45,6 +45,7 @@ int main(void)  	OFFSET(__SF_SIE_SAVEAREA, stack_frame, empty1[2]);  	OFFSET(__SF_SIE_REASON, stack_frame, empty1[3]);  	OFFSET(__SF_SIE_FLAGS, stack_frame, empty1[4]); +	DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame));  	BLANK();  	/* idle data offsets */  	OFFSET(__CLOCK_IDLE_ENTER, s390_idle_data, clock_idle_enter); diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 7f2696e8d511..6083090be1f4 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -70,5 +70,6 @@ extern struct exception_table_entry _stop_amode31_ex_table[];  #define __amode31_data __section(".amode31.data")  #define __amode31_ref __section(".amode31.refs")  extern long _start_amode31_refs[], _end_amode31_refs[]; +extern unsigned long __amode31_base;  #endif /* _ENTRY_H */ diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 1d94ffdf347b..e416aaaaf524 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c @@ -17,6 +17,7 @@  #include <linux/kprobes.h>  #include <trace/syscall.h>  #include <asm/asm-offsets.h> +#include <asm/text-patching.h>  #include <asm/cacheflush.h>  #include <asm/ftrace.lds.h>  #include <asm/nospec-branch.h> @@ -80,17 +81,6 @@ asm(  #ifdef CONFIG_MODULES  static char *ftrace_plt; - -asm( -	"	.data\n" -	"ftrace_plt_template:\n" -	"	basr	%r1,%r0\n" -	"	lg	%r1,0f-.(%r1)\n" -	"	br	%r1\n" -	"0:	.quad	ftrace_caller\n" -	"ftrace_plt_template_end:\n" -	"	.previous\n" -);  #endif /* CONFIG_MODULES */  static const char *ftrace_shared_hotpatch_trampoline(const char **end) @@ -116,7 +106,7 @@ static const char *ftrace_shared_hotpatch_trampoline(const char **end)  bool ftrace_need_init_nop(void)  { -	return ftrace_shared_hotpatch_trampoline(NULL); +	return true;  }  int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec) @@ -175,28 +165,6 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,  	return 0;  } -static void ftrace_generate_nop_insn(struct ftrace_insn *insn) -{ -	/* brcl 0,0 */ -	insn->opc = 0xc004; -	insn->disp = 0; -} - -static void ftrace_generate_call_insn(struct ftrace_insn *insn, -				      unsigned long ip) -{ -	unsigned long target; - -	/* brasl r0,ftrace_caller */ -	target = FTRACE_ADDR; -#ifdef CONFIG_MODULES -	if (is_module_addr((void *)ip)) -		target = (unsigned long)ftrace_plt; -#endif /* CONFIG_MODULES */ -	insn->opc = 0xc005; -	insn->disp = (target - ip) / 2; -} -  static void brcl_disable(void *brcl)  {  	u8 op = 0x04; /* set mask field to zero */ @@ -207,23 +175,7 @@ static void brcl_disable(void *brcl)  int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,  		    unsigned long addr)  { -	struct ftrace_insn orig, new, old; - -	if (ftrace_shared_hotpatch_trampoline(NULL)) { -		brcl_disable((void *)rec->ip); -		return 0; -	} - -	if (copy_from_kernel_nofault(&old, (void *) rec->ip, sizeof(old))) -		return -EFAULT; -	/* Replace ftrace call with a nop. */ -	ftrace_generate_call_insn(&orig, rec->ip); -	ftrace_generate_nop_insn(&new); - -	/* Verify that the to be replaced code matches what we expect. */ -	if (memcmp(&orig, &old, sizeof(old))) -		return -EINVAL; -	s390_kernel_write((void *) rec->ip, &new, sizeof(new)); +	brcl_disable((void *)rec->ip);  	return 0;  } @@ -236,23 +188,7 @@ static void brcl_enable(void *brcl)  int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)  { -	struct ftrace_insn orig, new, old; - -	if (ftrace_shared_hotpatch_trampoline(NULL)) { -		brcl_enable((void *)rec->ip); -		return 0; -	} - -	if (copy_from_kernel_nofault(&old, (void *) rec->ip, sizeof(old))) -		return -EFAULT; -	/* Replace nop with an ftrace call. */ -	ftrace_generate_nop_insn(&orig); -	ftrace_generate_call_insn(&new, rec->ip); - -	/* Verify that the to be replaced code matches what we expect. */ -	if (memcmp(&orig, &old, sizeof(old))) -		return -EINVAL; -	s390_kernel_write((void *) rec->ip, &new, sizeof(new)); +	brcl_enable((void *)rec->ip);  	return 0;  } @@ -269,22 +205,16 @@ int __init ftrace_dyn_arch_init(void)  void arch_ftrace_update_code(int command)  { -	if (ftrace_shared_hotpatch_trampoline(NULL)) -		ftrace_modify_all_code(command); -	else -		ftrace_run_stop_machine(command); -} - -static void __ftrace_sync(void *dummy) -{ +	ftrace_modify_all_code(command);  }  int ftrace_arch_code_modify_post_process(void)  { -	if (ftrace_shared_hotpatch_trampoline(NULL)) { -		/* Send SIGP to the other CPUs, so they see the new code. */ -		smp_call_function(__ftrace_sync, NULL, 1); -	} +	/* +	 * Flush any pre-fetched instructions on all +	 * CPUs to make the new code visible. +	 */ +	text_poke_sync_lock();  	return 0;  } @@ -299,10 +229,6 @@ static int __init ftrace_plt_init(void)  		panic("cannot allocate ftrace plt\n");  	start = ftrace_shared_hotpatch_trampoline(&end); -	if (!start) { -		start = ftrace_plt_template; -		end = ftrace_plt_template_end; -	}  	memcpy(ftrace_plt, start, end - start);  	set_memory_ro((unsigned long)ftrace_plt, 1);  	return 0; @@ -342,12 +268,14 @@ NOKPROBE_SYMBOL(prepare_ftrace_return);  int ftrace_enable_ftrace_graph_caller(void)  {  	brcl_disable(ftrace_graph_caller); +	text_poke_sync_lock();  	return 0;  }  int ftrace_disable_ftrace_graph_caller(void)  {  	brcl_enable(ftrace_graph_caller); +	text_poke_sync_lock();  	return 0;  } diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 114b5490ad8e..42f9a325a257 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -20,8 +20,6 @@ __HEAD  ENTRY(startup_continue)  	larl	%r1,tod_clock_base  	mvc	0(16,%r1),__LC_BOOT_CLOCK -	larl	%r13,.LPG1		# get base -	lctlg	%c0,%c15,.Lctl-.LPG1(%r13)	# load control registers  #  # Setup stack  # @@ -42,19 +40,3 @@ ENTRY(startup_continue)  	.align	16  .LPG1:  .Ldw:	.quad	0x0002000180000000,0x0000000000000000 -.Lctl:	.quad	0x04040000		# cr0: AFP registers & secondary space -	.quad	0			# cr1: primary space segment table -	.quad	0			# cr2: dispatchable unit control table -	.quad	0			# cr3: instruction authorization -	.quad	0xffff			# cr4: instruction authorization -	.quad	0			# cr5: primary-aste origin -	.quad	0			# cr6: I/O interrupts -	.quad	0			# cr7: secondary space segment table -	.quad	0x0000000000008000	# cr8: access registers translation -	.quad	0			# cr9: tracing off -	.quad	0			# cr10: tracing off -	.quad	0			# cr11: tracing off -	.quad	0			# cr12: tracing off -	.quad	0			# cr13: home space segment table -	.quad	0xc0000000		# cr14: machine check handling off -	.quad	0			# cr15: linkage stack operations diff --git a/arch/s390/kernel/jump_label.c b/arch/s390/kernel/jump_label.c index 9156653b56f6..6bec000c6c1c 100644 --- a/arch/s390/kernel/jump_label.c +++ b/arch/s390/kernel/jump_label.c @@ -6,8 +6,9 @@   * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>   */  #include <linux/uaccess.h> -#include <linux/stop_machine.h>  #include <linux/jump_label.h> +#include <linux/module.h> +#include <asm/text-patching.h>  #include <asm/ipl.h>  struct insn { @@ -48,9 +49,9 @@ static struct insn orignop = {  	.offset = JUMP_LABEL_NOP_OFFSET >> 1,  }; -static void __jump_label_transform(struct jump_entry *entry, -				   enum jump_label_type type, -				   int init) +static void jump_label_transform(struct jump_entry *entry, +				 enum jump_label_type type, +				 int init)  {  	void *code = (void *)jump_entry_code(entry);  	struct insn old, new; @@ -72,19 +73,28 @@ static void __jump_label_transform(struct jump_entry *entry,  	s390_kernel_write(code, &new, sizeof(new));  } -static void __jump_label_sync(void *dummy) +void arch_jump_label_transform(struct jump_entry *entry, +			       enum jump_label_type type)  { +	jump_label_transform(entry, type, 0); +	text_poke_sync();  } -void arch_jump_label_transform(struct jump_entry *entry, -			       enum jump_label_type type) +bool arch_jump_label_transform_queue(struct jump_entry *entry, +				     enum jump_label_type type) +{ +	jump_label_transform(entry, type, 0); +	return true; +} + +void arch_jump_label_transform_apply(void)  { -	__jump_label_transform(entry, type, 0); -	smp_call_function(__jump_label_sync, NULL, 1); +	text_poke_sync();  } -void arch_jump_label_transform_static(struct jump_entry *entry, -				      enum jump_label_type type) +void __init_or_module arch_jump_label_transform_static(struct jump_entry *entry, +						       enum jump_label_type type)  { -	__jump_label_transform(entry, type, 1); +	jump_label_transform(entry, type, 1); +	text_poke_sync();  } diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index 52d056a5f89f..0093c326a239 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c @@ -120,9 +120,55 @@ static void s390_free_insn_slot(struct kprobe *p)  }  NOKPROBE_SYMBOL(s390_free_insn_slot); +/* Check if paddr is at an instruction boundary */ +static bool can_probe(unsigned long paddr) +{ +	unsigned long addr, offset = 0; +	kprobe_opcode_t insn; +	struct kprobe *kp; + +	if (paddr & 0x01) +		return false; + +	if (!kallsyms_lookup_size_offset(paddr, NULL, &offset)) +		return false; + +	/* Decode instructions */ +	addr = paddr - offset; +	while (addr < paddr) { +		if (copy_from_kernel_nofault(&insn, (void *)addr, sizeof(insn))) +			return false; + +		if (insn >> 8 == 0) { +			if (insn != BREAKPOINT_INSTRUCTION) { +				/* +				 * Note that QEMU inserts opcode 0x0000 to implement +				 * software breakpoints for guests. Since the size of +				 * the original instruction is unknown, stop following +				 * instructions and prevent setting a kprobe. +				 */ +				return false; +			} +			/* +			 * Check if the instruction has been modified by another +			 * kprobe, in which case the original instruction is +			 * decoded. +			 */ +			kp = get_kprobe((void *)addr); +			if (!kp) { +				/* not a kprobe */ +				return false; +			} +			insn = kp->opcode; +		} +		addr += insn_length(insn >> 8); +	} +	return addr == paddr; +} +  int arch_prepare_kprobe(struct kprobe *p)  { -	if ((unsigned long) p->addr & 0x01) +	if (!can_probe((unsigned long)p->addr))  		return -EINVAL;  	/* Make sure the probe isn't going on a difficult instruction */  	if (probe_is_prohibited_opcode(p->addr)) diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 6b13797143a7..39bcc0e39a10 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S @@ -22,10 +22,11 @@ ENTRY(ftrace_stub)  	BR_EX	%r14  ENDPROC(ftrace_stub) -#define STACK_FRAME_SIZE  (STACK_FRAME_OVERHEAD + __PT_SIZE) -#define STACK_PTREGS	  (STACK_FRAME_OVERHEAD) -#define STACK_PTREGS_GPRS (STACK_PTREGS + __PT_GPRS) -#define STACK_PTREGS_PSW  (STACK_PTREGS + __PT_PSW) +#define STACK_FRAME_SIZE	(STACK_FRAME_OVERHEAD + __PT_SIZE) +#define STACK_PTREGS		(STACK_FRAME_OVERHEAD) +#define STACK_PTREGS_GPRS	(STACK_PTREGS + __PT_GPRS) +#define STACK_PTREGS_PSW	(STACK_PTREGS + __PT_PSW) +#define STACK_PTREGS_ORIG_GPR2	(STACK_PTREGS + __PT_ORIG_GPR2)  #ifdef __PACK_STACK  /* allocate just enough for r14, r15 and backchain */  #define TRACED_FUNC_FRAME_SIZE	24 @@ -33,13 +34,15 @@ ENDPROC(ftrace_stub)  #define TRACED_FUNC_FRAME_SIZE	STACK_FRAME_OVERHEAD  #endif -ENTRY(ftrace_caller) -	.globl	ftrace_regs_caller -	.set	ftrace_regs_caller,ftrace_caller +	.macro	ftrace_regs_entry, allregs=0  	stg	%r14,(__SF_GPRS+8*8)(%r15)	# save traced function caller + +	.if \allregs == 1  	lghi	%r14,0				# save condition code  	ipm	%r14				# don't put any instructions  	sllg	%r14,%r14,16			# clobbering CC before this point +	.endif +  	lgr	%r1,%r15  	# allocate stack frame for ftrace_caller to contain traced function  	aghi	%r15,-TRACED_FUNC_FRAME_SIZE @@ -49,13 +52,31 @@ ENTRY(ftrace_caller)  	# allocate pt_regs and stack frame for ftrace_trace_function  	aghi	%r15,-STACK_FRAME_SIZE  	stg	%r1,(STACK_PTREGS_GPRS+15*8)(%r15) +	xc	STACK_PTREGS_ORIG_GPR2(8,%r15),STACK_PTREGS_ORIG_GPR2(%r15) + +	.if \allregs == 1  	stg	%r14,(STACK_PTREGS_PSW)(%r15) -	lg	%r14,(__SF_GPRS+8*8)(%r1)	# restore original return address  	stosm	(STACK_PTREGS_PSW)(%r15),0 +	.endif + +	lg	%r14,(__SF_GPRS+8*8)(%r1)	# restore original return address  	aghi	%r1,-TRACED_FUNC_FRAME_SIZE  	stg	%r1,__SF_BACKCHAIN(%r15)  	stg	%r0,(STACK_PTREGS_PSW+8)(%r15)  	stmg	%r2,%r14,(STACK_PTREGS_GPRS+2*8)(%r15) +	.endm + +SYM_CODE_START(ftrace_regs_caller) +	ftrace_regs_entry	1 +	j	ftrace_common +SYM_CODE_END(ftrace_regs_caller) + +SYM_CODE_START(ftrace_caller) +	ftrace_regs_entry	0 +	j	ftrace_common +SYM_CODE_END(ftrace_caller) + +SYM_CODE_START(ftrace_common)  #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES  	aghik	%r2,%r0,-MCOUNT_INSN_SIZE  	lgrl	%r4,function_trace_op @@ -74,24 +95,31 @@ ENTRY(ftrace_caller)  #ifdef CONFIG_FUNCTION_GRAPH_TRACER  # The j instruction gets runtime patched to a nop instruction.  # See ftrace_enable_ftrace_graph_caller. -	.globl ftrace_graph_caller -ftrace_graph_caller: -	j	ftrace_graph_caller_end +SYM_INNER_LABEL(ftrace_graph_caller, SYM_L_GLOBAL) +	j	.Lftrace_graph_caller_end  	lmg	%r2,%r3,(STACK_PTREGS_GPRS+14*8)(%r15)  	lg	%r4,(STACK_PTREGS_PSW+8)(%r15)  	brasl	%r14,prepare_ftrace_return  	stg	%r2,(STACK_PTREGS_GPRS+14*8)(%r15) -ftrace_graph_caller_end: -	.globl	ftrace_graph_caller_end +.Lftrace_graph_caller_end: +#endif +	lg	%r0,(STACK_PTREGS_PSW+8)(%r15) +#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES +	ltg	%r1,STACK_PTREGS_ORIG_GPR2(%r15) +	locgrz	%r1,%r0 +#else +	lg	%r1,STACK_PTREGS_ORIG_GPR2(%r15) +	ltgr	%r1,%r1 +	jnz	0f +	lgr	%r1,%r0  #endif -	lg	%r1,(STACK_PTREGS_PSW+8)(%r15) -	lmg	%r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15) +0:	lmg	%r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15)  	BR_EX	%r1 -ENDPROC(ftrace_caller) +SYM_CODE_END(ftrace_common)  #ifdef CONFIG_FUNCTION_GRAPH_TRACER -ENTRY(return_to_handler) +SYM_FUNC_START(return_to_handler)  	stmg	%r2,%r5,32(%r15)  	lgr	%r1,%r15  	aghi	%r15,-STACK_FRAME_OVERHEAD @@ -101,6 +129,6 @@ ENTRY(return_to_handler)  	lgr	%r14,%r2  	lmg	%r2,%r5,32(%r15)  	BR_EX	%r14 -ENDPROC(return_to_handler) +SYM_FUNC_END(return_to_handler)  #endif diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 67e5fff96ee0..191fc96a41b2 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -95,10 +95,10 @@ EXPORT_SYMBOL(console_irq);   * relocated above 2 GB, because it has to use 31 bit addresses.   * Such code and data is part of the .amode31 section.   */ -unsigned long __amode31_ref __samode31 = __pa(&_samode31); -unsigned long __amode31_ref __eamode31 = __pa(&_eamode31); -unsigned long __amode31_ref __stext_amode31 = __pa(&_stext_amode31); -unsigned long __amode31_ref __etext_amode31 = __pa(&_etext_amode31); +unsigned long __amode31_ref __samode31 = (unsigned long)&_samode31; +unsigned long __amode31_ref __eamode31 = (unsigned long)&_eamode31; +unsigned long __amode31_ref __stext_amode31 = (unsigned long)&_stext_amode31; +unsigned long __amode31_ref __etext_amode31 = (unsigned long)&_etext_amode31;  struct exception_table_entry __amode31_ref *__start_amode31_ex_table = _start_amode31_ex_table;  struct exception_table_entry __amode31_ref *__stop_amode31_ex_table = _stop_amode31_ex_table; @@ -149,6 +149,7 @@ struct mem_detect_info __bootdata(mem_detect);  struct initrd_data __bootdata(initrd_data);  unsigned long __bootdata_preserved(__kaslr_offset); +unsigned long __bootdata(__amode31_base);  unsigned int __bootdata_preserved(zlib_dfltcc_support);  EXPORT_SYMBOL(zlib_dfltcc_support);  u64 __bootdata_preserved(stfle_fac_list[16]); @@ -808,6 +809,7 @@ static void __init reserve_kernel(void)  	memblock_reserve(0, STARTUP_NORMAL_OFFSET);  	memblock_reserve((unsigned long)sclp_early_sccb, EXT_SCCB_READ_SCP); +	memblock_reserve(__amode31_base, __eamode31 - __samode31);  	memblock_reserve((unsigned long)_stext, PFN_PHYS(start_pfn)  			 - (unsigned long)_stext);  } @@ -831,20 +833,14 @@ static void __init setup_memory(void)  static void __init relocate_amode31_section(void)  { -	unsigned long amode31_addr, amode31_size; -	long amode31_offset; +	unsigned long amode31_size = __eamode31 - __samode31; +	long amode31_offset = __amode31_base - __samode31;  	long *ptr; -	/* Allocate a new AMODE31 capable memory region */ -	amode31_size = __eamode31 - __samode31;  	pr_info("Relocating AMODE31 section of size 0x%08lx\n", amode31_size); -	amode31_addr = (unsigned long)memblock_alloc_low(amode31_size, PAGE_SIZE); -	if (!amode31_addr) -		panic("Failed to allocate memory for AMODE31 section\n"); -	amode31_offset = amode31_addr - __samode31;  	/* Move original AMODE31 section to the new one */ -	memmove((void *)amode31_addr, (void *)__samode31, amode31_size); +	memmove((void *)__amode31_base, (void *)__samode31, amode31_size);  	/* Zero out the old AMODE31 section to catch invalid accesses within it */  	memset((void *)__samode31, 0, amode31_size); diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S index 63bdb9e1bfc1..42c43521878f 100644 --- a/arch/s390/kernel/vmlinux.lds.S +++ b/arch/s390/kernel/vmlinux.lds.S @@ -212,6 +212,7 @@ SECTIONS  		QUAD(__dynsym_start)				/* dynsym_start */  		QUAD(__rela_dyn_start)				/* rela_dyn_start */  		QUAD(__rela_dyn_end)				/* rela_dyn_end */ +		QUAD(_eamode31 - _samode31)			/* amode31_size */  	} :NONE  	/* Debugging sections.	*/ diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index 678333936f78..707cd4622c13 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -7,6 +7,8 @@ lib-y += delay.o string.o uaccess.o find.o spinlock.o  obj-y += mem.o xor.o  lib-$(CONFIG_KPROBES) += probes.o  lib-$(CONFIG_UPROBES) += probes.o +obj-$(CONFIG_S390_KPROBES_SANITY_TEST) += test_kprobes_s390.o +test_kprobes_s390-objs += test_kprobes_asm.o test_kprobes.o  # Instrumenting memory accesses to __user data (in different address space)  # produce false positives diff --git a/arch/s390/lib/test_kprobes.c b/arch/s390/lib/test_kprobes.c new file mode 100644 index 000000000000..9e62d62812e5 --- /dev/null +++ b/arch/s390/lib/test_kprobes.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <linux/kernel.h> +#include <linux/kprobes.h> +#include <linux/random.h> +#include <kunit/test.h> +#include "test_kprobes.h" + +static struct kprobe kp; + +static void setup_kprobe(struct kunit *test, struct kprobe *kp, +			 const char *symbol, int offset) +{ +	kp->offset = offset; +	kp->addr = NULL; +	kp->symbol_name = symbol; +} + +static void test_kprobe_offset(struct kunit *test, struct kprobe *kp, +			       const char *target, int offset) +{ +	int ret; + +	setup_kprobe(test, kp, target, 0); +	ret = register_kprobe(kp); +	if (!ret) +		unregister_kprobe(kp); +	KUNIT_EXPECT_EQ(test, 0, ret); +	setup_kprobe(test, kp, target, offset); +	ret = register_kprobe(kp); +	KUNIT_EXPECT_EQ(test, -EINVAL, ret); +	if (!ret) +		unregister_kprobe(kp); +} + +static void test_kprobe_odd(struct kunit *test) +{ +	test_kprobe_offset(test, &kp, "kprobes_target_odd", +			   kprobes_target_odd_offs); +} + +static void test_kprobe_in_insn4(struct kunit *test) +{ +	test_kprobe_offset(test, &kp, "kprobes_target_in_insn4", +			   kprobes_target_in_insn4_offs); +} + +static void test_kprobe_in_insn6_lo(struct kunit *test) +{ +	test_kprobe_offset(test, &kp, "kprobes_target_in_insn6_lo", +			   kprobes_target_in_insn6_lo_offs); +} + +static void test_kprobe_in_insn6_hi(struct kunit *test) +{ +	test_kprobe_offset(test, &kp, "kprobes_target_in_insn6_hi", +			   kprobes_target_in_insn6_hi_offs); +} + +static struct kunit_case kprobes_testcases[] = { +	KUNIT_CASE(test_kprobe_odd), +	KUNIT_CASE(test_kprobe_in_insn4), +	KUNIT_CASE(test_kprobe_in_insn6_lo), +	KUNIT_CASE(test_kprobe_in_insn6_hi), +	{} +}; + +static struct kunit_suite kprobes_test_suite = { +	.name = "kprobes_test_s390", +	.test_cases = kprobes_testcases, +}; + +kunit_test_suites(&kprobes_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/arch/s390/lib/test_kprobes.h b/arch/s390/lib/test_kprobes.h new file mode 100644 index 000000000000..2b4c9bc337f1 --- /dev/null +++ b/arch/s390/lib/test_kprobes.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef TEST_KPROBES_H +#define TEST_KPROBES_H + +extern unsigned long kprobes_target_odd_offs; +extern unsigned long kprobes_target_in_insn4_offs; +extern unsigned long kprobes_target_in_insn6_lo_offs; +extern unsigned long kprobes_target_in_insn6_hi_offs; + +#endif diff --git a/arch/s390/lib/test_kprobes_asm.S b/arch/s390/lib/test_kprobes_asm.S new file mode 100644 index 000000000000..ade7a3042334 --- /dev/null +++ b/arch/s390/lib/test_kprobes_asm.S @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#include <linux/linkage.h> +#include <asm/ftrace.h> + +#define KPROBES_TARGET_START(name)	\ +	SYM_FUNC_START(name);		\ +	FTRACE_GEN_NOP_ASM(name) + +#define KPROBES_TARGET_END(name)	\ +	SYM_FUNC_END(name);		\ +	SYM_DATA(name##_offs, .quad 1b - name) + +KPROBES_TARGET_START(kprobes_target_in_insn4) +	.word 0x4700 // bc 0,0 +1:	.word 0x0000 +	br %r14 +KPROBES_TARGET_END(kprobes_target_in_insn4) + +KPROBES_TARGET_START(kprobes_target_in_insn6_lo) +	.word 0xe310 // ly 1,0 +1:	.word 0x0000 +	.word 0x0058 +	br %r14 +KPROBES_TARGET_END(kprobes_target_in_insn6_lo) + +KPROBES_TARGET_START(kprobes_target_in_insn6_hi) +	.word 0xe310 // ly 1,0 +	.word 0x0000 +1:	.word 0x0058 +	br %r14 +KPROBES_TARGET_END(kprobes_target_in_insn6_hi) + +KPROBES_TARGET_START(kprobes_target_bp) +	nop +	.word 0x0000 +	nop +1:	br %r14 +KPROBES_TARGET_END(kprobes_target_bp) + +KPROBES_TARGET_START(kprobes_target_odd) +	.byte 0x07 +1:	.byte 0x07 +	br %r14 +KPROBES_TARGET_END(kprobes_target_odd) diff --git a/arch/s390/lib/test_unwind.c b/arch/s390/lib/test_unwind.c index ecf327d743a0..cfc5f5557c06 100644 --- a/arch/s390/lib/test_unwind.c +++ b/arch/s390/lib/test_unwind.c @@ -3,7 +3,7 @@   * Test module for unwind_for_each_frame   */ -#define pr_fmt(fmt) "test_unwind: " fmt +#include <kunit/test.h>  #include <asm/unwind.h>  #include <linux/completion.h>  #include <linux/kallsyms.h> @@ -16,6 +16,8 @@  #include <linux/wait.h>  #include <asm/irq.h> +struct kunit *current_test; +  #define BT_BUF_SIZE (PAGE_SIZE * 4)  /* @@ -29,7 +31,7 @@ static void print_backtrace(char *bt)  		p = strsep(&bt, "\n");  		if (!p)  			break; -		pr_err("%s\n", p); +		kunit_err(current_test, "%s\n", p);  	}  } @@ -49,7 +51,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,  	bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC);  	if (!bt) { -		pr_err("failed to allocate backtrace buffer\n"); +		kunit_err(current_test, "failed to allocate backtrace buffer\n");  		return -ENOMEM;  	}  	/* Unwind. */ @@ -63,7 +65,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,  		if (frame_count++ == max_frames)  			break;  		if (state.reliable && !addr) { -			pr_err("unwind state reliable but addr is 0\n"); +			kunit_err(current_test, "unwind state reliable but addr is 0\n");  			ret = -EINVAL;  			break;  		} @@ -75,7 +77,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,  					   stack_type_name(state.stack_info.type),  					   (void *)state.sp, (void *)state.ip);  			if (bt_pos >= BT_BUF_SIZE) -				pr_err("backtrace buffer is too small\n"); +				kunit_err(current_test, "backtrace buffer is too small\n");  		}  		frame_count += 1;  		if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1")) @@ -85,15 +87,15 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,  	/* Check the results. */  	if (unwind_error(&state)) { -		pr_err("unwind error\n"); +		kunit_err(current_test, "unwind error\n");  		ret = -EINVAL;  	}  	if (!seen_func2_func1) { -		pr_err("unwindme_func2 and unwindme_func1 not found\n"); +		kunit_err(current_test, "unwindme_func2 and unwindme_func1 not found\n");  		ret = -EINVAL;  	}  	if (frame_count == max_frames) { -		pr_err("Maximum number of frames exceeded\n"); +		kunit_err(current_test, "Maximum number of frames exceeded\n");  		ret = -EINVAL;  	}  	if (ret) @@ -166,7 +168,7 @@ static noinline int unwindme_func4(struct unwindme *u)  		kp.pre_handler = pgm_pre_handler;  		ret = register_kprobe(&kp);  		if (ret < 0) { -			pr_err("register_kprobe failed %d\n", ret); +			kunit_err(current_test, "register_kprobe failed %d\n", ret);  			return -EINVAL;  		} @@ -252,7 +254,7 @@ static int test_unwind_irq(struct unwindme *u)  }  /* Spawns a task and passes it to test_unwind(). */ -static int test_unwind_task(struct unwindme *u) +static int test_unwind_task(struct kunit *test, struct unwindme *u)  {  	struct task_struct *task;  	int ret; @@ -267,7 +269,7 @@ static int test_unwind_task(struct unwindme *u)  	 */  	task = kthread_run(unwindme_func1, u, "%s", __func__);  	if (IS_ERR(task)) { -		pr_err("kthread_run() failed\n"); +		kunit_err(test, "kthread_run() failed\n");  		return PTR_ERR(task);  	}  	/* @@ -282,77 +284,98 @@ static int test_unwind_task(struct unwindme *u)  	return ret;  } -static int test_unwind_flags(int flags) +struct test_params { +	int flags; +	char *name; +}; + +/* + * Create required parameter list for tests + */ +static const struct test_params param_list[] = { +	{.flags = UWM_DEFAULT, .name = "UWM_DEFAULT"}, +	{.flags = UWM_SP, .name = "UWM_SP"}, +	{.flags = UWM_REGS, .name = "UWM_REGS"}, +	{.flags = UWM_SWITCH_STACK, +		.name = "UWM_SWITCH_STACK"}, +	{.flags = UWM_SP | UWM_REGS, +		.name = "UWM_SP | UWM_REGS"}, +	{.flags = UWM_CALLER | UWM_SP, +		.name = "WM_CALLER | UWM_SP"}, +	{.flags = UWM_CALLER | UWM_SP | UWM_REGS, +		.name = "UWM_CALLER | UWM_SP | UWM_REGS"}, +	{.flags = UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK, +		.name = "UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK"}, +	{.flags = UWM_THREAD, .name = "UWM_THREAD"}, +	{.flags = UWM_THREAD | UWM_SP, +		.name = "UWM_THREAD | UWM_SP"}, +	{.flags = UWM_THREAD | UWM_CALLER | UWM_SP, +		.name = "UWM_THREAD | UWM_CALLER | UWM_SP"}, +	{.flags = UWM_IRQ, .name = "UWM_IRQ"}, +	{.flags = UWM_IRQ | UWM_SWITCH_STACK, +		.name = "UWM_IRQ | UWM_SWITCH_STACK"}, +	{.flags = UWM_IRQ | UWM_SP, +		.name = "UWM_IRQ | UWM_SP"}, +	{.flags = UWM_IRQ | UWM_REGS, +		.name = "UWM_IRQ | UWM_REGS"}, +	{.flags = UWM_IRQ | UWM_SP | UWM_REGS, +		.name = "UWM_IRQ | UWM_SP | UWM_REGS"}, +	{.flags = UWM_IRQ | UWM_CALLER | UWM_SP, +		.name = "UWM_IRQ | UWM_CALLER | UWM_SP"}, +	{.flags = UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS, +		.name = "UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS"}, +	{.flags = UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK, +		.name = "UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK"}, +	#ifdef CONFIG_KPROBES +	{.flags = UWM_PGM, .name = "UWM_PGM"}, +	{.flags = UWM_PGM | UWM_SP, +		.name = "UWM_PGM | UWM_SP"}, +	{.flags = UWM_PGM | UWM_REGS, +		.name = "UWM_PGM | UWM_REGS"}, +	{.flags = UWM_PGM | UWM_SP | UWM_REGS, +		.name = "UWM_PGM | UWM_SP | UWM_REGS"}, +	#endif +}; + +/* + * Parameter description generator: required for KUNIT_ARRAY_PARAM() + */ +static void get_desc(const struct test_params *params, char *desc) +{ +	strscpy(desc, params->name, KUNIT_PARAM_DESC_SIZE); +} + +/* + * Create test_unwind_gen_params + */ +KUNIT_ARRAY_PARAM(test_unwind, param_list, get_desc); + +static void test_unwind_flags(struct kunit *test)  {  	struct unwindme u; +	const struct test_params *params; -	u.flags = flags; +	current_test = test; +	params = (const struct test_params *)test->param_value; +	u.flags = params->flags;  	if (u.flags & UWM_THREAD) -		return test_unwind_task(&u); +		KUNIT_EXPECT_EQ(test, 0, test_unwind_task(test, &u));  	else if (u.flags & UWM_IRQ) -		return test_unwind_irq(&u); +		KUNIT_EXPECT_EQ(test, 0, test_unwind_irq(&u));  	else -		return unwindme_func1(&u); +		KUNIT_EXPECT_EQ(test, 0, unwindme_func1(&u));  } -static int test_unwind_init(void) -{ -	int failed = 0; -	int total = 0; - -#define TEST(flags)							\ -do {									\ -	pr_info("[ RUN      ] " #flags "\n");				\ -	total++;							\ -	if (!test_unwind_flags((flags))) {				\ -		pr_info("[       OK ] " #flags "\n");			\ -	} else {							\ -		pr_err("[  FAILED  ] " #flags "\n");			\ -		failed++;						\ -	}								\ -} while (0) - -	pr_info("running stack unwinder tests"); -	TEST(UWM_DEFAULT); -	TEST(UWM_SP); -	TEST(UWM_REGS); -	TEST(UWM_SWITCH_STACK); -	TEST(UWM_SP | UWM_REGS); -	TEST(UWM_CALLER | UWM_SP); -	TEST(UWM_CALLER | UWM_SP | UWM_REGS); -	TEST(UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); -	TEST(UWM_THREAD); -	TEST(UWM_THREAD | UWM_SP); -	TEST(UWM_THREAD | UWM_CALLER | UWM_SP); -	TEST(UWM_IRQ); -	TEST(UWM_IRQ | UWM_SWITCH_STACK); -	TEST(UWM_IRQ | UWM_SP); -	TEST(UWM_IRQ | UWM_REGS); -	TEST(UWM_IRQ | UWM_SP | UWM_REGS); -	TEST(UWM_IRQ | UWM_CALLER | UWM_SP); -	TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS); -	TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); -#ifdef CONFIG_KPROBES -	TEST(UWM_PGM); -	TEST(UWM_PGM | UWM_SP); -	TEST(UWM_PGM | UWM_REGS); -	TEST(UWM_PGM | UWM_SP | UWM_REGS); -#endif -#undef TEST -	if (failed) { -		pr_err("%d of %d stack unwinder tests failed", failed, total); -		WARN(1, "%d of %d stack unwinder tests failed", failed, total); -	} else { -		pr_info("all %d stack unwinder tests passed", total); -	} +static struct kunit_case unwind_test_cases[] = { +	KUNIT_CASE_PARAM(test_unwind_flags, test_unwind_gen_params), +	{} +}; -	return failed ? -EINVAL : 0; -} +static struct kunit_suite test_unwind_suite = { +	.name = "test_unwind", +	.test_cases = unwind_test_cases, +}; -static void test_unwind_exit(void) -{ -} +kunit_test_suites(&test_unwind_suite); -module_init(test_unwind_init); -module_exit(test_unwind_exit);  MODULE_LICENSE("GPL"); diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c index 1141c8d5c0d0..2203164b39da 100644 --- a/arch/s390/mm/cmm.c +++ b/arch/s390/mm/cmm.c @@ -14,8 +14,8 @@  #include <linux/moduleparam.h>  #include <linux/gfp.h>  #include <linux/sched.h> +#include <linux/string_helpers.h>  #include <linux/sysctl.h> -#include <linux/ctype.h>  #include <linux/swap.h>  #include <linux/kthread.h>  #include <linux/oom.h> @@ -394,13 +394,10 @@ static int __init cmm_init(void)  		goto out_sysctl;  #ifdef CONFIG_CMM_IUCV  	/* convert sender to uppercase characters */ -	if (sender) { -		int len = strlen(sender); -		while (len--) -			sender[len] = toupper(sender[len]); -	} else { +	if (sender) +		string_upper(sender, sender); +	else  		sender = cmm_default_sender; -	}  	rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);  	if (rc < 0) diff --git a/arch/s390/pci/pci_event.c b/arch/s390/pci/pci_event.c index 5b8d647523f9..6a5bfa9dc1f2 100644 --- a/arch/s390/pci/pci_event.c +++ b/arch/s390/pci/pci_event.c @@ -52,6 +52,8 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)  	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);  	struct pci_dev *pdev = NULL; +	zpci_dbg(3, "err fid:%x, fh:%x, pec:%x\n", +		 ccdf->fid, ccdf->fh, ccdf->pec);  	zpci_err("error CCDF:\n");  	zpci_err_hex(ccdf, sizeof(*ccdf)); @@ -96,6 +98,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)  	struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);  	enum zpci_state state; +	zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n", +		 ccdf->fid, ccdf->fh, ccdf->pec);  	zpci_err("avail CCDF:\n");  	zpci_err_hex(ccdf, sizeof(*ccdf)); diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index 335c281811c7..cae280e5c047 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -90,6 +90,14 @@ static ssize_t recover_store(struct device *dev, struct device_attribute *attr,  		if (zdev_enabled(zdev)) {  			ret = zpci_disable_device(zdev); +			/* +			 * Due to a z/VM vs LPAR inconsistency in the error +			 * state the FH may indicate an enabled device but +			 * disable says the device is already disabled don't +			 * treat it as an error here. +			 */ +			if (ret == -EINVAL) +				ret = 0;  			if (ret)  				goto out;  		} diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ab83c22d274e..620fce152be9 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -190,6 +190,7 @@ config X86  	select HAVE_DYNAMIC_FTRACE_WITH_REGS  	select HAVE_DYNAMIC_FTRACE_WITH_ARGS	if X86_64  	select HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS +	select HAVE_SAMPLE_FTRACE_DIRECT	if X86_64  	select HAVE_EBPF_JIT  	select HAVE_EFFICIENT_UNALIGNED_ACCESS  	select HAVE_EISA diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c index fa966e0db6ca..80673dbfb1f9 100644 --- a/drivers/s390/block/dasd_genhd.c +++ b/drivers/s390/block/dasd_genhd.c @@ -33,7 +33,7 @@ int dasd_gendisk_alloc(struct dasd_block *block)  {  	struct gendisk *gdp;  	struct dasd_device *base; -	int len; +	int len, rc;  	/* Make sure the minor for this device exists. */  	base = block->base; @@ -79,7 +79,13 @@ int dasd_gendisk_alloc(struct dasd_block *block)  	dasd_add_link_to_gendisk(gdp, base);  	block->gdp = gdp;  	set_capacity(block->gdp, 0); -	device_add_disk(&base->cdev->dev, block->gdp, NULL); + +	rc = device_add_disk(&base->cdev->dev, block->gdp, NULL); +	if (rc) { +		dasd_gendisk_free(block); +		return rc; +	} +  	return 0;  } diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 5be3d1c39a78..0741a9321712 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -696,7 +696,9 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char  	}  	get_device(&dev_info->dev); -	device_add_disk(&dev_info->dev, dev_info->gd, NULL); +	rc = device_add_disk(&dev_info->dev, dev_info->gd, NULL); +	if (rc) +		goto out_dax;  	switch (dev_info->segment_type) {  		case SEG_TYPE_SR: @@ -712,6 +714,10 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char  	rc = count;  	goto out; +out_dax: +	put_device(&dev_info->dev); +	kill_dax(dev_info->dax_dev); +	put_dax(dev_info->dax_dev);  put_dev:  	list_del(&dev_info->lh);  	blk_cleanup_disk(dev_info->gd); diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c index 88cba6212ee2..61ecdcb2cc6a 100644 --- a/drivers/s390/block/scm_blk.c +++ b/drivers/s390/block/scm_blk.c @@ -495,9 +495,14 @@ int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)  	/* 512 byte sectors */  	set_capacity(bdev->gendisk, scmdev->size >> 9); -	device_add_disk(&scmdev->dev, bdev->gendisk, NULL); +	ret = device_add_disk(&scmdev->dev, bdev->gendisk, NULL); +	if (ret) +		goto out_cleanup_disk; +  	return 0; +out_cleanup_disk: +	blk_cleanup_disk(bdev->gendisk);  out_tag:  	blk_mq_free_tag_set(&bdev->tag_set);  out: diff --git a/drivers/s390/char/sclp_sd.c b/drivers/s390/char/sclp_sd.c index 1e244f78f192..a5dd4e9f5b1b 100644 --- a/drivers/s390/char/sclp_sd.c +++ b/drivers/s390/char/sclp_sd.c @@ -122,6 +122,7 @@ static void sclp_sd_listener_remove(struct sclp_sd_listener *listener)  /**   * sclp_sd_listener_init() - Initialize a Store Data response listener + * @listener: Response listener to initialize   * @id: Event ID to listen for   *   * Initialize a listener for asynchronous Store Data responses. This listener @@ -403,6 +404,7 @@ static int sclp_sd_file_update(struct sclp_sd_file *sd_file)  /**   * sclp_sd_file_update_async() - Wrapper for asynchronous update call   * @data: Object to update + * @cookie: Unused   */  static void sclp_sd_file_update_async(void *data, async_cookie_t cookie)  { @@ -414,6 +416,9 @@ static void sclp_sd_file_update_async(void *data, async_cookie_t cookie)  /**   * reload_store() - Store function for "reload" sysfs attribute   * @kobj: Kobject of sclp_sd_file object + * @attr: Reload attribute + * @buf: Data written to sysfs attribute + * @count: Count of bytes written   *   * Initiate a reload of the data associated with an sclp_sd_file object.   */ @@ -441,8 +446,10 @@ static struct kobj_type sclp_sd_file_ktype = {  };  /** - * data_read() - Read function for "read" sysfs attribute + * data_read() - Read function for "data" sysfs attribute + * @file: Open file pointer   * @kobj: Kobject of sclp_sd_file object + * @attr: Data attribute   * @buffer: Target buffer   * @off: Requested file offset   * @size: Requested number of bytes diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 29a6a0099f83..7bc4e4a10937 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -768,6 +768,8 @@ out_driver:  }  __initcall(sclp_vt220_tty_init); +#ifdef CONFIG_SCLP_VT220_CONSOLE +  static void __sclp_vt220_flush_buffer(void)  {  	unsigned long flags; @@ -784,8 +786,6 @@ static void __sclp_vt220_flush_buffer(void)  	spin_unlock_irqrestore(&sclp_vt220_lock, flags);  } -#ifdef CONFIG_SCLP_VT220_CONSOLE -  static void  sclp_vt220_con_write(struct console *con, const char *buf, unsigned int count)  { diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 44461928aab8..2bc55ccf3f23 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -792,10 +792,13 @@ static int __unset_online(struct device *dev, void *data)  {  	struct idset *set = data;  	struct subchannel *sch = to_subchannel(dev); -	struct ccw_device *cdev = sch_get_cdev(sch); +	struct ccw_device *cdev; -	if (cdev && cdev->online) -		idset_sch_del(set, sch->schid); +	if (sch->st == SUBCHANNEL_TYPE_IO) { +		cdev = sch_get_cdev(sch); +		if (cdev && cdev->online) +			idset_sch_del(set, sch->schid); +	}  	return 0;  } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 8d14569823d7..07a17613fab5 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -1322,6 +1322,7 @@ static int purge_fn(struct device *dev, void *data)  {  	struct ccw_device *cdev = to_ccwdev(dev);  	struct ccw_dev_id *id = &cdev->private->dev_id; +	struct subchannel *sch = to_subchannel(cdev->dev.parent);  	spin_lock_irq(cdev->ccwlock);  	if (is_blacklisted(id->ssid, id->devno) && @@ -1330,6 +1331,7 @@ static int purge_fn(struct device *dev, void *data)  		CIO_MSG_EVENT(3, "ccw: purging 0.%x.%04x\n", id->ssid,  			      id->devno);  		ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); +		css_sched_sch_todo(sch, SCH_TODO_UNREG);  		atomic_set(&cdev->private->onoff, 0);  	}  	spin_unlock_irq(cdev->ccwlock); diff --git a/samples/Kconfig b/samples/Kconfig index b0503ef058d3..501f66309118 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -26,7 +26,7 @@ config SAMPLE_TRACE_PRINTK  config SAMPLE_FTRACE_DIRECT  	tristate "Build register_ftrace_direct() example"  	depends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS && m -	depends on X86_64 # has x86_64 inlined asm +	depends on HAVE_SAMPLE_FTRACE_DIRECT  	help  	  This builds an ftrace direct function example  	  that hooks to wake_up_process and prints the parameters. @@ -224,3 +224,6 @@ config SAMPLE_WATCH_QUEUE  	  sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.  endif # SAMPLES + +config HAVE_SAMPLE_FTRACE_DIRECT +	bool diff --git a/samples/ftrace/ftrace-direct-modify.c b/samples/ftrace/ftrace-direct-modify.c index 5b9a09957c6e..690e4a9ff333 100644 --- a/samples/ftrace/ftrace-direct-modify.c +++ b/samples/ftrace/ftrace-direct-modify.c @@ -2,6 +2,7 @@  #include <linux/module.h>  #include <linux/kthread.h>  #include <linux/ftrace.h> +#include <asm/asm-offsets.h>  void my_direct_func1(void)  { @@ -18,6 +19,8 @@ extern void my_tramp2(void *);  static unsigned long my_ip = (unsigned long)schedule; +#ifdef CONFIG_X86_64 +  asm (  "	.pushsection    .text, \"ax\", @progbits\n"  "	.type		my_tramp1, @function\n" @@ -41,6 +44,47 @@ asm (  "	.popsection\n"  ); +#endif /* CONFIG_X86_64 */ + +#ifdef CONFIG_S390 + +asm ( +"	.pushsection	.text, \"ax\", @progbits\n" +"	.type		my_tramp1, @function\n" +"	.globl		my_tramp1\n" +"   my_tramp1:" +"	lgr		%r1,%r15\n" +"	stmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	stg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	aghi		%r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" +"	stg		%r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" +"	brasl		%r14,my_direct_func1\n" +"	aghi		%r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" +"	lmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	lg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	lgr		%r1,%r0\n" +"	br		%r1\n" +"	.size		my_tramp1, .-my_tramp1\n" +"	.type		my_tramp2, @function\n" +"	.globl		my_tramp2\n" +"   my_tramp2:" +"	lgr		%r1,%r15\n" +"	stmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	stg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	aghi		%r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" +"	stg		%r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" +"	brasl		%r14,my_direct_func2\n" +"	aghi		%r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" +"	lmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	lg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	lgr		%r1,%r0\n" +"	br		%r1\n" +"	.size		my_tramp2, .-my_tramp2\n" +"	.popsection\n" +); + +#endif /* CONFIG_S390 */ +  static unsigned long my_tramp = (unsigned long)my_tramp1;  static unsigned long tramps[2] = {  	(unsigned long)my_tramp1, diff --git a/samples/ftrace/ftrace-direct-too.c b/samples/ftrace/ftrace-direct-too.c index 3f0079c9bd6f..6e0de725bf22 100644 --- a/samples/ftrace/ftrace-direct-too.c +++ b/samples/ftrace/ftrace-direct-too.c @@ -3,6 +3,7 @@  #include <linux/mm.h> /* for handle_mm_fault() */  #include <linux/ftrace.h> +#include <asm/asm-offsets.h>  void my_direct_func(struct vm_area_struct *vma,  			unsigned long address, unsigned int flags) @@ -13,6 +14,8 @@ void my_direct_func(struct vm_area_struct *vma,  extern void my_tramp(void *); +#ifdef CONFIG_X86_64 +  asm (  "	.pushsection    .text, \"ax\", @progbits\n"  "	.type		my_tramp, @function\n" @@ -33,6 +36,31 @@ asm (  "	.popsection\n"  ); +#endif /* CONFIG_X86_64 */ + +#ifdef CONFIG_S390 + +asm ( +"	.pushsection	.text, \"ax\", @progbits\n" +"	.type		my_tramp, @function\n" +"	.globl		my_tramp\n" +"   my_tramp:" +"	lgr		%r1,%r15\n" +"	stmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	stg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	aghi		%r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" +"	stg		%r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" +"	brasl		%r14,my_direct_func\n" +"	aghi		%r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" +"	lmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	lg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	lgr		%r1,%r0\n" +"	br		%r1\n" +"	.size		my_tramp, .-my_tramp\n" +"	.popsection\n" +); + +#endif /* CONFIG_S390 */  static int __init ftrace_direct_init(void)  { diff --git a/samples/ftrace/ftrace-direct.c b/samples/ftrace/ftrace-direct.c index a2729d1ef17f..a30aa42ec76a 100644 --- a/samples/ftrace/ftrace-direct.c +++ b/samples/ftrace/ftrace-direct.c @@ -3,6 +3,7 @@  #include <linux/sched.h> /* for wake_up_process() */  #include <linux/ftrace.h> +#include <asm/asm-offsets.h>  void my_direct_func(struct task_struct *p)  { @@ -11,6 +12,8 @@ void my_direct_func(struct task_struct *p)  extern void my_tramp(void *); +#ifdef CONFIG_X86_64 +  asm (  "	.pushsection    .text, \"ax\", @progbits\n"  "	.type		my_tramp, @function\n" @@ -27,6 +30,31 @@ asm (  "	.popsection\n"  ); +#endif /* CONFIG_X86_64 */ + +#ifdef CONFIG_S390 + +asm ( +"	.pushsection	.text, \"ax\", @progbits\n" +"	.type		my_tramp, @function\n" +"	.globl		my_tramp\n" +"   my_tramp:" +"	lgr		%r1,%r15\n" +"	stmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	stg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	aghi		%r15,"__stringify(-STACK_FRAME_OVERHEAD)"\n" +"	stg		%r1,"__stringify(__SF_BACKCHAIN)"(%r15)\n" +"	brasl		%r14,my_direct_func\n" +"	aghi		%r15,"__stringify(STACK_FRAME_OVERHEAD)"\n" +"	lmg		%r0,%r5,"__stringify(__SF_GPRS)"(%r15)\n" +"	lg		%r14,"__stringify(__SF_GPRS+8*8)"(%r15)\n" +"	lgr		%r1,%r0\n" +"	br		%r1\n" +"	.size		my_tramp, .-my_tramp\n" +"	.popsection\n" +); + +#endif /* CONFIG_S390 */  static int __init ftrace_direct_init(void)  { diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc index 84285a6f60b0..dc7ade196798 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_string.tc @@ -22,6 +22,9 @@ ppc64*)  ppc*)    ARG1=%r3  ;; +s390*) +  ARG1=%r2 +;;  *)    echo "Please implement other architecture here"    exit_untested diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc index 474ca1a9a088..47d84b5cb6ca 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_syntax.tc @@ -32,6 +32,10 @@ ppc*)    GOODREG=%r3    BADREG=%msr  ;; +s390*) +  GOODREG=%r2 +  BADREG=%s2 +;;  *)    echo "Please implement other architecture here"    exit_untested  | 
