mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	s390: convert to generic entry
This patch converts s390 to use the generic entry infrastructure from
kernel/entry/*.
There are a few special things on s390:
- PIF_PER_TRAP is moved to TIF_PER_TRAP as the generic code doesn't
  know about our PIF flags in exit_to_user_mode_loop().
- The old code had several ways to restart syscalls:
  a) PIF_SYSCALL_RESTART, which was only set during execve to force a
     restart after upgrading a process (usually qemu-kvm) to pgste page
     table extensions.
  b) PIF_SYSCALL, which is set by do_signal() to indicate that the
     current syscall should be restarted. This is changed so that
     do_signal() now also uses PIF_SYSCALL_RESTART. Continuing to use
     PIF_SYSCALL doesn't work with the generic code, and changing it
     to PIF_SYSCALL_RESTART makes PIF_SYSCALL and PIF_SYSCALL_RESTART
     more unique.
- On s390 calling sys_sigreturn or sys_rt_sigreturn is implemented by
executing a svc instruction on the process stack which causes a fault.
While handling that fault the fault code sets PIF_SYSCALL to hand over
processing to the syscall code on exit to usermode.
The patch introduces PIF_SYSCALL_RET_SET, which is set if ptrace sets
a return value for a syscall. The s390x ptrace ABI uses r2 both for the
syscall number and return value, so ptrace cannot set the syscall number +
return value at the same time. The flag makes handling that a bit easier.
do_syscall() will just skip executing the syscall if PIF_SYSCALL_RET_SET
is set.
CONFIG_DEBUG_ASCE was removd in favour of the generic CONFIG_DEBUG_ENTRY.
CR1/7/13 will be checked both on kernel entry and exit to contain the
correct asces.
Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
			
			
This commit is contained in:
		
							parent
							
								
									ac94a2911e
								
							
						
					
					
						commit
						56e62a7370
					
				
					 39 changed files with 652 additions and 920 deletions
				
			
		| 
						 | 
				
			
			@ -123,6 +123,7 @@ config S390
 | 
			
		|||
	select GENERIC_ALLOCATOR
 | 
			
		||||
	select GENERIC_CPU_AUTOPROBE
 | 
			
		||||
	select GENERIC_CPU_VULNERABILITIES
 | 
			
		||||
	select GENERIC_ENTRY
 | 
			
		||||
	select GENERIC_FIND_FIRST_BIT
 | 
			
		||||
	select GENERIC_GETTIMEOFDAY
 | 
			
		||||
	select GENERIC_PTDUMP
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,10 +6,12 @@ config TRACE_IRQFLAGS_SUPPORT
 | 
			
		|||
config EARLY_PRINTK
 | 
			
		||||
	def_bool y
 | 
			
		||||
 | 
			
		||||
config DEBUG_USER_ASCE
 | 
			
		||||
	bool "Debug User ASCE"
 | 
			
		||||
config DEBUG_ENTRY
 | 
			
		||||
	bool "Debug low-level entry code"
 | 
			
		||||
	depends on DEBUG_KERNEL
 | 
			
		||||
	help
 | 
			
		||||
	  Check on exit to user space that address space control
 | 
			
		||||
	  elements are setup correctly.
 | 
			
		||||
	  This option enables sanity checks in s390 low-level entry code.
 | 
			
		||||
	  Some of these sanity checks may slow down kernel entries and
 | 
			
		||||
	  exits or otherwise impact performance.
 | 
			
		||||
 | 
			
		||||
	  If unsure, say N.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -833,7 +833,6 @@ CONFIG_BPF_KPROBE_OVERRIDE=y
 | 
			
		|||
CONFIG_HIST_TRIGGERS=y
 | 
			
		||||
CONFIG_FTRACE_STARTUP_TEST=y
 | 
			
		||||
# CONFIG_EVENT_TRACE_STARTUP_TEST is not set
 | 
			
		||||
CONFIG_DEBUG_USER_ASCE=y
 | 
			
		||||
CONFIG_NOTIFIER_ERROR_INJECTION=m
 | 
			
		||||
CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m
 | 
			
		||||
CONFIG_FAULT_INJECTION=y
 | 
			
		||||
| 
						 | 
				
			
			@ -857,3 +856,4 @@ CONFIG_PERCPU_TEST=m
 | 
			
		|||
CONFIG_ATOMIC64_SELFTEST=y
 | 
			
		||||
CONFIG_TEST_BITOPS=m
 | 
			
		||||
CONFIG_TEST_BPF=m
 | 
			
		||||
CONFIG_DEBUG_ENTRY=y
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -781,7 +781,6 @@ CONFIG_FTRACE_SYSCALLS=y
 | 
			
		|||
CONFIG_BLK_DEV_IO_TRACE=y
 | 
			
		||||
CONFIG_BPF_KPROBE_OVERRIDE=y
 | 
			
		||||
CONFIG_HIST_TRIGGERS=y
 | 
			
		||||
CONFIG_DEBUG_USER_ASCE=y
 | 
			
		||||
CONFIG_LKDTM=m
 | 
			
		||||
CONFIG_PERCPU_TEST=m
 | 
			
		||||
CONFIG_ATOMIC64_SELFTEST=y
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,4 +35,6 @@ u64 arch_cpu_idle_time(int cpu);
 | 
			
		|||
 | 
			
		||||
#define arch_idle_time(cpu) arch_cpu_idle_time(cpu)
 | 
			
		||||
 | 
			
		||||
void account_idle_time_irq(void);
 | 
			
		||||
 | 
			
		||||
#endif /* _S390_CPUTIME_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -233,8 +233,7 @@ extern char elf_platform[];
 | 
			
		|||
do {								\
 | 
			
		||||
	set_personality(PER_LINUX |				\
 | 
			
		||||
		(current->personality & (~PER_MASK)));		\
 | 
			
		||||
	current->thread.sys_call_table =			\
 | 
			
		||||
		(unsigned long) &sys_call_table;		\
 | 
			
		||||
	current->thread.sys_call_table = sys_call_table;	\
 | 
			
		||||
} while (0)
 | 
			
		||||
#else /* CONFIG_COMPAT */
 | 
			
		||||
#define SET_PERSONALITY(ex)					\
 | 
			
		||||
| 
						 | 
				
			
			@ -245,11 +244,11 @@ do {								\
 | 
			
		|||
	if ((ex).e_ident[EI_CLASS] == ELFCLASS32) {		\
 | 
			
		||||
		set_thread_flag(TIF_31BIT);			\
 | 
			
		||||
		current->thread.sys_call_table =		\
 | 
			
		||||
			(unsigned long)	&sys_call_table_emu;	\
 | 
			
		||||
			sys_call_table_emu;			\
 | 
			
		||||
	} else {						\
 | 
			
		||||
		clear_thread_flag(TIF_31BIT);			\
 | 
			
		||||
		current->thread.sys_call_table =		\
 | 
			
		||||
			(unsigned long) &sys_call_table;	\
 | 
			
		||||
			sys_call_table;				\
 | 
			
		||||
	}							\
 | 
			
		||||
} while (0)
 | 
			
		||||
#endif /* CONFIG_COMPAT */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										60
									
								
								arch/s390/include/asm/entry-common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								arch/s390/include/asm/entry-common.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0 */
 | 
			
		||||
#ifndef ARCH_S390_ENTRY_COMMON_H
 | 
			
		||||
#define ARCH_S390_ENTRY_COMMON_H
 | 
			
		||||
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/audit.h>
 | 
			
		||||
#include <linux/tracehook.h>
 | 
			
		||||
#include <linux/processor.h>
 | 
			
		||||
#include <linux/uaccess.h>
 | 
			
		||||
#include <asm/fpu/api.h>
 | 
			
		||||
 | 
			
		||||
#define ARCH_EXIT_TO_USER_MODE_WORK (_TIF_GUARDED_STORAGE | _TIF_PER_TRAP)
 | 
			
		||||
 | 
			
		||||
void do_per_trap(struct pt_regs *regs);
 | 
			
		||||
void do_syscall(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
typedef void (*pgm_check_func)(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
extern pgm_check_func pgm_check_table[128];
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_DEBUG_ENTRY
 | 
			
		||||
static __always_inline void arch_check_user_regs(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	debug_user_asce(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define arch_check_user_regs arch_check_user_regs
 | 
			
		||||
#endif /* CONFIG_DEBUG_ENTRY */
 | 
			
		||||
 | 
			
		||||
static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs,
 | 
			
		||||
							unsigned long ti_work)
 | 
			
		||||
{
 | 
			
		||||
	if (ti_work & _TIF_PER_TRAP) {
 | 
			
		||||
		clear_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
		do_per_trap(regs);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ti_work & _TIF_GUARDED_STORAGE)
 | 
			
		||||
		gs_load_bc_cb(regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define arch_exit_to_user_mode_work arch_exit_to_user_mode_work
 | 
			
		||||
 | 
			
		||||
static __always_inline void arch_exit_to_user_mode(void)
 | 
			
		||||
{
 | 
			
		||||
	if (test_cpu_flag(CIF_FPU))
 | 
			
		||||
		__load_fpu_regs();
 | 
			
		||||
 | 
			
		||||
	if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
 | 
			
		||||
		debug_user_asce(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define arch_exit_to_user_mode arch_exit_to_user_mode
 | 
			
		||||
 | 
			
		||||
static inline bool on_thread_stack(void)
 | 
			
		||||
{
 | 
			
		||||
	return !(((unsigned long)(current->stack) ^ current_stack_pointer()) & ~(THREAD_SIZE - 1));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +47,8 @@
 | 
			
		|||
#include <linux/preempt.h>
 | 
			
		||||
 | 
			
		||||
void save_fpu_regs(void);
 | 
			
		||||
void load_fpu_regs(void);
 | 
			
		||||
void __load_fpu_regs(void);
 | 
			
		||||
 | 
			
		||||
static inline int test_fp_ctl(u32 fpc)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,11 +20,13 @@ struct s390_idle_data {
 | 
			
		|||
	unsigned long long clock_idle_exit;
 | 
			
		||||
	unsigned long long timer_idle_enter;
 | 
			
		||||
	unsigned long long timer_idle_exit;
 | 
			
		||||
	unsigned long mt_cycles_enter[8];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern struct device_attribute dev_attr_idle_count;
 | 
			
		||||
extern struct device_attribute dev_attr_idle_time_us;
 | 
			
		||||
 | 
			
		||||
void psw_idle(struct s390_idle_data *, unsigned long);
 | 
			
		||||
void psw_idle(struct s390_idle_data *data, unsigned long psw_mask);
 | 
			
		||||
void psw_idle_exit(void);
 | 
			
		||||
 | 
			
		||||
#endif /* _S390_IDLE_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,8 +81,8 @@ struct lowcore {
 | 
			
		|||
	psw_t	return_mcck_psw;		/* 0x02a0 */
 | 
			
		||||
 | 
			
		||||
	/* CPU accounting and timing values. */
 | 
			
		||||
	__u64	sync_enter_timer;		/* 0x02b0 */
 | 
			
		||||
	__u64	async_enter_timer;		/* 0x02b8 */
 | 
			
		||||
	__u64	sys_enter_timer;		/* 0x02b0 */
 | 
			
		||||
	__u8	pad_0x02b8[0x02c0-0x02b8];	/* 0x02b8 */
 | 
			
		||||
	__u64	mcck_enter_timer;		/* 0x02c0 */
 | 
			
		||||
	__u64	exit_timer;			/* 0x02c8 */
 | 
			
		||||
	__u64	user_timer;			/* 0x02d0 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,6 +99,7 @@ int nmi_alloc_per_cpu(struct lowcore *lc);
 | 
			
		|||
void nmi_free_per_cpu(struct lowcore *lc);
 | 
			
		||||
 | 
			
		||||
void s390_handle_mcck(void);
 | 
			
		||||
void __s390_handle_mcck(void);
 | 
			
		||||
int s390_do_machine_check(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
#endif /* __ASSEMBLY__ */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,11 @@
 | 
			
		|||
#include <asm/runtime_instr.h>
 | 
			
		||||
#include <asm/fpu/types.h>
 | 
			
		||||
#include <asm/fpu/internal.h>
 | 
			
		||||
#include <asm/irqflags.h>
 | 
			
		||||
 | 
			
		||||
typedef long (*sys_call_ptr_t)(unsigned long, unsigned long,
 | 
			
		||||
			       unsigned long, unsigned long,
 | 
			
		||||
			       unsigned long, unsigned long);
 | 
			
		||||
 | 
			
		||||
static inline void set_cpu_flag(int flag)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -107,11 +112,12 @@ struct thread_struct {
 | 
			
		|||
	unsigned long system_timer;		/* task cputime in kernel space */
 | 
			
		||||
	unsigned long hardirq_timer;		/* task cputime in hardirq context */
 | 
			
		||||
	unsigned long softirq_timer;		/* task cputime in softirq context */
 | 
			
		||||
	unsigned long sys_call_table;	/* system call table address */
 | 
			
		||||
	const sys_call_ptr_t *sys_call_table;	/* system call table address */
 | 
			
		||||
	unsigned long gmap_addr;		/* address of last gmap fault. */
 | 
			
		||||
	unsigned int gmap_write_flag;		/* gmap fault write indication */
 | 
			
		||||
	unsigned int gmap_int_code;		/* int code of last gmap fault */
 | 
			
		||||
	unsigned int gmap_pfault;		/* signal of a pending guest pfault */
 | 
			
		||||
 | 
			
		||||
	/* Per-thread information related to debugging */
 | 
			
		||||
	struct per_regs per_user;		/* User specified PER registers */
 | 
			
		||||
	struct per_event per_event;		/* Cause of the last PER trap */
 | 
			
		||||
| 
						 | 
				
			
			@ -184,6 +190,7 @@ static inline void release_thread(struct task_struct *tsk) { }
 | 
			
		|||
 | 
			
		||||
/* Free guarded storage control block */
 | 
			
		||||
void guarded_storage_release(struct task_struct *tsk);
 | 
			
		||||
void gs_load_bc_cb(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
unsigned long get_wchan(struct task_struct *p);
 | 
			
		||||
#define task_pt_regs(tsk) ((struct pt_regs *) \
 | 
			
		||||
| 
						 | 
				
			
			@ -324,6 +331,11 @@ extern void memcpy_absolute(void *, void *, size_t);
 | 
			
		|||
extern int s390_isolate_bp(void);
 | 
			
		||||
extern int s390_isolate_bp_guest(void);
 | 
			
		||||
 | 
			
		||||
static __always_inline bool regs_irqs_disabled(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	return arch_irqs_disabled_flags(regs->psw.mask);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* __ASSEMBLY__ */
 | 
			
		||||
 | 
			
		||||
#endif /* __ASM_S390_PROCESSOR_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,13 +11,13 @@
 | 
			
		|||
#include <uapi/asm/ptrace.h>
 | 
			
		||||
 | 
			
		||||
#define PIF_SYSCALL		0	/* inside a system call */
 | 
			
		||||
#define PIF_PER_TRAP		1	/* deliver sigtrap on return to user */
 | 
			
		||||
#define PIF_SYSCALL_RESTART	2	/* restart the current system call */
 | 
			
		||||
#define PIF_SYSCALL_RESTART	1	/* restart the current system call */
 | 
			
		||||
#define PIF_SYSCALL_RET_SET	2	/* return value was set via ptrace */
 | 
			
		||||
#define PIF_GUEST_FAULT		3	/* indicates program check in sie64a */
 | 
			
		||||
 | 
			
		||||
#define _PIF_SYSCALL		BIT(PIF_SYSCALL)
 | 
			
		||||
#define _PIF_PER_TRAP		BIT(PIF_PER_TRAP)
 | 
			
		||||
#define _PIF_SYSCALL_RESTART	BIT(PIF_SYSCALL_RESTART)
 | 
			
		||||
#define _PIF_SYSCALL_RET_SET	BIT(PIF_SYSCALL_RET_SET)
 | 
			
		||||
#define _PIF_GUEST_FAULT	BIT(PIF_GUEST_FAULT)
 | 
			
		||||
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +68,9 @@ enum {
 | 
			
		|||
	&(*(struct psw_bits *)(&(__psw)));	\
 | 
			
		||||
}))
 | 
			
		||||
 | 
			
		||||
#define PGM_INT_CODE_MASK	0x7f
 | 
			
		||||
#define PGM_INT_CODE_PER	0x80
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The pt_regs struct defines the way the registers are stored on
 | 
			
		||||
 * the stack during a system call.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,8 +14,8 @@
 | 
			
		|||
#include <linux/err.h>
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
 | 
			
		||||
extern const unsigned long sys_call_table[];
 | 
			
		||||
extern const unsigned long sys_call_table_emu[];
 | 
			
		||||
extern const sys_call_ptr_t sys_call_table[];
 | 
			
		||||
extern const sys_call_ptr_t sys_call_table_emu[];
 | 
			
		||||
 | 
			
		||||
static inline long syscall_get_nr(struct task_struct *task,
 | 
			
		||||
				  struct pt_regs *regs)
 | 
			
		||||
| 
						 | 
				
			
			@ -56,6 +56,7 @@ static inline void syscall_set_return_value(struct task_struct *task,
 | 
			
		|||
					    struct pt_regs *regs,
 | 
			
		||||
					    int error, long val)
 | 
			
		||||
{
 | 
			
		||||
	set_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
 | 
			
		||||
	regs->gprs[2] = error ? error : val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -97,4 +98,10 @@ static inline int syscall_get_arch(struct task_struct *task)
 | 
			
		|||
#endif
 | 
			
		||||
	return AUDIT_ARCH_S390X;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif	/* _ASM_SYSCALL_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,6 +36,7 @@
 | 
			
		|||
 */
 | 
			
		||||
struct thread_info {
 | 
			
		||||
	unsigned long		flags;		/* low level flags */
 | 
			
		||||
	unsigned long		syscall_work;	/* SYSCALL_WORK_ flags */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +69,7 @@ void arch_setup_new_exec(void);
 | 
			
		|||
#define TIF_NOTIFY_SIGNAL	7	/* signal notifications exist */
 | 
			
		||||
#define TIF_ISOLATE_BP		8	/* Run process with isolated BP */
 | 
			
		||||
#define TIF_ISOLATE_BP_GUEST	9	/* Run KVM guests with isolated BP */
 | 
			
		||||
#define TIF_PER_TRAP		10	/* Need to handle PER trap on exit to usermode */
 | 
			
		||||
 | 
			
		||||
#define TIF_31BIT		16	/* 32bit process */
 | 
			
		||||
#define TIF_MEMDIE		17	/* is terminating due to OOM killer */
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +93,7 @@ void arch_setup_new_exec(void);
 | 
			
		|||
#define _TIF_PATCH_PENDING	BIT(TIF_PATCH_PENDING)
 | 
			
		||||
#define _TIF_ISOLATE_BP		BIT(TIF_ISOLATE_BP)
 | 
			
		||||
#define _TIF_ISOLATE_BP_GUEST	BIT(TIF_ISOLATE_BP_GUEST)
 | 
			
		||||
#define _TIF_PER_TRAP		BIT(TIF_PER_TRAP)
 | 
			
		||||
 | 
			
		||||
#define _TIF_31BIT		BIT(TIF_31BIT)
 | 
			
		||||
#define _TIF_SINGLE_STEP	BIT(TIF_SINGLE_STEP)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,7 +18,7 @@
 | 
			
		|||
#include <asm/extable.h>
 | 
			
		||||
#include <asm/facility.h>
 | 
			
		||||
 | 
			
		||||
void debug_user_asce(void);
 | 
			
		||||
void debug_user_asce(int exit);
 | 
			
		||||
 | 
			
		||||
static inline int __range_ok(unsigned long addr, unsigned long size)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,4 +4,18 @@
 | 
			
		|||
 | 
			
		||||
#define __ARCH_HAS_VTIME_TASK_SWITCH
 | 
			
		||||
 | 
			
		||||
static inline void update_timer_sys(void)
 | 
			
		||||
{
 | 
			
		||||
	S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer;
 | 
			
		||||
	S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.sys_enter_timer;
 | 
			
		||||
	S390_lowcore.last_update_timer = S390_lowcore.sys_enter_timer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void update_timer_mcck(void)
 | 
			
		||||
{
 | 
			
		||||
	S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer;
 | 
			
		||||
	S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.mcck_enter_timer;
 | 
			
		||||
	S390_lowcore.last_update_timer = S390_lowcore.mcck_enter_timer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif /* _S390_VTIME_H */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,7 +180,8 @@
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
#define PTRACE_OLDSETOPTIONS		21
 | 
			
		||||
 | 
			
		||||
#define PTRACE_SYSEMU			31
 | 
			
		||||
#define PTRACE_SYSEMU_SINGLESTEP	32
 | 
			
		||||
#ifndef __ASSEMBLY__
 | 
			
		||||
#include <linux/stddef.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ CFLAGS_dumpstack.o	+= -fno-optimize-sibling-calls
 | 
			
		|||
CFLAGS_unwind_bc.o	+= -fno-optimize-sibling-calls
 | 
			
		||||
 | 
			
		||||
obj-y	:= traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
 | 
			
		||||
obj-y	+= processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
 | 
			
		||||
obj-y	+= processor.o syscall.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
 | 
			
		||||
obj-y	+= debug.o irq.o ipl.o dis.o diag.o vdso.o
 | 
			
		||||
obj-y	+= sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o
 | 
			
		||||
obj-y	+= runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,26 +26,14 @@ int main(void)
 | 
			
		|||
	BLANK();
 | 
			
		||||
	/* thread struct offsets */
 | 
			
		||||
	OFFSET(__THREAD_ksp, thread_struct, ksp);
 | 
			
		||||
	OFFSET(__THREAD_sysc_table,  thread_struct, sys_call_table);
 | 
			
		||||
	OFFSET(__THREAD_last_break, thread_struct, last_break);
 | 
			
		||||
	OFFSET(__THREAD_FPU_fpc, thread_struct, fpu.fpc);
 | 
			
		||||
	OFFSET(__THREAD_FPU_regs, thread_struct, fpu.regs);
 | 
			
		||||
	OFFSET(__THREAD_per_cause, thread_struct, per_event.cause);
 | 
			
		||||
	OFFSET(__THREAD_per_address, thread_struct, per_event.address);
 | 
			
		||||
	OFFSET(__THREAD_per_paid, thread_struct, per_event.paid);
 | 
			
		||||
	OFFSET(__THREAD_trap_tdb, thread_struct, trap_tdb);
 | 
			
		||||
	BLANK();
 | 
			
		||||
	/* thread info offsets */
 | 
			
		||||
	OFFSET(__TI_flags, task_struct, thread_info.flags);
 | 
			
		||||
	BLANK();
 | 
			
		||||
	/* pt_regs offsets */
 | 
			
		||||
	OFFSET(__PT_ARGS, pt_regs, args);
 | 
			
		||||
	OFFSET(__PT_PSW, pt_regs, psw);
 | 
			
		||||
	OFFSET(__PT_GPRS, pt_regs, gprs);
 | 
			
		||||
	OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2);
 | 
			
		||||
	OFFSET(__PT_INT_CODE, pt_regs, int_code);
 | 
			
		||||
	OFFSET(__PT_INT_PARM, pt_regs, int_parm);
 | 
			
		||||
	OFFSET(__PT_INT_PARM_LONG, pt_regs, int_parm_long);
 | 
			
		||||
	OFFSET(__PT_FLAGS, pt_regs, flags);
 | 
			
		||||
	OFFSET(__PT_CR1, pt_regs, cr1);
 | 
			
		||||
	DEFINE(__PT_SIZE, sizeof(struct pt_regs));
 | 
			
		||||
| 
						 | 
				
			
			@ -64,6 +52,7 @@ int main(void)
 | 
			
		|||
	OFFSET(__CLOCK_IDLE_EXIT, s390_idle_data, clock_idle_exit);
 | 
			
		||||
	OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter);
 | 
			
		||||
	OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit);
 | 
			
		||||
	OFFSET(__MT_CYCLES_ENTER, s390_idle_data, mt_cycles_enter);
 | 
			
		||||
	BLANK();
 | 
			
		||||
	/* hardware defined lowcore locations 0x000 - 0x1ff */
 | 
			
		||||
	OFFSET(__LC_EXT_PARAMS, lowcore, ext_params);
 | 
			
		||||
| 
						 | 
				
			
			@ -115,13 +104,9 @@ int main(void)
 | 
			
		|||
	OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags);
 | 
			
		||||
	OFFSET(__LC_RETURN_PSW, lowcore, return_psw);
 | 
			
		||||
	OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw);
 | 
			
		||||
	OFFSET(__LC_SYNC_ENTER_TIMER, lowcore, sync_enter_timer);
 | 
			
		||||
	OFFSET(__LC_ASYNC_ENTER_TIMER, lowcore, async_enter_timer);
 | 
			
		||||
	OFFSET(__LC_SYS_ENTER_TIMER, lowcore, sys_enter_timer);
 | 
			
		||||
	OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer);
 | 
			
		||||
	OFFSET(__LC_EXIT_TIMER, lowcore, exit_timer);
 | 
			
		||||
	OFFSET(__LC_USER_TIMER, lowcore, user_timer);
 | 
			
		||||
	OFFSET(__LC_SYSTEM_TIMER, lowcore, system_timer);
 | 
			
		||||
	OFFSET(__LC_STEAL_TIMER, lowcore, steal_timer);
 | 
			
		||||
	OFFSET(__LC_LAST_UPDATE_TIMER, lowcore, last_update_timer);
 | 
			
		||||
	OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
 | 
			
		||||
	OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,6 +118,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
 | 
			
		|||
	fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, ¤t->thread.fpu);
 | 
			
		||||
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -17,8 +17,9 @@ void io_int_handler(void);
 | 
			
		|||
void mcck_int_handler(void);
 | 
			
		||||
void restart_int_handler(void);
 | 
			
		||||
 | 
			
		||||
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs);
 | 
			
		||||
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
 | 
			
		||||
void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs);
 | 
			
		||||
void __do_pgm_check(struct pt_regs *regs);
 | 
			
		||||
void __do_syscall(struct pt_regs *regs, int per_trap);
 | 
			
		||||
 | 
			
		||||
void do_protection_exception(struct pt_regs *regs);
 | 
			
		||||
void do_dat_exception(struct pt_regs *regs);
 | 
			
		||||
| 
						 | 
				
			
			@ -48,9 +49,7 @@ void translation_exception(struct pt_regs *regs);
 | 
			
		|||
void vector_exception(struct pt_regs *regs);
 | 
			
		||||
void monitor_event_exception(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
void do_per_trap(struct pt_regs *regs);
 | 
			
		||||
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str);
 | 
			
		||||
void syscall_trace(struct pt_regs *regs, int entryexit);
 | 
			
		||||
void kernel_stack_overflow(struct pt_regs * regs);
 | 
			
		||||
void do_signal(struct pt_regs *regs);
 | 
			
		||||
void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +57,8 @@ void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
 | 
			
		|||
void do_notify_resume(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
void __init init_IRQ(void);
 | 
			
		||||
void do_IRQ(struct pt_regs *regs, int irq);
 | 
			
		||||
void do_io_irq(struct pt_regs *regs);
 | 
			
		||||
void do_ext_irq(struct pt_regs *regs);
 | 
			
		||||
void do_restart(void);
 | 
			
		||||
void __init startup_init(void);
 | 
			
		||||
void die(struct pt_regs *regs, const char *str);
 | 
			
		||||
| 
						 | 
				
			
			@ -82,8 +82,6 @@ long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user
 | 
			
		|||
 | 
			
		||||
DECLARE_PER_CPU(u64, mt_cycles[8]);
 | 
			
		||||
 | 
			
		||||
void gs_load_bc_cb(struct pt_regs *regs);
 | 
			
		||||
 | 
			
		||||
unsigned long stack_alloc(void);
 | 
			
		||||
void stack_free(unsigned long stack);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -175,3 +175,91 @@ void __kernel_fpu_end(struct kernel_fpu *state, u32 flags)
 | 
			
		|||
		: "1", "cc");
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(__kernel_fpu_end);
 | 
			
		||||
 | 
			
		||||
void __load_fpu_regs(void)
 | 
			
		||||
{
 | 
			
		||||
	struct fpu *state = ¤t->thread.fpu;
 | 
			
		||||
	unsigned long *regs = current->thread.fpu.regs;
 | 
			
		||||
 | 
			
		||||
	asm volatile("lfpc %0" : : "Q" (state->fpc));
 | 
			
		||||
	if (likely(MACHINE_HAS_VX)) {
 | 
			
		||||
		asm volatile("lgr	1,%0\n"
 | 
			
		||||
			     "VLM	0,15,0,1\n"
 | 
			
		||||
			     "VLM	16,31,256,1\n"
 | 
			
		||||
			     :
 | 
			
		||||
			     : "d" (regs)
 | 
			
		||||
			     : "1", "cc", "memory");
 | 
			
		||||
	} else {
 | 
			
		||||
		asm volatile("ld 0,%0" : : "Q" (regs[0]));
 | 
			
		||||
		asm volatile("ld 1,%0" : : "Q" (regs[1]));
 | 
			
		||||
		asm volatile("ld 2,%0" : : "Q" (regs[2]));
 | 
			
		||||
		asm volatile("ld 3,%0" : : "Q" (regs[3]));
 | 
			
		||||
		asm volatile("ld 4,%0" : : "Q" (regs[4]));
 | 
			
		||||
		asm volatile("ld 5,%0" : : "Q" (regs[5]));
 | 
			
		||||
		asm volatile("ld 6,%0" : : "Q" (regs[6]));
 | 
			
		||||
		asm volatile("ld 7,%0" : : "Q" (regs[7]));
 | 
			
		||||
		asm volatile("ld 8,%0" : : "Q" (regs[8]));
 | 
			
		||||
		asm volatile("ld 9,%0" : : "Q" (regs[9]));
 | 
			
		||||
		asm volatile("ld 10,%0" : : "Q" (regs[10]));
 | 
			
		||||
		asm volatile("ld 11,%0" : : "Q" (regs[11]));
 | 
			
		||||
		asm volatile("ld 12,%0" : : "Q" (regs[12]));
 | 
			
		||||
		asm volatile("ld 13,%0" : : "Q" (regs[13]));
 | 
			
		||||
		asm volatile("ld 14,%0" : : "Q" (regs[14]));
 | 
			
		||||
		asm volatile("ld 15,%0" : : "Q" (regs[15]));
 | 
			
		||||
	}
 | 
			
		||||
	clear_cpu_flag(CIF_FPU);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(__load_fpu_regs);
 | 
			
		||||
 | 
			
		||||
void load_fpu_regs(void)
 | 
			
		||||
{
 | 
			
		||||
	raw_local_irq_disable();
 | 
			
		||||
	__load_fpu_regs();
 | 
			
		||||
	raw_local_irq_enable();
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(load_fpu_regs);
 | 
			
		||||
 | 
			
		||||
void save_fpu_regs(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags, *regs;
 | 
			
		||||
	struct fpu *state;
 | 
			
		||||
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
 | 
			
		||||
	if (test_cpu_flag(CIF_FPU))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	state = ¤t->thread.fpu;
 | 
			
		||||
	regs = current->thread.fpu.regs;
 | 
			
		||||
 | 
			
		||||
	asm volatile("stfpc %0" : "=Q" (state->fpc));
 | 
			
		||||
	if (likely(MACHINE_HAS_VX)) {
 | 
			
		||||
		asm volatile("lgr	1,%0\n"
 | 
			
		||||
			     "VSTM	0,15,0,1\n"
 | 
			
		||||
			     "VSTM	16,31,256,1\n"
 | 
			
		||||
			     :
 | 
			
		||||
			     : "d" (regs)
 | 
			
		||||
			     : "1", "cc", "memory");
 | 
			
		||||
	} else {
 | 
			
		||||
		asm volatile("std 0,%0" : "=Q" (regs[0]));
 | 
			
		||||
		asm volatile("std 1,%0" : "=Q" (regs[1]));
 | 
			
		||||
		asm volatile("std 2,%0" : "=Q" (regs[2]));
 | 
			
		||||
		asm volatile("std 3,%0" : "=Q" (regs[3]));
 | 
			
		||||
		asm volatile("std 4,%0" : "=Q" (regs[4]));
 | 
			
		||||
		asm volatile("std 5,%0" : "=Q" (regs[5]));
 | 
			
		||||
		asm volatile("std 6,%0" : "=Q" (regs[6]));
 | 
			
		||||
		asm volatile("std 7,%0" : "=Q" (regs[7]));
 | 
			
		||||
		asm volatile("std 8,%0" : "=Q" (regs[8]));
 | 
			
		||||
		asm volatile("std 9,%0" : "=Q" (regs[9]));
 | 
			
		||||
		asm volatile("std 10,%0" : "=Q" (regs[10]));
 | 
			
		||||
		asm volatile("std 11,%0" : "=Q" (regs[11]));
 | 
			
		||||
		asm volatile("std 12,%0" : "=Q" (regs[12]));
 | 
			
		||||
		asm volatile("std 13,%0" : "=Q" (regs[13]));
 | 
			
		||||
		asm volatile("std 14,%0" : "=Q" (regs[14]));
 | 
			
		||||
		asm volatile("std 15,%0" : "=Q" (regs[15]));
 | 
			
		||||
	}
 | 
			
		||||
	set_cpu_flag(CIF_FPU);
 | 
			
		||||
out:
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(save_fpu_regs);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,12 +14,36 @@
 | 
			
		|||
#include <linux/cpu.h>
 | 
			
		||||
#include <linux/sched/cputime.h>
 | 
			
		||||
#include <trace/events/power.h>
 | 
			
		||||
#include <asm/cpu_mf.h>
 | 
			
		||||
#include <asm/nmi.h>
 | 
			
		||||
#include <asm/smp.h>
 | 
			
		||||
#include "entry.h"
 | 
			
		||||
 | 
			
		||||
static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
 | 
			
		||||
 | 
			
		||||
void account_idle_time_irq(void)
 | 
			
		||||
{
 | 
			
		||||
	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
 | 
			
		||||
	u64 cycles_new[8];
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	clear_cpu_flag(CIF_ENABLED_WAIT);
 | 
			
		||||
	if (smp_cpu_mtid) {
 | 
			
		||||
		stcctm(MT_DIAG, smp_cpu_mtid, cycles_new);
 | 
			
		||||
		for (i = 0; i < smp_cpu_mtid; i++)
 | 
			
		||||
			this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	idle->clock_idle_exit = S390_lowcore.int_clock;
 | 
			
		||||
	idle->timer_idle_exit = S390_lowcore.sys_enter_timer;
 | 
			
		||||
 | 
			
		||||
	S390_lowcore.steal_timer += idle->clock_idle_enter - S390_lowcore.last_update_clock;
 | 
			
		||||
	S390_lowcore.last_update_clock = idle->clock_idle_exit;
 | 
			
		||||
 | 
			
		||||
	S390_lowcore.system_timer += S390_lowcore.last_update_timer - idle->timer_idle_enter;
 | 
			
		||||
	S390_lowcore.last_update_timer = idle->timer_idle_exit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void arch_cpu_idle(void)
 | 
			
		||||
{
 | 
			
		||||
	struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,7 @@
 | 
			
		|||
#include <linux/init.h>
 | 
			
		||||
#include <linux/cpu.h>
 | 
			
		||||
#include <linux/irq.h>
 | 
			
		||||
#include <linux/entry-common.h>
 | 
			
		||||
#include <asm/irq_regs.h>
 | 
			
		||||
#include <asm/cputime.h>
 | 
			
		||||
#include <asm/lowcore.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -95,19 +96,97 @@ static const struct irq_class irqclass_sub_desc[] = {
 | 
			
		|||
	{.irq = CPU_RST,    .name = "RST", .desc = "[CPU] CPU Restart"},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void do_IRQ(struct pt_regs *regs, int irq)
 | 
			
		||||
static void do_IRQ(struct pt_regs *regs, int irq)
 | 
			
		||||
{
 | 
			
		||||
	struct pt_regs *old_regs;
 | 
			
		||||
 | 
			
		||||
	old_regs = set_irq_regs(regs);
 | 
			
		||||
	irq_enter();
 | 
			
		||||
	if (tod_after_eq(S390_lowcore.int_clock,
 | 
			
		||||
			 S390_lowcore.clock_comparator))
 | 
			
		||||
		/* Serve timer interrupts first. */
 | 
			
		||||
		clock_comparator_work();
 | 
			
		||||
	generic_handle_irq(irq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int on_async_stack(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long frame = current_frame_address();
 | 
			
		||||
 | 
			
		||||
	return !!!((S390_lowcore.async_stack - frame) >> (PAGE_SHIFT + THREAD_SIZE_ORDER));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_irq_async(struct pt_regs *regs, int irq)
 | 
			
		||||
{
 | 
			
		||||
	if (on_async_stack())
 | 
			
		||||
		do_IRQ(regs, irq);
 | 
			
		||||
	else
 | 
			
		||||
		CALL_ON_STACK(do_IRQ, S390_lowcore.async_stack, 2, regs, irq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int irq_pending(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	int cc;
 | 
			
		||||
 | 
			
		||||
	asm volatile("tpi 0\n"
 | 
			
		||||
		     "ipm %0" : "=d" (cc) : : "cc");
 | 
			
		||||
	return cc >> 28;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void noinstr do_io_irq(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	irqentry_state_t state = irqentry_enter(regs);
 | 
			
		||||
	struct pt_regs *old_regs = set_irq_regs(regs);
 | 
			
		||||
	int from_idle;
 | 
			
		||||
 | 
			
		||||
	irq_enter();
 | 
			
		||||
 | 
			
		||||
	if (user_mode(regs))
 | 
			
		||||
		update_timer_sys();
 | 
			
		||||
 | 
			
		||||
	from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
 | 
			
		||||
	if (from_idle)
 | 
			
		||||
		account_idle_time_irq();
 | 
			
		||||
 | 
			
		||||
	do {
 | 
			
		||||
		memcpy(®s->int_code, &S390_lowcore.subchannel_id, 12);
 | 
			
		||||
		if (S390_lowcore.io_int_word & BIT(31))
 | 
			
		||||
			do_irq_async(regs, THIN_INTERRUPT);
 | 
			
		||||
		else
 | 
			
		||||
			do_irq_async(regs, IO_INTERRUPT);
 | 
			
		||||
	} while (MACHINE_IS_LPAR && irq_pending(regs));
 | 
			
		||||
 | 
			
		||||
	irq_exit();
 | 
			
		||||
	set_irq_regs(old_regs);
 | 
			
		||||
	irqentry_exit(regs, state);
 | 
			
		||||
 | 
			
		||||
	if (from_idle)
 | 
			
		||||
		regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void noinstr do_ext_irq(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	irqentry_state_t state = irqentry_enter(regs);
 | 
			
		||||
	struct pt_regs *old_regs = set_irq_regs(regs);
 | 
			
		||||
	int from_idle;
 | 
			
		||||
 | 
			
		||||
	irq_enter();
 | 
			
		||||
 | 
			
		||||
	if (user_mode(regs))
 | 
			
		||||
		update_timer_sys();
 | 
			
		||||
 | 
			
		||||
	memcpy(®s->int_code, &S390_lowcore.ext_cpu_addr, 4);
 | 
			
		||||
	regs->int_parm = S390_lowcore.ext_params;
 | 
			
		||||
	regs->int_parm_long = *(unsigned long *)S390_lowcore.ext_params2;
 | 
			
		||||
 | 
			
		||||
	from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
 | 
			
		||||
	if (from_idle)
 | 
			
		||||
		account_idle_time_irq();
 | 
			
		||||
 | 
			
		||||
	do_irq_async(regs, EXT_INTERRUPT);
 | 
			
		||||
 | 
			
		||||
	irq_exit();
 | 
			
		||||
	set_irq_regs(old_regs);
 | 
			
		||||
	irqentry_exit(regs, state);
 | 
			
		||||
 | 
			
		||||
	if (from_idle)
 | 
			
		||||
		regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void show_msi_interrupt(struct seq_file *p, int irq)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,12 +131,11 @@ static notrace void s390_handle_damage(void)
 | 
			
		|||
NOKPROBE_SYMBOL(s390_handle_damage);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Main machine check handler function. Will be called with interrupts enabled
 | 
			
		||||
 * or disabled and machine checks enabled or disabled.
 | 
			
		||||
 * Main machine check handler function. Will be called with interrupts disabled
 | 
			
		||||
 * and machine checks enabled.
 | 
			
		||||
 */
 | 
			
		||||
void s390_handle_mcck(void)
 | 
			
		||||
void __s390_handle_mcck(void)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	struct mcck_struct mcck;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
| 
						 | 
				
			
			@ -144,12 +143,10 @@ void s390_handle_mcck(void)
 | 
			
		|||
	 * machine checks. Afterwards delete the old state and enable machine
 | 
			
		||||
	 * checks again.
 | 
			
		||||
	 */
 | 
			
		||||
	local_irq_save(flags);
 | 
			
		||||
	local_mcck_disable();
 | 
			
		||||
	mcck = *this_cpu_ptr(&cpu_mcck);
 | 
			
		||||
	memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
 | 
			
		||||
	local_mcck_enable();
 | 
			
		||||
	local_irq_restore(flags);
 | 
			
		||||
 | 
			
		||||
	if (mcck.channel_report)
 | 
			
		||||
		crw_handle_channel_report();
 | 
			
		||||
| 
						 | 
				
			
			@ -181,8 +178,13 @@ void s390_handle_mcck(void)
 | 
			
		|||
		do_exit(SIGSEGV);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL_GPL(s390_handle_mcck);
 | 
			
		||||
 | 
			
		||||
void noinstr s390_handle_mcck(void)
 | 
			
		||||
{
 | 
			
		||||
	trace_hardirqs_off();
 | 
			
		||||
	__s390_handle_mcck();
 | 
			
		||||
	trace_hardirqs_on();
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * returns 0 if all required registers are available
 | 
			
		||||
 * returns 1 otherwise
 | 
			
		||||
| 
						 | 
				
			
			@ -344,6 +346,9 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
 | 
			
		|||
	int mcck_pending = 0;
 | 
			
		||||
 | 
			
		||||
	nmi_enter();
 | 
			
		||||
 | 
			
		||||
	if (user_mode(regs))
 | 
			
		||||
		update_timer_mcck();
 | 
			
		||||
	inc_irq_stat(NMI_NMI);
 | 
			
		||||
	mci.val = S390_lowcore.mcck_interruption_code;
 | 
			
		||||
	mcck = this_cpu_ptr(&cpu_mcck);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,7 @@
 | 
			
		|||
#include <linux/random.h>
 | 
			
		||||
#include <linux/export.h>
 | 
			
		||||
#include <linux/init_task.h>
 | 
			
		||||
#include <linux/entry-common.h>
 | 
			
		||||
#include <asm/cpu_mf.h>
 | 
			
		||||
#include <asm/io.h>
 | 
			
		||||
#include <asm/processor.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -43,9 +44,22 @@
 | 
			
		|||
#include <asm/unwind.h>
 | 
			
		||||
#include "entry.h"
 | 
			
		||||
 | 
			
		||||
asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
 | 
			
		||||
void ret_from_fork(void) asm("ret_from_fork");
 | 
			
		||||
 | 
			
		||||
extern void kernel_thread_starter(void);
 | 
			
		||||
void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	void (*func)(void *arg);
 | 
			
		||||
 | 
			
		||||
	schedule_tail(prev);
 | 
			
		||||
 | 
			
		||||
	if (!user_mode(regs)) {
 | 
			
		||||
		/* Kernel thread */
 | 
			
		||||
		func = (void *)regs->gprs[9];
 | 
			
		||||
		func((void *)regs->gprs[10]);
 | 
			
		||||
	}
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
	syscall_exit_to_user_mode(regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void flush_thread(void)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -108,10 +122,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
 | 
			
		|||
	p->thread.last_break = 1;
 | 
			
		||||
 | 
			
		||||
	frame->sf.back_chain = 0;
 | 
			
		||||
	frame->sf.gprs[5] = (unsigned long)frame + sizeof(struct stack_frame);
 | 
			
		||||
	frame->sf.gprs[6] = (unsigned long)p;
 | 
			
		||||
	/* new return point is ret_from_fork */
 | 
			
		||||
	frame->sf.gprs[8] = (unsigned long) ret_from_fork;
 | 
			
		||||
	frame->sf.gprs[8] = (unsigned long)ret_from_fork;
 | 
			
		||||
	/* fake return stack for resume(), don't go back to schedule */
 | 
			
		||||
	frame->sf.gprs[9] = (unsigned long) frame;
 | 
			
		||||
	frame->sf.gprs[9] = (unsigned long)frame;
 | 
			
		||||
 | 
			
		||||
	/* Store access registers to kernel stack of new process. */
 | 
			
		||||
	if (unlikely(p->flags & PF_KTHREAD)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -120,10 +136,10 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
 | 
			
		|||
		frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT |
 | 
			
		||||
				PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
 | 
			
		||||
		frame->childregs.psw.addr =
 | 
			
		||||
				(unsigned long) kernel_thread_starter;
 | 
			
		||||
				(unsigned long)__ret_from_fork;
 | 
			
		||||
		frame->childregs.gprs[9] = new_stackp; /* function */
 | 
			
		||||
		frame->childregs.gprs[10] = arg;
 | 
			
		||||
		frame->childregs.gprs[11] = (unsigned long) do_exit;
 | 
			
		||||
		frame->childregs.gprs[11] = (unsigned long)do_exit;
 | 
			
		||||
		frame->childregs.orig_gpr2 = -1;
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +169,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
asmlinkage void execve_tail(void)
 | 
			
		||||
void execve_tail(void)
 | 
			
		||||
{
 | 
			
		||||
	current->thread.fpu.fpc = 0;
 | 
			
		||||
	asm volatile("sfpc %0" : : "d" (0));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,6 +7,7 @@
 | 
			
		|||
 *               Martin Schwidefsky (schwidefsky@de.ibm.com)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "asm/ptrace.h"
 | 
			
		||||
#include <linux/kernel.h>
 | 
			
		||||
#include <linux/sched.h>
 | 
			
		||||
#include <linux/sched/task_stack.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -37,9 +38,6 @@
 | 
			
		|||
#include "compat_ptrace.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define CREATE_TRACE_POINTS
 | 
			
		||||
#include <trace/events/syscalls.h>
 | 
			
		||||
 | 
			
		||||
void update_cr_regs(struct task_struct *task)
 | 
			
		||||
{
 | 
			
		||||
	struct pt_regs *regs = task_pt_regs(task);
 | 
			
		||||
| 
						 | 
				
			
			@ -140,7 +138,7 @@ void ptrace_disable(struct task_struct *task)
 | 
			
		|||
	memset(&task->thread.per_user, 0, sizeof(task->thread.per_user));
 | 
			
		||||
	memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
 | 
			
		||||
	clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
 | 
			
		||||
	clear_pt_regs_flag(task_pt_regs(task), PIF_PER_TRAP);
 | 
			
		||||
	clear_tsk_thread_flag(task, TIF_PER_TRAP);
 | 
			
		||||
	task->thread.per_flags = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -322,25 +320,6 @@ static inline void __poke_user_per(struct task_struct *child,
 | 
			
		|||
		child->thread.per_user.end = data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fixup_int_code(struct task_struct *child, addr_t data)
 | 
			
		||||
{
 | 
			
		||||
	struct pt_regs *regs = task_pt_regs(child);
 | 
			
		||||
	int ilc = regs->int_code >> 16;
 | 
			
		||||
	u16 insn;
 | 
			
		||||
 | 
			
		||||
	if (ilc > 6)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (ptrace_access_vm(child, regs->psw.addr - (regs->int_code >> 16),
 | 
			
		||||
			&insn, sizeof(insn), FOLL_FORCE) != sizeof(insn))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	/* double check that tracee stopped on svc instruction */
 | 
			
		||||
	if ((insn >> 8) != 0xa)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	regs->int_code = 0x20000 | (data & 0xffff);
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * Write a word to the user area of a process at location addr. This
 | 
			
		||||
 * operation does have an additional problem compared to peek_user.
 | 
			
		||||
| 
						 | 
				
			
			@ -374,10 +353,12 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
 | 
			
		|||
		}
 | 
			
		||||
 | 
			
		||||
		if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
 | 
			
		||||
			addr == offsetof(struct user, regs.gprs[2]))
 | 
			
		||||
			fixup_int_code(child, data);
 | 
			
		||||
		*(addr_t *)((addr_t) ®s->psw + addr) = data;
 | 
			
		||||
			addr == offsetof(struct user, regs.gprs[2])) {
 | 
			
		||||
			struct pt_regs *regs = task_pt_regs(child);
 | 
			
		||||
 | 
			
		||||
			regs->int_code = 0x20000 | (data & 0xffff);
 | 
			
		||||
		}
 | 
			
		||||
		*(addr_t *)((addr_t) ®s->psw + addr) = data;
 | 
			
		||||
	} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * access registers are stored in the thread structure
 | 
			
		||||
| 
						 | 
				
			
			@ -742,10 +723,12 @@ static int __poke_user_compat(struct task_struct *child,
 | 
			
		|||
			regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
 | 
			
		||||
				(__u64)(tmp & PSW32_ADDR_AMODE);
 | 
			
		||||
		} else {
 | 
			
		||||
 | 
			
		||||
			if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
 | 
			
		||||
				addr == offsetof(struct compat_user, regs.gprs[2]))
 | 
			
		||||
				fixup_int_code(child, data);
 | 
			
		||||
				addr == offsetof(struct compat_user, regs.gprs[2])) {
 | 
			
		||||
				struct pt_regs *regs = task_pt_regs(child);
 | 
			
		||||
 | 
			
		||||
				regs->int_code = 0x20000 | (data & 0xffff);
 | 
			
		||||
			}
 | 
			
		||||
			/* gpr 0-15 */
 | 
			
		||||
			*(__u32*)((addr_t) ®s->psw + addr*2 + 4) = tmp;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -862,82 +845,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
 | 
			
		|||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long mask = -1UL;
 | 
			
		||||
	long ret = -1;
 | 
			
		||||
 | 
			
		||||
	if (is_compat_task())
 | 
			
		||||
		mask = 0xffffffff;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * The sysc_tracesys code in entry.S stored the system
 | 
			
		||||
	 * call number to gprs[2].
 | 
			
		||||
	 */
 | 
			
		||||
	if (test_thread_flag(TIF_SYSCALL_TRACE) &&
 | 
			
		||||
	    tracehook_report_syscall_entry(regs)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Tracing decided this syscall should not happen. Skip
 | 
			
		||||
		 * the system call and the system call restart handling.
 | 
			
		||||
		 */
 | 
			
		||||
		goto skip;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_SECCOMP
 | 
			
		||||
	/* Do the secure computing check after ptrace. */
 | 
			
		||||
	if (unlikely(test_thread_flag(TIF_SECCOMP))) {
 | 
			
		||||
		struct seccomp_data sd;
 | 
			
		||||
 | 
			
		||||
		if (is_compat_task()) {
 | 
			
		||||
			sd.instruction_pointer = regs->psw.addr & 0x7fffffff;
 | 
			
		||||
			sd.arch = AUDIT_ARCH_S390;
 | 
			
		||||
		} else {
 | 
			
		||||
			sd.instruction_pointer = regs->psw.addr;
 | 
			
		||||
			sd.arch = AUDIT_ARCH_S390X;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sd.nr = regs->int_code & 0xffff;
 | 
			
		||||
		sd.args[0] = regs->orig_gpr2 & mask;
 | 
			
		||||
		sd.args[1] = regs->gprs[3] & mask;
 | 
			
		||||
		sd.args[2] = regs->gprs[4] & mask;
 | 
			
		||||
		sd.args[3] = regs->gprs[5] & mask;
 | 
			
		||||
		sd.args[4] = regs->gprs[6] & mask;
 | 
			
		||||
		sd.args[5] = regs->gprs[7] & mask;
 | 
			
		||||
 | 
			
		||||
		if (__secure_computing(&sd) == -1)
 | 
			
		||||
			goto skip;
 | 
			
		||||
	}
 | 
			
		||||
#endif /* CONFIG_SECCOMP */
 | 
			
		||||
 | 
			
		||||
	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 | 
			
		||||
		trace_sys_enter(regs, regs->int_code & 0xffff);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	audit_syscall_entry(regs->int_code & 0xffff, regs->orig_gpr2 & mask,
 | 
			
		||||
			    regs->gprs[3] &mask, regs->gprs[4] &mask,
 | 
			
		||||
			    regs->gprs[5] &mask);
 | 
			
		||||
 | 
			
		||||
	if ((signed long)regs->gprs[2] >= NR_syscalls) {
 | 
			
		||||
		regs->gprs[2] = -ENOSYS;
 | 
			
		||||
		ret = -ENOSYS;
 | 
			
		||||
	}
 | 
			
		||||
	return regs->gprs[2];
 | 
			
		||||
skip:
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	audit_syscall_exit(regs);
 | 
			
		||||
 | 
			
		||||
	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 | 
			
		||||
		trace_sys_exit(regs, regs->gprs[2]);
 | 
			
		||||
 | 
			
		||||
	if (test_thread_flag(TIF_SYSCALL_TRACE))
 | 
			
		||||
		tracehook_report_syscall_exit(regs, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * user_regset definitions.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -411,8 +411,7 @@ static void __init setup_lowcore_dat_off(void)
 | 
			
		|||
	memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
 | 
			
		||||
	       sizeof(lc->alt_stfle_fac_list));
 | 
			
		||||
	nmi_alloc_boot_cpu(lc);
 | 
			
		||||
	lc->sync_enter_timer = S390_lowcore.sync_enter_timer;
 | 
			
		||||
	lc->async_enter_timer = S390_lowcore.async_enter_timer;
 | 
			
		||||
	lc->sys_enter_timer = S390_lowcore.sys_enter_timer;
 | 
			
		||||
	lc->exit_timer = S390_lowcore.exit_timer;
 | 
			
		||||
	lc->user_timer = S390_lowcore.user_timer;
 | 
			
		||||
	lc->system_timer = S390_lowcore.system_timer;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,6 +170,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
 | 
			
		|||
	fpregs_load(&user_sregs.fpregs, ¤t->thread.fpu);
 | 
			
		||||
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -459,7 +460,8 @@ static void handle_signal(struct ksignal *ksig, sigset_t *oldset,
 | 
			
		|||
 * the kernel can handle, and then we build all the user-level signal handling
 | 
			
		||||
 * stack-frames in one go after that.
 | 
			
		||||
 */
 | 
			
		||||
void do_signal(struct pt_regs *regs)
 | 
			
		||||
 | 
			
		||||
void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
 | 
			
		||||
{
 | 
			
		||||
	struct ksignal ksig;
 | 
			
		||||
	sigset_t *oldset = sigmask_to_save();
 | 
			
		||||
| 
						 | 
				
			
			@ -472,7 +474,7 @@ void do_signal(struct pt_regs *regs)
 | 
			
		|||
	current->thread.system_call =
 | 
			
		||||
		test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0;
 | 
			
		||||
 | 
			
		||||
	if (test_thread_flag(TIF_SIGPENDING) && get_signal(&ksig)) {
 | 
			
		||||
	if (has_signal && get_signal(&ksig)) {
 | 
			
		||||
		/* Whee!  Actually deliver the signal.  */
 | 
			
		||||
		if (current->thread.system_call) {
 | 
			
		||||
			regs->int_code = current->thread.system_call;
 | 
			
		||||
| 
						 | 
				
			
			@ -498,6 +500,7 @@ void do_signal(struct pt_regs *regs)
 | 
			
		|||
		}
 | 
			
		||||
		/* No longer in a system call */
 | 
			
		||||
		clear_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
		clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
 | 
			
		||||
		rseq_signal_deliver(&ksig, regs);
 | 
			
		||||
		if (is_compat_task())
 | 
			
		||||
			handle_signal32(&ksig, oldset, regs);
 | 
			
		||||
| 
						 | 
				
			
			@ -508,6 +511,7 @@ void do_signal(struct pt_regs *regs)
 | 
			
		|||
 | 
			
		||||
	/* No handlers present - check for system call restart */
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
 | 
			
		||||
	if (current->thread.system_call) {
 | 
			
		||||
		regs->int_code = current->thread.system_call;
 | 
			
		||||
		switch (regs->gprs[2]) {
 | 
			
		||||
| 
						 | 
				
			
			@ -520,9 +524,9 @@ void do_signal(struct pt_regs *regs)
 | 
			
		|||
		case -ERESTARTNOINTR:
 | 
			
		||||
			/* Restart system call with magic TIF bit. */
 | 
			
		||||
			regs->gprs[2] = regs->orig_gpr2;
 | 
			
		||||
			set_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
			set_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
 | 
			
		||||
			if (test_thread_flag(TIF_SINGLE_STEP))
 | 
			
		||||
				clear_pt_regs_flag(regs, PIF_PER_TRAP);
 | 
			
		||||
				clear_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -499,7 +499,7 @@ static void smp_handle_ext_call(void)
 | 
			
		|||
	if (test_bit(ec_call_function_single, &bits))
 | 
			
		||||
		generic_smp_call_function_single_interrupt();
 | 
			
		||||
	if (test_bit(ec_mcck_pending, &bits))
 | 
			
		||||
		s390_handle_mcck();
 | 
			
		||||
		__s390_handle_mcck();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_ext_call_interrupt(struct ext_code ext_code,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,13 @@
 | 
			
		|||
#include <linux/unistd.h>
 | 
			
		||||
#include <linux/ipc.h>
 | 
			
		||||
#include <linux/uaccess.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/thread_info.h>
 | 
			
		||||
#include <linux/entry-common.h>
 | 
			
		||||
 | 
			
		||||
#include <asm/ptrace.h>
 | 
			
		||||
#include <asm/vtime.h>
 | 
			
		||||
 | 
			
		||||
#include "entry.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -100,3 +107,66 @@ SYSCALL_DEFINE0(ni_syscall)
 | 
			
		|||
{
 | 
			
		||||
	return -ENOSYS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void do_syscall(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long nr;
 | 
			
		||||
 | 
			
		||||
	nr = regs->int_code & 0xffff;
 | 
			
		||||
	if (!nr) {
 | 
			
		||||
		nr = regs->gprs[1] & 0xffff;
 | 
			
		||||
		regs->int_code &= ~0xffffUL;
 | 
			
		||||
		regs->int_code |= nr;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regs->gprs[2] = nr;
 | 
			
		||||
 | 
			
		||||
	nr = syscall_enter_from_user_mode_work(regs, nr);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * In the s390 ptrace ABI, both the syscall number and the return value
 | 
			
		||||
	 * use gpr2. However, userspace puts the syscall number either in the
 | 
			
		||||
	 * svc instruction itself, or uses gpr1. To make at least skipping syscalls
 | 
			
		||||
	 * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
 | 
			
		||||
	 * and if set, the syscall will be skipped.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!test_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)) {
 | 
			
		||||
		regs->gprs[2] = -ENOSYS;
 | 
			
		||||
		if (likely(nr < NR_syscalls)) {
 | 
			
		||||
			regs->gprs[2] = current->thread.sys_call_table[nr](
 | 
			
		||||
					regs->orig_gpr2, regs->gprs[3],
 | 
			
		||||
					regs->gprs[4], regs->gprs[5],
 | 
			
		||||
					regs->gprs[6], regs->gprs[7]);
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
 | 
			
		||||
	}
 | 
			
		||||
	syscall_exit_to_user_mode_work(regs);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
 | 
			
		||||
{
 | 
			
		||||
	enter_from_user_mode(regs);
 | 
			
		||||
 | 
			
		||||
	memcpy(®s->gprs[8], S390_lowcore.save_area_sync, 8 * sizeof(unsigned long));
 | 
			
		||||
	memcpy(®s->int_code, &S390_lowcore.svc_ilc, sizeof(regs->int_code));
 | 
			
		||||
	regs->psw = S390_lowcore.svc_old_psw;
 | 
			
		||||
 | 
			
		||||
	update_timer_sys();
 | 
			
		||||
 | 
			
		||||
	local_irq_enable();
 | 
			
		||||
	regs->orig_gpr2 = regs->gprs[2];
 | 
			
		||||
 | 
			
		||||
	if (per_trap)
 | 
			
		||||
		set_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
 | 
			
		||||
	for (;;) {
 | 
			
		||||
		regs->flags = 0;
 | 
			
		||||
		set_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
		do_syscall(regs);
 | 
			
		||||
		if (!test_pt_regs_flag(regs, PIF_SYSCALL_RESTART))
 | 
			
		||||
			break;
 | 
			
		||||
		local_irq_enable();
 | 
			
		||||
	}
 | 
			
		||||
	exit_to_user_mode();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,8 @@
 | 
			
		|||
 * 'Traps.c' handles hardware traps and faults after we have saved some
 | 
			
		||||
 * state in 'asm.s'.
 | 
			
		||||
 */
 | 
			
		||||
#include "asm/irqflags.h"
 | 
			
		||||
#include "asm/ptrace.h"
 | 
			
		||||
#include <linux/kprobes.h>
 | 
			
		||||
#include <linux/kdebug.h>
 | 
			
		||||
#include <linux/extable.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +25,9 @@
 | 
			
		|||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/uaccess.h>
 | 
			
		||||
#include <linux/cpu.h>
 | 
			
		||||
#include <linux/entry-common.h>
 | 
			
		||||
#include <asm/fpu/api.h>
 | 
			
		||||
#include <asm/vtime.h>
 | 
			
		||||
#include "entry.h"
 | 
			
		||||
 | 
			
		||||
static inline void __user *get_trap_ip(struct pt_regs *regs)
 | 
			
		||||
| 
						 | 
				
			
			@ -288,3 +292,64 @@ void __init trap_init(void)
 | 
			
		|||
	local_mcck_enable();
 | 
			
		||||
	test_monitor_call();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void noinstr __do_pgm_check(struct pt_regs *regs)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long last_break = S390_lowcore.breaking_event_addr;
 | 
			
		||||
	unsigned int trapnr, syscall_redirect = 0;
 | 
			
		||||
	irqentry_state_t state;
 | 
			
		||||
 | 
			
		||||
	regs->int_code = *(u32 *)&S390_lowcore.pgm_ilc;
 | 
			
		||||
	regs->int_parm_long = S390_lowcore.trans_exc_code;
 | 
			
		||||
 | 
			
		||||
	state = irqentry_enter(regs);
 | 
			
		||||
 | 
			
		||||
	if (user_mode(regs)) {
 | 
			
		||||
		update_timer_sys();
 | 
			
		||||
		if (last_break < 4096)
 | 
			
		||||
			last_break = 1;
 | 
			
		||||
		current->thread.last_break = last_break;
 | 
			
		||||
		regs->args[0] = last_break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (S390_lowcore.pgm_code & 0x0200) {
 | 
			
		||||
		/* transaction abort */
 | 
			
		||||
		memcpy(¤t->thread.trap_tdb, &S390_lowcore.pgm_tdb, 256);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (S390_lowcore.pgm_code & PGM_INT_CODE_PER) {
 | 
			
		||||
		if (user_mode(regs)) {
 | 
			
		||||
			struct per_event *ev = ¤t->thread.per_event;
 | 
			
		||||
 | 
			
		||||
			set_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
			ev->address = S390_lowcore.per_address;
 | 
			
		||||
			ev->cause = *(u16 *)&S390_lowcore.per_code;
 | 
			
		||||
			ev->paid = S390_lowcore.per_access_id;
 | 
			
		||||
		} else {
 | 
			
		||||
			/* PER event in kernel is kprobes */
 | 
			
		||||
			__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
 | 
			
		||||
			do_per_trap(regs);
 | 
			
		||||
			goto out;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!irqs_disabled_flags(regs->psw.mask))
 | 
			
		||||
		trace_hardirqs_on();
 | 
			
		||||
	__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
 | 
			
		||||
 | 
			
		||||
	trapnr = regs->int_code & PGM_INT_CODE_MASK;
 | 
			
		||||
	if (trapnr)
 | 
			
		||||
		pgm_check_table[trapnr](regs);
 | 
			
		||||
	syscall_redirect = user_mode(regs) && test_pt_regs_flag(regs, PIF_SYSCALL);
 | 
			
		||||
out:
 | 
			
		||||
	local_irq_disable();
 | 
			
		||||
	irqentry_exit(regs, state);
 | 
			
		||||
 | 
			
		||||
	if (syscall_redirect) {
 | 
			
		||||
		enter_from_user_mode(regs);
 | 
			
		||||
		local_irq_enable();
 | 
			
		||||
		regs->orig_gpr2 = regs->gprs[2];
 | 
			
		||||
		do_syscall(regs);
 | 
			
		||||
		exit_to_user_mode();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 | 
			
		|||
		return -EINVAL;
 | 
			
		||||
	if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_PER_TRAP);
 | 
			
		||||
	clear_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
	auprobe->saved_per = psw_bits(regs->psw).per;
 | 
			
		||||
	auprobe->saved_int_code = regs->int_code;
 | 
			
		||||
	regs->int_code = UPROBE_TRAP_NR;
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 | 
			
		|||
		/* fix per address */
 | 
			
		||||
		current->thread.per_event.address = utask->vaddr;
 | 
			
		||||
		/* trigger per event */
 | 
			
		||||
		set_pt_regs_flag(regs, PIF_PER_TRAP);
 | 
			
		||||
		set_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -259,7 +259,7 @@ static void sim_stor_event(struct pt_regs *regs, void *addr, int len)
 | 
			
		|||
		return;
 | 
			
		||||
	current->thread.per_event.address = regs->psw.addr;
 | 
			
		||||
	current->thread.per_event.cause = PER_EVENT_STORE >> 16;
 | 
			
		||||
	set_pt_regs_flag(regs, PIF_PER_TRAP);
 | 
			
		||||
	set_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,7 @@
 | 
			
		|||
#include <asm/timex.h>
 | 
			
		||||
#include <asm/ap.h>
 | 
			
		||||
#include <asm/uv.h>
 | 
			
		||||
#include <asm/fpu/api.h>
 | 
			
		||||
#include "kvm-s390.h"
 | 
			
		||||
#include "gaccess.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4147,6 +4148,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
 | 
			
		|||
			       vcpu->run->s.regs.gprs,
 | 
			
		||||
			       sizeof(sie_page->pv_grregs));
 | 
			
		||||
		}
 | 
			
		||||
		if (test_cpu_flag(CIF_FPU))
 | 
			
		||||
			load_fpu_regs();
 | 
			
		||||
		exit_reason = sie64a(vcpu->arch.sie_block,
 | 
			
		||||
				     vcpu->run->s.regs.gprs);
 | 
			
		||||
		if (kvm_s390_pv_cpu_is_protected(vcpu)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,7 @@
 | 
			
		|||
#include <asm/sclp.h>
 | 
			
		||||
#include <asm/nmi.h>
 | 
			
		||||
#include <asm/dis.h>
 | 
			
		||||
#include <asm/fpu/api.h>
 | 
			
		||||
#include "kvm-s390.h"
 | 
			
		||||
#include "gaccess.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1028,6 +1029,8 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
 | 
			
		|||
	 */
 | 
			
		||||
	vcpu->arch.sie_block->prog0c |= PROG_IN_SIE;
 | 
			
		||||
	barrier();
 | 
			
		||||
	if (test_cpu_flag(CIF_FPU))
 | 
			
		||||
		load_fpu_regs();
 | 
			
		||||
	if (!kvm_s390_vcpu_sie_inhibited(vcpu))
 | 
			
		||||
		rc = sie64a(scb_s, vcpu->run->s.regs.gprs);
 | 
			
		||||
	barrier();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,8 @@
 | 
			
		|||
#include <asm/mmu_context.h>
 | 
			
		||||
#include <asm/facility.h>
 | 
			
		||||
 | 
			
		||||
#ifdef CONFIG_DEBUG_USER_ASCE
 | 
			
		||||
void debug_user_asce(void)
 | 
			
		||||
#ifdef CONFIG_DEBUG_ENTRY
 | 
			
		||||
void debug_user_asce(int exit)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long cr1, cr7;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -25,12 +25,14 @@ void debug_user_asce(void)
 | 
			
		|||
	__ctl_store(cr7, 7, 7);
 | 
			
		||||
	if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce)
 | 
			
		||||
		return;
 | 
			
		||||
	panic("incorrect ASCE on kernel exit\n"
 | 
			
		||||
	panic("incorrect ASCE on kernel %s\n"
 | 
			
		||||
	      "cr1:    %016lx cr7:  %016lx\n"
 | 
			
		||||
	      "kernel: %016llx user: %016llx\n",
 | 
			
		||||
	      cr1, cr7, S390_lowcore.kernel_asce, S390_lowcore.user_asce);
 | 
			
		||||
	      exit ? "exit" : "entry", cr1, cr7,
 | 
			
		||||
	      S390_lowcore.kernel_asce, S390_lowcore.user_asce);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
#endif /*CONFIG_DEBUG_USER_ASCE */
 | 
			
		||||
#endif /*CONFIG_DEBUG_ENTRY */
 | 
			
		||||
 | 
			
		||||
#ifndef CONFIG_HAVE_MARCH_Z10_FEATURES
 | 
			
		||||
static DEFINE_STATIC_KEY_FALSE(have_mvcos);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -385,7 +385,7 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
 | 
			
		|||
	 * The instruction that caused the program check has
 | 
			
		||||
	 * been nullified. Don't signal single step via SIGTRAP.
 | 
			
		||||
	 */
 | 
			
		||||
	clear_pt_regs_flag(regs, PIF_PER_TRAP);
 | 
			
		||||
	clear_thread_flag(TIF_PER_TRAP);
 | 
			
		||||
 | 
			
		||||
	if (kprobe_page_fault(regs, 14))
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue