Commit 56e62a73 authored by Sven Schnelle's avatar Sven Schnelle Committed by Vasily Gorbik

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: default avatarSven Schnelle <svens@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent ac94a291
...@@ -123,6 +123,7 @@ config S390 ...@@ -123,6 +123,7 @@ config S390
select GENERIC_ALLOCATOR select GENERIC_ALLOCATOR
select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_VULNERABILITIES select GENERIC_CPU_VULNERABILITIES
select GENERIC_ENTRY
select GENERIC_FIND_FIRST_BIT select GENERIC_FIND_FIRST_BIT
select GENERIC_GETTIMEOFDAY select GENERIC_GETTIMEOFDAY
select GENERIC_PTDUMP select GENERIC_PTDUMP
......
...@@ -6,10 +6,12 @@ config TRACE_IRQFLAGS_SUPPORT ...@@ -6,10 +6,12 @@ config TRACE_IRQFLAGS_SUPPORT
config EARLY_PRINTK config EARLY_PRINTK
def_bool y def_bool y
config DEBUG_USER_ASCE config DEBUG_ENTRY
bool "Debug User ASCE" bool "Debug low-level entry code"
depends on DEBUG_KERNEL
help help
Check on exit to user space that address space control This option enables sanity checks in s390 low-level entry code.
elements are setup correctly. Some of these sanity checks may slow down kernel entries and
exits or otherwise impact performance.
If unsure, say N. If unsure, say N.
...@@ -833,7 +833,6 @@ CONFIG_BPF_KPROBE_OVERRIDE=y ...@@ -833,7 +833,6 @@ CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y CONFIG_HIST_TRIGGERS=y
CONFIG_FTRACE_STARTUP_TEST=y CONFIG_FTRACE_STARTUP_TEST=y
# CONFIG_EVENT_TRACE_STARTUP_TEST is not set # CONFIG_EVENT_TRACE_STARTUP_TEST is not set
CONFIG_DEBUG_USER_ASCE=y
CONFIG_NOTIFIER_ERROR_INJECTION=m CONFIG_NOTIFIER_ERROR_INJECTION=m
CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m
CONFIG_FAULT_INJECTION=y CONFIG_FAULT_INJECTION=y
...@@ -857,3 +856,4 @@ CONFIG_PERCPU_TEST=m ...@@ -857,3 +856,4 @@ CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y CONFIG_ATOMIC64_SELFTEST=y
CONFIG_TEST_BITOPS=m CONFIG_TEST_BITOPS=m
CONFIG_TEST_BPF=m CONFIG_TEST_BPF=m
CONFIG_DEBUG_ENTRY=y
...@@ -781,7 +781,6 @@ CONFIG_FTRACE_SYSCALLS=y ...@@ -781,7 +781,6 @@ CONFIG_FTRACE_SYSCALLS=y
CONFIG_BLK_DEV_IO_TRACE=y CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y CONFIG_HIST_TRIGGERS=y
CONFIG_DEBUG_USER_ASCE=y
CONFIG_LKDTM=m CONFIG_LKDTM=m
CONFIG_PERCPU_TEST=m CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y CONFIG_ATOMIC64_SELFTEST=y
......
...@@ -35,4 +35,6 @@ u64 arch_cpu_idle_time(int cpu); ...@@ -35,4 +35,6 @@ u64 arch_cpu_idle_time(int cpu);
#define arch_idle_time(cpu) arch_cpu_idle_time(cpu) #define arch_idle_time(cpu) arch_cpu_idle_time(cpu)
void account_idle_time_irq(void);
#endif /* _S390_CPUTIME_H */ #endif /* _S390_CPUTIME_H */
...@@ -233,8 +233,7 @@ extern char elf_platform[]; ...@@ -233,8 +233,7 @@ extern char elf_platform[];
do { \ do { \
set_personality(PER_LINUX | \ set_personality(PER_LINUX | \
(current->personality & (~PER_MASK))); \ (current->personality & (~PER_MASK))); \
current->thread.sys_call_table = \ current->thread.sys_call_table = sys_call_table; \
(unsigned long) &sys_call_table; \
} while (0) } while (0)
#else /* CONFIG_COMPAT */ #else /* CONFIG_COMPAT */
#define SET_PERSONALITY(ex) \ #define SET_PERSONALITY(ex) \
...@@ -245,11 +244,11 @@ do { \ ...@@ -245,11 +244,11 @@ do { \
if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \ if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \
set_thread_flag(TIF_31BIT); \ set_thread_flag(TIF_31BIT); \
current->thread.sys_call_table = \ current->thread.sys_call_table = \
(unsigned long) &sys_call_table_emu; \ sys_call_table_emu; \
} else { \ } else { \
clear_thread_flag(TIF_31BIT); \ clear_thread_flag(TIF_31BIT); \
current->thread.sys_call_table = \ current->thread.sys_call_table = \
(unsigned long) &sys_call_table; \ sys_call_table; \
} \ } \
} while (0) } while (0)
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
......
/* 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 @@ ...@@ -47,6 +47,8 @@
#include <linux/preempt.h> #include <linux/preempt.h>
void save_fpu_regs(void); void save_fpu_regs(void);
void load_fpu_regs(void);
void __load_fpu_regs(void);
static inline int test_fp_ctl(u32 fpc) static inline int test_fp_ctl(u32 fpc)
{ {
......
...@@ -20,11 +20,13 @@ struct s390_idle_data { ...@@ -20,11 +20,13 @@ struct s390_idle_data {
unsigned long long clock_idle_exit; unsigned long long clock_idle_exit;
unsigned long long timer_idle_enter; unsigned long long timer_idle_enter;
unsigned long long timer_idle_exit; 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_count;
extern struct device_attribute dev_attr_idle_time_us; 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 */ #endif /* _S390_IDLE_H */
...@@ -81,8 +81,8 @@ struct lowcore { ...@@ -81,8 +81,8 @@ struct lowcore {
psw_t return_mcck_psw; /* 0x02a0 */ psw_t return_mcck_psw; /* 0x02a0 */
/* CPU accounting and timing values. */ /* CPU accounting and timing values. */
__u64 sync_enter_timer; /* 0x02b0 */ __u64 sys_enter_timer; /* 0x02b0 */
__u64 async_enter_timer; /* 0x02b8 */ __u8 pad_0x02b8[0x02c0-0x02b8]; /* 0x02b8 */
__u64 mcck_enter_timer; /* 0x02c0 */ __u64 mcck_enter_timer; /* 0x02c0 */
__u64 exit_timer; /* 0x02c8 */ __u64 exit_timer; /* 0x02c8 */
__u64 user_timer; /* 0x02d0 */ __u64 user_timer; /* 0x02d0 */
......
...@@ -99,6 +99,7 @@ int nmi_alloc_per_cpu(struct lowcore *lc); ...@@ -99,6 +99,7 @@ int nmi_alloc_per_cpu(struct lowcore *lc);
void nmi_free_per_cpu(struct lowcore *lc); void nmi_free_per_cpu(struct lowcore *lc);
void s390_handle_mcck(void); void s390_handle_mcck(void);
void __s390_handle_mcck(void);
int s390_do_machine_check(struct pt_regs *regs); int s390_do_machine_check(struct pt_regs *regs);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
......
...@@ -38,6 +38,11 @@ ...@@ -38,6 +38,11 @@
#include <asm/runtime_instr.h> #include <asm/runtime_instr.h>
#include <asm/fpu/types.h> #include <asm/fpu/types.h>
#include <asm/fpu/internal.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) static inline void set_cpu_flag(int flag)
{ {
...@@ -101,31 +106,32 @@ extern void __bpon(void); ...@@ -101,31 +106,32 @@ extern void __bpon(void);
*/ */
struct thread_struct { struct thread_struct {
unsigned int acrs[NUM_ACRS]; unsigned int acrs[NUM_ACRS];
unsigned long ksp; /* kernel stack pointer */ unsigned long ksp; /* kernel stack pointer */
unsigned long user_timer; /* task cputime in user space */ unsigned long user_timer; /* task cputime in user space */
unsigned long guest_timer; /* task cputime in kvm guest */ unsigned long guest_timer; /* task cputime in kvm guest */
unsigned long system_timer; /* task cputime in kernel space */ unsigned long system_timer; /* task cputime in kernel space */
unsigned long hardirq_timer; /* task cputime in hardirq context */ unsigned long hardirq_timer; /* task cputime in hardirq context */
unsigned long softirq_timer; /* task cputime in softirq 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 long gmap_addr; /* address of last gmap fault. */
unsigned int gmap_write_flag; /* gmap fault write indication */ unsigned int gmap_write_flag; /* gmap fault write indication */
unsigned int gmap_int_code; /* int code of last gmap fault */ unsigned int gmap_int_code; /* int code of last gmap fault */
unsigned int gmap_pfault; /* signal of a pending guest pfault */ unsigned int gmap_pfault; /* signal of a pending guest pfault */
/* Per-thread information related to debugging */ /* Per-thread information related to debugging */
struct per_regs per_user; /* User specified PER registers */ struct per_regs per_user; /* User specified PER registers */
struct per_event per_event; /* Cause of the last PER trap */ struct per_event per_event; /* Cause of the last PER trap */
unsigned long per_flags; /* Flags to control debug behavior */ unsigned long per_flags; /* Flags to control debug behavior */
unsigned int system_call; /* system call number in signal */ unsigned int system_call; /* system call number in signal */
unsigned long last_break; /* last breaking-event-address. */ unsigned long last_break; /* last breaking-event-address. */
/* pfault_wait is used to block the process on a pfault event */ /* pfault_wait is used to block the process on a pfault event */
unsigned long pfault_wait; unsigned long pfault_wait;
struct list_head list; struct list_head list;
/* cpu runtime instrumentation */ /* cpu runtime instrumentation */
struct runtime_instr_cb *ri_cb; struct runtime_instr_cb *ri_cb;
struct gs_cb *gs_cb; /* Current guarded storage cb */ struct gs_cb *gs_cb; /* Current guarded storage cb */
struct gs_cb *gs_bc_cb; /* Broadcast guarded storage cb */ struct gs_cb *gs_bc_cb; /* Broadcast guarded storage cb */
unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ unsigned char trap_tdb[256]; /* Transaction abort diagnose block */
/* /*
* Warning: 'fpu' is dynamically-sized. It *MUST* be at * Warning: 'fpu' is dynamically-sized. It *MUST* be at
* the end. * the end.
...@@ -184,6 +190,7 @@ static inline void release_thread(struct task_struct *tsk) { } ...@@ -184,6 +190,7 @@ static inline void release_thread(struct task_struct *tsk) { }
/* Free guarded storage control block */ /* Free guarded storage control block */
void guarded_storage_release(struct task_struct *tsk); 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); unsigned long get_wchan(struct task_struct *p);
#define task_pt_regs(tsk) ((struct pt_regs *) \ #define task_pt_regs(tsk) ((struct pt_regs *) \
...@@ -324,6 +331,11 @@ extern void memcpy_absolute(void *, void *, size_t); ...@@ -324,6 +331,11 @@ extern void memcpy_absolute(void *, void *, size_t);
extern int s390_isolate_bp(void); extern int s390_isolate_bp(void);
extern int s390_isolate_bp_guest(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 /* __ASSEMBLY__ */
#endif /* __ASM_S390_PROCESSOR_H */ #endif /* __ASM_S390_PROCESSOR_H */
...@@ -11,13 +11,13 @@ ...@@ -11,13 +11,13 @@
#include <uapi/asm/ptrace.h> #include <uapi/asm/ptrace.h>
#define PIF_SYSCALL 0 /* inside a system call */ #define PIF_SYSCALL 0 /* inside a system call */
#define PIF_PER_TRAP 1 /* deliver sigtrap on return to user */ #define PIF_SYSCALL_RESTART 1 /* restart the current system call */
#define PIF_SYSCALL_RESTART 2 /* 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_GUEST_FAULT 3 /* indicates program check in sie64a */
#define _PIF_SYSCALL BIT(PIF_SYSCALL) #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_RESTART BIT(PIF_SYSCALL_RESTART)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
#define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT) #define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -68,6 +68,9 @@ enum { ...@@ -68,6 +68,9 @@ enum {
&(*(struct psw_bits *)(&(__psw))); \ &(*(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 pt_regs struct defines the way the registers are stored on
* the stack during a system call. * the stack during a system call.
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
#include <linux/err.h> #include <linux/err.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
extern const unsigned long sys_call_table[]; extern const sys_call_ptr_t sys_call_table[];
extern const unsigned long sys_call_table_emu[]; extern const sys_call_ptr_t sys_call_table_emu[];
static inline long syscall_get_nr(struct task_struct *task, static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs) struct pt_regs *regs)
...@@ -56,6 +56,7 @@ static inline void syscall_set_return_value(struct task_struct *task, ...@@ -56,6 +56,7 @@ static inline void syscall_set_return_value(struct task_struct *task,
struct pt_regs *regs, struct pt_regs *regs,
int error, long val) int error, long val)
{ {
set_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
regs->gprs[2] = error ? error : val; regs->gprs[2] = error ? error : val;
} }
...@@ -97,4 +98,10 @@ static inline int syscall_get_arch(struct task_struct *task) ...@@ -97,4 +98,10 @@ static inline int syscall_get_arch(struct task_struct *task)
#endif #endif
return AUDIT_ARCH_S390X; return AUDIT_ARCH_S390X;
} }
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
{
return false;
}
#endif /* _ASM_SYSCALL_H */ #endif /* _ASM_SYSCALL_H */
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
*/ */
struct thread_info { struct thread_info {
unsigned long flags; /* low level flags */ unsigned long flags; /* low level flags */
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
}; };
/* /*
...@@ -68,6 +69,7 @@ void arch_setup_new_exec(void); ...@@ -68,6 +69,7 @@ void arch_setup_new_exec(void);
#define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */ #define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */
#define TIF_ISOLATE_BP 8 /* Run process with isolated BP */ #define TIF_ISOLATE_BP 8 /* Run process with isolated BP */
#define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests 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_31BIT 16 /* 32bit process */
#define TIF_MEMDIE 17 /* is terminating due to OOM killer */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */
...@@ -91,6 +93,7 @@ void arch_setup_new_exec(void); ...@@ -91,6 +93,7 @@ void arch_setup_new_exec(void);
#define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING) #define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING)
#define _TIF_ISOLATE_BP BIT(TIF_ISOLATE_BP) #define _TIF_ISOLATE_BP BIT(TIF_ISOLATE_BP)
#define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST) #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_31BIT BIT(TIF_31BIT)
#define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP) #define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP)
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include <asm/extable.h> #include <asm/extable.h>
#include <asm/facility.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) static inline int __range_ok(unsigned long addr, unsigned long size)
{ {
......
...@@ -4,4 +4,18 @@ ...@@ -4,4 +4,18 @@
#define __ARCH_HAS_VTIME_TASK_SWITCH #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 */ #endif /* _S390_VTIME_H */
...@@ -179,8 +179,9 @@ ...@@ -179,8 +179,9 @@
#define ACR_SIZE 4 #define ACR_SIZE 4
#define PTRACE_OLDSETOPTIONS 21 #define PTRACE_OLDSETOPTIONS 21
#define PTRACE_SYSEMU 31
#define PTRACE_SYSEMU_SINGLESTEP 32
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/types.h> #include <linux/types.h>
......
...@@ -34,7 +34,7 @@ CFLAGS_dumpstack.o += -fno-optimize-sibling-calls ...@@ -34,7 +34,7 @@ CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
CFLAGS_unwind_bc.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 := 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 += 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 += 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 obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
......
...@@ -26,26 +26,14 @@ int main(void) ...@@ -26,26 +26,14 @@ int main(void)
BLANK(); BLANK();
/* thread struct offsets */ /* thread struct offsets */
OFFSET(__THREAD_ksp, thread_struct, ksp); 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(); BLANK();
/* thread info offsets */ /* thread info offsets */
OFFSET(__TI_flags, task_struct, thread_info.flags); OFFSET(__TI_flags, task_struct, thread_info.flags);
BLANK(); BLANK();
/* pt_regs offsets */ /* pt_regs offsets */
OFFSET(__PT_ARGS, pt_regs, args);
OFFSET(__PT_PSW, pt_regs, psw); OFFSET(__PT_PSW, pt_regs, psw);
OFFSET(__PT_GPRS, pt_regs, gprs); OFFSET(__PT_GPRS, pt_regs, gprs);
OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2); 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_FLAGS, pt_regs, flags);
OFFSET(__PT_CR1, pt_regs, cr1); OFFSET(__PT_CR1, pt_regs, cr1);
DEFINE(__PT_SIZE, sizeof(struct pt_regs)); DEFINE(__PT_SIZE, sizeof(struct pt_regs));
...@@ -64,6 +52,7 @@ int main(void) ...@@ -64,6 +52,7 @@ int main(void)
OFFSET(__CLOCK_IDLE_EXIT, s390_idle_data, clock_idle_exit); OFFSET(__CLOCK_IDLE_EXIT, s390_idle_data, clock_idle_exit);
OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter); OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter);
OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit); OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit);
OFFSET(__MT_CYCLES_ENTER, s390_idle_data, mt_cycles_enter);
BLANK(); BLANK();
/* hardware defined lowcore locations 0x000 - 0x1ff */ /* hardware defined lowcore locations 0x000 - 0x1ff */
OFFSET(__LC_EXT_PARAMS, lowcore, ext_params); OFFSET(__LC_EXT_PARAMS, lowcore, ext_params);
...@@ -115,13 +104,9 @@ int main(void) ...@@ -115,13 +104,9 @@ int main(void)
OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags); OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags);
OFFSET(__LC_RETURN_PSW, lowcore, return_psw); OFFSET(__LC_RETURN_PSW, lowcore, return_psw);
OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw); OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw);
OFFSET(__LC_SYNC_ENTER_TIMER, lowcore, sync_enter_timer); OFFSET(__LC_SYS_ENTER_TIMER, lowcore, sys_enter_timer);
OFFSET(__LC_ASYNC_ENTER_TIMER, lowcore, async_enter_timer);
OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer); OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer);
OFFSET(__LC_EXIT_TIMER, lowcore, exit_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_TIMER, lowcore, last_update_timer);
OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
OFFSET(__LC_INT_CLOCK, lowcore, int_clock); OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
......
...@@ -118,6 +118,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -118,6 +118,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu); fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu);
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -17,8 +17,9 @@ void io_int_handler(void); ...@@ -17,8 +17,9 @@ void io_int_handler(void);
void mcck_int_handler(void); void mcck_int_handler(void);
void restart_int_handler(void); void restart_int_handler(void);
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs);
asmlinkage void do_syscall_trace_exit(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_protection_exception(struct pt_regs *regs);
void do_dat_exception(struct pt_regs *regs); void do_dat_exception(struct pt_regs *regs);
...@@ -48,9 +49,7 @@ void translation_exception(struct pt_regs *regs); ...@@ -48,9 +49,7 @@ void translation_exception(struct pt_regs *regs);
void vector_exception(struct pt_regs *regs); void vector_exception(struct pt_regs *regs);
void monitor_event_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 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 kernel_stack_overflow(struct pt_regs * regs);
void do_signal(struct pt_regs *regs); void do_signal(struct pt_regs *regs);
void handle_signal32(struct ksignal *ksig, sigset_t *oldset, void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
...@@ -58,7 +57,8 @@ 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 do_notify_resume(struct pt_regs *regs);
void __init init_IRQ(void); 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 do_restart(void);
void __init startup_init(void); void __init startup_init(void);
void die(struct pt_regs *regs, const char *str); 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 ...@@ -82,8 +82,6 @@ long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user
DECLARE_PER_CPU(u64, mt_cycles[8]); DECLARE_PER_CPU(u64, mt_cycles[8]);
void gs_load_bc_cb(struct pt_regs *regs);
unsigned long stack_alloc(void); unsigned long stack_alloc(void);
void stack_free(unsigned long stack); void stack_free(unsigned long stack);
......
...@@ -175,3 +175,91 @@ void __kernel_fpu_end(struct kernel_fpu *state, u32 flags) ...@@ -175,3 +175,91 @@ void __kernel_fpu_end(struct kernel_fpu *state, u32 flags)
: "1", "cc"); : "1", "cc");
} }
EXPORT_SYMBOL(__kernel_fpu_end); EXPORT_SYMBOL(__kernel_fpu_end);
void __load_fpu_regs(void)
{
struct fpu *state = &current->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 = &current->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 @@ ...@@ -14,12 +14,36 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/sched/cputime.h> #include <linux/sched/cputime.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <asm/cpu_mf.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/smp.h> #include <asm/smp.h>
#include "entry.h" #include "entry.h"
static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); 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) void arch_cpu_idle(void)
{ {
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/entry-common.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
#include <asm/cputime.h> #include <asm/cputime.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
...@@ -95,19 +96,97 @@ static const struct irq_class irqclass_sub_desc[] = { ...@@ -95,19 +96,97 @@ static const struct irq_class irqclass_sub_desc[] = {
{.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"}, {.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, if (tod_after_eq(S390_lowcore.int_clock,
S390_lowcore.clock_comparator)) S390_lowcore.clock_comparator))
/* Serve timer interrupts first. */ /* Serve timer interrupts first. */
clock_comparator_work(); clock_comparator_work();
generic_handle_irq(irq); 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(&regs->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(&regs->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(); irq_exit();
set_irq_regs(old_regs); 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) static void show_msi_interrupt(struct seq_file *p, int irq)
......
...@@ -131,12 +131,11 @@ static notrace void s390_handle_damage(void) ...@@ -131,12 +131,11 @@ static notrace void s390_handle_damage(void)
NOKPROBE_SYMBOL(s390_handle_damage); NOKPROBE_SYMBOL(s390_handle_damage);
/* /*
* Main machine check handler function. Will be called with interrupts enabled * Main machine check handler function. Will be called with interrupts disabled
* or disabled and machine checks enabled or disabled. * and machine checks enabled.
*/ */
void s390_handle_mcck(void) void __s390_handle_mcck(void)
{ {
unsigned long flags;
struct mcck_struct mcck; struct mcck_struct mcck;
/* /*
...@@ -144,12 +143,10 @@ void s390_handle_mcck(void) ...@@ -144,12 +143,10 @@ void s390_handle_mcck(void)
* machine checks. Afterwards delete the old state and enable machine * machine checks. Afterwards delete the old state and enable machine
* checks again. * checks again.
*/ */
local_irq_save(flags);
local_mcck_disable(); local_mcck_disable();
mcck = *this_cpu_ptr(&cpu_mcck); mcck = *this_cpu_ptr(&cpu_mcck);
memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck)); memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
local_mcck_enable(); local_mcck_enable();
local_irq_restore(flags);
if (mcck.channel_report) if (mcck.channel_report)
crw_handle_channel_report(); crw_handle_channel_report();
...@@ -181,8 +178,13 @@ void s390_handle_mcck(void) ...@@ -181,8 +178,13 @@ void s390_handle_mcck(void)
do_exit(SIGSEGV); 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 0 if all required registers are available
* returns 1 otherwise * returns 1 otherwise
...@@ -344,6 +346,9 @@ int notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -344,6 +346,9 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
int mcck_pending = 0; int mcck_pending = 0;
nmi_enter(); nmi_enter();
if (user_mode(regs))
update_timer_mcck();
inc_irq_stat(NMI_NMI); inc_irq_stat(NMI_NMI);
mci.val = S390_lowcore.mcck_interruption_code; mci.val = S390_lowcore.mcck_interruption_code;
mcck = this_cpu_ptr(&cpu_mcck); mcck = this_cpu_ptr(&cpu_mcck);
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/init_task.h> #include <linux/init_task.h>
#include <linux/entry-common.h>
#include <asm/cpu_mf.h> #include <asm/cpu_mf.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -43,9 +44,22 @@ ...@@ -43,9 +44,22 @@
#include <asm/unwind.h> #include <asm/unwind.h>
#include "entry.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) void flush_thread(void)
{ {
...@@ -108,10 +122,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -108,10 +122,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
p->thread.last_break = 1; p->thread.last_break = 1;
frame->sf.back_chain = 0; 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 */ /* 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 */ /* 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. */ /* Store access registers to kernel stack of new process. */
if (unlikely(p->flags & PF_KTHREAD)) { if (unlikely(p->flags & PF_KTHREAD)) {
...@@ -120,10 +136,10 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -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 | frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
frame->childregs.psw.addr = frame->childregs.psw.addr =
(unsigned long) kernel_thread_starter; (unsigned long)__ret_from_fork;
frame->childregs.gprs[9] = new_stackp; /* function */ frame->childregs.gprs[9] = new_stackp; /* function */
frame->childregs.gprs[10] = arg; 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; frame->childregs.orig_gpr2 = -1;
return 0; return 0;
...@@ -153,7 +169,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -153,7 +169,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
return 0; return 0;
} }
asmlinkage void execve_tail(void) void execve_tail(void)
{ {
current->thread.fpu.fpc = 0; current->thread.fpu.fpc = 0;
asm volatile("sfpc %0" : : "d" (0)); asm volatile("sfpc %0" : : "d" (0));
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com)
*/ */
#include "asm/ptrace.h"
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
...@@ -37,9 +38,6 @@ ...@@ -37,9 +38,6 @@
#include "compat_ptrace.h" #include "compat_ptrace.h"
#endif #endif
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
void update_cr_regs(struct task_struct *task) void update_cr_regs(struct task_struct *task)
{ {
struct pt_regs *regs = task_pt_regs(task); struct pt_regs *regs = task_pt_regs(task);
...@@ -140,7 +138,7 @@ void ptrace_disable(struct task_struct *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_user, 0, sizeof(task->thread.per_user));
memset(&task->thread.per_event, 0, sizeof(task->thread.per_event)); memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
clear_tsk_thread_flag(task, TIF_SINGLE_STEP); 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; task->thread.per_flags = 0;
} }
...@@ -322,25 +320,6 @@ static inline void __poke_user_per(struct task_struct *child, ...@@ -322,25 +320,6 @@ static inline void __poke_user_per(struct task_struct *child,
child->thread.per_user.end = data; 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 * Write a word to the user area of a process at location addr. This
* operation does have an additional problem compared to peek_user. * 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) ...@@ -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) && if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
addr == offsetof(struct user, regs.gprs[2])) addr == offsetof(struct user, regs.gprs[2])) {
fixup_int_code(child, data); struct pt_regs *regs = task_pt_regs(child);
*(addr_t *)((addr_t) &regs->psw + addr) = data;
regs->int_code = 0x20000 | (data & 0xffff);
}
*(addr_t *)((addr_t) &regs->psw + addr) = data;
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
/* /*
* access registers are stored in the thread structure * access registers are stored in the thread structure
...@@ -742,10 +723,12 @@ static int __poke_user_compat(struct task_struct *child, ...@@ -742,10 +723,12 @@ static int __poke_user_compat(struct task_struct *child,
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) | regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
(__u64)(tmp & PSW32_ADDR_AMODE); (__u64)(tmp & PSW32_ADDR_AMODE);
} else { } else {
if (test_pt_regs_flag(regs, PIF_SYSCALL) && if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
addr == offsetof(struct compat_user, regs.gprs[2])) addr == offsetof(struct compat_user, regs.gprs[2])) {
fixup_int_code(child, data); struct pt_regs *regs = task_pt_regs(child);
regs->int_code = 0x20000 | (data & 0xffff);
}
/* gpr 0-15 */ /* gpr 0-15 */
*(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp; *(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp;
} }
...@@ -862,82 +845,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, ...@@ -862,82 +845,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
} }
#endif #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. * user_regset definitions.
*/ */
......
...@@ -411,8 +411,7 @@ static void __init setup_lowcore_dat_off(void) ...@@ -411,8 +411,7 @@ static void __init setup_lowcore_dat_off(void)
memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list, memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
sizeof(lc->alt_stfle_fac_list)); sizeof(lc->alt_stfle_fac_list));
nmi_alloc_boot_cpu(lc); nmi_alloc_boot_cpu(lc);
lc->sync_enter_timer = S390_lowcore.sync_enter_timer; lc->sys_enter_timer = S390_lowcore.sys_enter_timer;
lc->async_enter_timer = S390_lowcore.async_enter_timer;
lc->exit_timer = S390_lowcore.exit_timer; lc->exit_timer = S390_lowcore.exit_timer;
lc->user_timer = S390_lowcore.user_timer; lc->user_timer = S390_lowcore.user_timer;
lc->system_timer = S390_lowcore.system_timer; lc->system_timer = S390_lowcore.system_timer;
......
...@@ -170,6 +170,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -170,6 +170,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
fpregs_load(&user_sregs.fpregs, &current->thread.fpu); fpregs_load(&user_sregs.fpregs, &current->thread.fpu);
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
return 0; return 0;
} }
...@@ -459,7 +460,8 @@ static void handle_signal(struct ksignal *ksig, sigset_t *oldset, ...@@ -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 * the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that. * 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; struct ksignal ksig;
sigset_t *oldset = sigmask_to_save(); sigset_t *oldset = sigmask_to_save();
...@@ -472,7 +474,7 @@ void do_signal(struct pt_regs *regs) ...@@ -472,7 +474,7 @@ void do_signal(struct pt_regs *regs)
current->thread.system_call = current->thread.system_call =
test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0; 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. */ /* Whee! Actually deliver the signal. */
if (current->thread.system_call) { if (current->thread.system_call) {
regs->int_code = current->thread.system_call; regs->int_code = current->thread.system_call;
...@@ -498,6 +500,7 @@ void do_signal(struct pt_regs *regs) ...@@ -498,6 +500,7 @@ void do_signal(struct pt_regs *regs)
} }
/* No longer in a system call */ /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL); clear_pt_regs_flag(regs, PIF_SYSCALL);
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
rseq_signal_deliver(&ksig, regs); rseq_signal_deliver(&ksig, regs);
if (is_compat_task()) if (is_compat_task())
handle_signal32(&ksig, oldset, regs); handle_signal32(&ksig, oldset, regs);
...@@ -508,6 +511,7 @@ void do_signal(struct pt_regs *regs) ...@@ -508,6 +511,7 @@ void do_signal(struct pt_regs *regs)
/* No handlers present - check for system call restart */ /* No handlers present - check for system call restart */
clear_pt_regs_flag(regs, PIF_SYSCALL); clear_pt_regs_flag(regs, PIF_SYSCALL);
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
if (current->thread.system_call) { if (current->thread.system_call) {
regs->int_code = current->thread.system_call; regs->int_code = current->thread.system_call;
switch (regs->gprs[2]) { switch (regs->gprs[2]) {
...@@ -520,9 +524,9 @@ void do_signal(struct pt_regs *regs) ...@@ -520,9 +524,9 @@ void do_signal(struct pt_regs *regs)
case -ERESTARTNOINTR: case -ERESTARTNOINTR:
/* Restart system call with magic TIF bit. */ /* Restart system call with magic TIF bit. */
regs->gprs[2] = regs->orig_gpr2; 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)) if (test_thread_flag(TIF_SINGLE_STEP))
clear_pt_regs_flag(regs, PIF_PER_TRAP); clear_thread_flag(TIF_PER_TRAP);
break; break;
} }
} }
......
...@@ -499,7 +499,7 @@ static void smp_handle_ext_call(void) ...@@ -499,7 +499,7 @@ static void smp_handle_ext_call(void)
if (test_bit(ec_call_function_single, &bits)) if (test_bit(ec_call_function_single, &bits))
generic_smp_call_function_single_interrupt(); generic_smp_call_function_single_interrupt();
if (test_bit(ec_mcck_pending, &bits)) if (test_bit(ec_mcck_pending, &bits))
s390_handle_mcck(); __s390_handle_mcck();
} }
static void do_ext_call_interrupt(struct ext_code ext_code, static void do_ext_call_interrupt(struct ext_code ext_code,
......
...@@ -29,6 +29,13 @@ ...@@ -29,6 +29,13 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/ipc.h> #include <linux/ipc.h>
#include <linux/uaccess.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" #include "entry.h"
/* /*
...@@ -100,3 +107,66 @@ SYSCALL_DEFINE0(ni_syscall) ...@@ -100,3 +107,66 @@ SYSCALL_DEFINE0(ni_syscall)
{ {
return -ENOSYS; 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(&regs->gprs[8], S390_lowcore.save_area_sync, 8 * sizeof(unsigned long));
memcpy(&regs->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 @@ ...@@ -13,6 +13,8 @@
* 'Traps.c' handles hardware traps and faults after we have saved some * 'Traps.c' handles hardware traps and faults after we have saved some
* state in 'asm.s'. * state in 'asm.s'.
*/ */
#include "asm/irqflags.h"
#include "asm/ptrace.h"
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/extable.h> #include <linux/extable.h>
...@@ -23,7 +25,9 @@ ...@@ -23,7 +25,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/entry-common.h>
#include <asm/fpu/api.h> #include <asm/fpu/api.h>
#include <asm/vtime.h>
#include "entry.h" #include "entry.h"
static inline void __user *get_trap_ip(struct pt_regs *regs) static inline void __user *get_trap_ip(struct pt_regs *regs)
...@@ -288,3 +292,64 @@ void __init trap_init(void) ...@@ -288,3 +292,64 @@ void __init trap_init(void)
local_mcck_enable(); local_mcck_enable();
test_monitor_call(); 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(&current->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 = &current->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) ...@@ -32,7 +32,7 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
return -EINVAL; return -EINVAL;
if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT) if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)
return -EINVAL; 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_per = psw_bits(regs->psw).per;
auprobe->saved_int_code = regs->int_code; auprobe->saved_int_code = regs->int_code;
regs->int_code = UPROBE_TRAP_NR; regs->int_code = UPROBE_TRAP_NR;
...@@ -103,7 +103,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) ...@@ -103,7 +103,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
/* fix per address */ /* fix per address */
current->thread.per_event.address = utask->vaddr; current->thread.per_event.address = utask->vaddr;
/* trigger per event */ /* trigger per event */
set_pt_regs_flag(regs, PIF_PER_TRAP); set_thread_flag(TIF_PER_TRAP);
} }
return 0; return 0;
} }
...@@ -259,7 +259,7 @@ static void sim_stor_event(struct pt_regs *regs, void *addr, int len) ...@@ -259,7 +259,7 @@ static void sim_stor_event(struct pt_regs *regs, void *addr, int len)
return; return;
current->thread.per_event.address = regs->psw.addr; current->thread.per_event.address = regs->psw.addr;
current->thread.per_event.cause = PER_EVENT_STORE >> 16; 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 @@ ...@@ -45,6 +45,7 @@
#include <asm/timex.h> #include <asm/timex.h>
#include <asm/ap.h> #include <asm/ap.h>
#include <asm/uv.h> #include <asm/uv.h>
#include <asm/fpu/api.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
...@@ -4147,6 +4148,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu) ...@@ -4147,6 +4148,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
vcpu->run->s.regs.gprs, vcpu->run->s.regs.gprs,
sizeof(sie_page->pv_grregs)); sizeof(sie_page->pv_grregs));
} }
if (test_cpu_flag(CIF_FPU))
load_fpu_regs();
exit_reason = sie64a(vcpu->arch.sie_block, exit_reason = sie64a(vcpu->arch.sie_block,
vcpu->run->s.regs.gprs); vcpu->run->s.regs.gprs);
if (kvm_s390_pv_cpu_is_protected(vcpu)) { if (kvm_s390_pv_cpu_is_protected(vcpu)) {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <asm/sclp.h> #include <asm/sclp.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/dis.h> #include <asm/dis.h>
#include <asm/fpu/api.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
...@@ -1028,6 +1029,8 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) ...@@ -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; vcpu->arch.sie_block->prog0c |= PROG_IN_SIE;
barrier(); barrier();
if (test_cpu_flag(CIF_FPU))
load_fpu_regs();
if (!kvm_s390_vcpu_sie_inhibited(vcpu)) if (!kvm_s390_vcpu_sie_inhibited(vcpu))
rc = sie64a(scb_s, vcpu->run->s.regs.gprs); rc = sie64a(scb_s, vcpu->run->s.regs.gprs);
barrier(); barrier();
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/facility.h> #include <asm/facility.h>
#ifdef CONFIG_DEBUG_USER_ASCE #ifdef CONFIG_DEBUG_ENTRY
void debug_user_asce(void) void debug_user_asce(int exit)
{ {
unsigned long cr1, cr7; unsigned long cr1, cr7;
...@@ -25,12 +25,14 @@ void debug_user_asce(void) ...@@ -25,12 +25,14 @@ void debug_user_asce(void)
__ctl_store(cr7, 7, 7); __ctl_store(cr7, 7, 7);
if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce) if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce)
return; return;
panic("incorrect ASCE on kernel exit\n" panic("incorrect ASCE on kernel %s\n"
"cr1: %016lx cr7: %016lx\n" "cr1: %016lx cr7: %016lx\n"
"kernel: %016llx user: %016llx\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 #ifndef CONFIG_HAVE_MARCH_Z10_FEATURES
static DEFINE_STATIC_KEY_FALSE(have_mvcos); static DEFINE_STATIC_KEY_FALSE(have_mvcos);
......
...@@ -385,7 +385,7 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access) ...@@ -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 * The instruction that caused the program check has
* been nullified. Don't signal single step via SIGTRAP. * 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)) if (kprobe_page_fault(regs, 14))
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment