Commit e98e03d0 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 's390-5.14-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull more s390 updates from Vasily Gorbik:

 - Fix preempt_count initialization.

 - Rework call_on_stack() macro to add proper type handling and avoid
   possible register corruption.

 - More error prone "register asm" removal and fixes.

 - Fix syscall restarting when multiple signals are coming in. This adds
   minimalistic trampolines to vdso so we can return from signal without
   using the stack which requires pgm check handler hacks when NX is
   enabled.

 - Remove HAVE_IRQ_EXIT_ON_IRQ_STACK since this is no longer true after
   switch to generic entry.

 - Fix protected virtualization secure storage access exception
   handling.

 - Make machine check C handler always enter with DAT enabled and move
   register validation to C code.

 - Fix tinyconfig boot problem by avoiding MONITOR CALL without
   CONFIG_BUG.

 - Increase asm symbols alignment to 16 to make it consistent with
   compilers.

 - Enable concurrent access to the CPU Measurement Counter Facility.

 - Add support for dynamic AP bus size limit and rework ap_dqap to deal
   with messages greater than recv buffer.

* tag 's390-5.14-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (41 commits)
  s390: preempt: Fix preempt_count initialization
  s390/linkage: increase asm symbols alignment to 16
  s390: rename CALL_ON_STACK_NORETURN() to call_on_stack_noreturn()
  s390: add type checking to CALL_ON_STACK_NORETURN() macro
  s390: remove old CALL_ON_STACK() macro
  s390/softirq: use call_on_stack() macro
  s390/lib: use call_on_stack() macro
  s390/smp: use call_on_stack() macro
  s390/kexec: use call_on_stack() macro
  s390/irq: use call_on_stack() macro
  s390/mm: use call_on_stack() macro
  s390: introduce proper type handling call_on_stack() macro
  s390/irq: simplify on_async_stack()
  s390/irq: inline do_softirq_own_stack()
  s390/irq: simplify do_softirq_own_stack()
  s390/ap: get rid of register asm in ap_dqap()
  s390: rename PIF_SYSCALL_RESTART to PIF_EXECVE_PGSTE_RESTART
  s390: move restart of execve() syscall
  s390/signal: remove sigreturn on stack
  s390/signal: switch to using vdso for sigreturn and syscall restart
  ...
parents 379cf80a 6a942f57
...@@ -163,7 +163,6 @@ config S390 ...@@ -163,7 +163,6 @@ config S390
select HAVE_GCC_PLUGINS select HAVE_GCC_PLUGINS
select HAVE_GENERIC_VDSO select HAVE_GENERIC_VDSO
select HAVE_IOREMAP_PROT if PCI select HAVE_IOREMAP_PROT if PCI
select HAVE_IRQ_EXIT_ON_IRQ_STACK
select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_BZIP2
select HAVE_KERNEL_GZIP select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4 select HAVE_KERNEL_LZ4
...@@ -438,6 +437,7 @@ config COMPAT ...@@ -438,6 +437,7 @@ config COMPAT
select COMPAT_OLD_SIGACTION select COMPAT_OLD_SIGACTION
select HAVE_UID16 select HAVE_UID16
depends on MULTIUSER depends on MULTIUSER
depends on !CC_IS_CLANG
help help
Select this option if you want to enable your system kernel to Select this option if you want to enable your system kernel to
handle system-calls from ELF binaries for 31 bit ESA. This option handle system-calls from ELF binaries for 31 bit ESA. This option
......
...@@ -166,6 +166,19 @@ archheaders: ...@@ -166,6 +166,19 @@ archheaders:
archprepare: archprepare:
$(Q)$(MAKE) $(build)=$(syscalls) kapi $(Q)$(MAKE) $(build)=$(syscalls) kapi
$(Q)$(MAKE) $(build)=$(tools) kapi $(Q)$(MAKE) $(build)=$(tools) kapi
ifeq ($(KBUILD_EXTMOD),)
# We need to generate vdso-offsets.h before compiling certain files in kernel/.
# In order to do that, we should use the archprepare target, but we can't since
# asm-offsets.h is included in some files used to generate vdso-offsets.h, and
# asm-offsets.h is built in prepare0, for which archprepare is a dependency.
# Therefore we need to generate the header after prepare0 has been made, hence
# this hack.
prepare: vdso_prepare
vdso_prepare: prepare0
$(Q)$(MAKE) $(build)=arch/s390/kernel/vdso64 include/generated/vdso64-offsets.h
$(if $(CONFIG_COMPAT),$(Q)$(MAKE) \
$(build)=arch/s390/kernel/vdso32 include/generated/vdso32-offsets.h)
endif
# Don't use tabs in echo arguments # Don't use tabs in echo arguments
define archhelp define archhelp
......
...@@ -23,6 +23,7 @@ unsigned long __bootdata_preserved(vmemmap_size); ...@@ -23,6 +23,7 @@ unsigned long __bootdata_preserved(vmemmap_size);
unsigned long __bootdata_preserved(MODULES_VADDR); unsigned long __bootdata_preserved(MODULES_VADDR);
unsigned long __bootdata_preserved(MODULES_END); unsigned long __bootdata_preserved(MODULES_END);
unsigned long __bootdata(ident_map_size); unsigned long __bootdata(ident_map_size);
int __bootdata(is_full_image) = 1;
u64 __bootdata_preserved(stfle_fac_list[16]); u64 __bootdata_preserved(stfle_fac_list[16]);
u64 __bootdata_preserved(alt_stfle_fac_list[16]); u64 __bootdata_preserved(alt_stfle_fac_list[16]);
......
...@@ -36,6 +36,7 @@ void uv_query_info(void) ...@@ -36,6 +36,7 @@ void uv_query_info(void)
uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE); uv_info.max_sec_stor_addr = ALIGN(uvcb.max_guest_stor_addr, PAGE_SIZE);
uv_info.max_num_sec_conf = uvcb.max_num_sec_conf; uv_info.max_num_sec_conf = uvcb.max_num_sec_conf;
uv_info.max_guest_cpu_id = uvcb.max_guest_cpu_id; uv_info.max_guest_cpu_id = uvcb.max_guest_cpu_id;
uv_info.uv_feature_indications = uvcb.uv_feature_indications;
} }
#ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST
......
This diff is collapsed.
...@@ -32,39 +32,22 @@ static const u64 cpumf_ctr_ctl[CPUMF_CTR_SET_MAX] = { ...@@ -32,39 +32,22 @@ static const u64 cpumf_ctr_ctl[CPUMF_CTR_SET_MAX] = {
[CPUMF_CTR_SET_MT_DIAG] = 0x20, [CPUMF_CTR_SET_MT_DIAG] = 0x20,
}; };
static inline void ctr_set_enable(u64 *state, int ctr_set) static inline void ctr_set_enable(u64 *state, u64 ctrsets)
{
*state |= cpumf_ctr_ctl[ctr_set] << CPUMF_LCCTL_ENABLE_SHIFT;
}
static inline void ctr_set_disable(u64 *state, int ctr_set)
{
*state &= ~(cpumf_ctr_ctl[ctr_set] << CPUMF_LCCTL_ENABLE_SHIFT);
}
static inline void ctr_set_start(u64 *state, int ctr_set)
{
*state |= cpumf_ctr_ctl[ctr_set] << CPUMF_LCCTL_ACTCTL_SHIFT;
}
static inline void ctr_set_stop(u64 *state, int ctr_set)
{
*state &= ~(cpumf_ctr_ctl[ctr_set] << CPUMF_LCCTL_ACTCTL_SHIFT);
}
static inline void ctr_set_multiple_enable(u64 *state, u64 ctrsets)
{ {
*state |= ctrsets << CPUMF_LCCTL_ENABLE_SHIFT; *state |= ctrsets << CPUMF_LCCTL_ENABLE_SHIFT;
} }
static inline void ctr_set_multiple_disable(u64 *state, u64 ctrsets) static inline void ctr_set_disable(u64 *state, u64 ctrsets)
{ {
*state &= ~(ctrsets << CPUMF_LCCTL_ENABLE_SHIFT); *state &= ~(ctrsets << CPUMF_LCCTL_ENABLE_SHIFT);
} }
static inline void ctr_set_multiple_start(u64 *state, u64 ctrsets) static inline void ctr_set_start(u64 *state, u64 ctrsets)
{ {
*state |= ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT; *state |= ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT;
} }
static inline void ctr_set_multiple_stop(u64 *state, u64 ctrsets) static inline void ctr_set_stop(u64 *state, u64 ctrsets)
{ {
*state &= ~(ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT); *state &= ~(ctrsets << CPUMF_LCCTL_ACTCTL_SHIFT);
} }
...@@ -92,8 +75,15 @@ struct cpu_cf_events { ...@@ -92,8 +75,15 @@ struct cpu_cf_events {
struct cpumf_ctr_info info; struct cpumf_ctr_info info;
atomic_t ctr_set[CPUMF_CTR_SET_MAX]; atomic_t ctr_set[CPUMF_CTR_SET_MAX];
atomic64_t alert; atomic64_t alert;
u64 state; u64 state; /* For perf_event_open SVC */
u64 dev_state; /* For /dev/hwctr */
unsigned int flags; unsigned int flags;
size_t used; /* Bytes used in data */
size_t usedss; /* Bytes used in start/stop */
unsigned char start[PAGE_SIZE]; /* Counter set at event add */
unsigned char stop[PAGE_SIZE]; /* Counter set at event delete */
unsigned char data[PAGE_SIZE]; /* Counter set at /dev/hwctr */
unsigned int sets; /* # Counter set saved in memory */
}; };
DECLARE_PER_CPU(struct cpu_cf_events, cpu_cf_events); DECLARE_PER_CPU(struct cpu_cf_events, cpu_cf_events);
...@@ -124,4 +114,6 @@ static inline int stccm_avail(void) ...@@ -124,4 +114,6 @@ static inline int stccm_avail(void)
size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset, size_t cpum_cf_ctrset_size(enum cpumf_ctr_set ctrset,
struct cpumf_ctr_info *info); struct cpumf_ctr_info *info);
int cfset_online_cpu(unsigned int cpu);
int cfset_offline_cpu(unsigned int cpu);
#endif /* _ASM_S390_CPU_MCF_H */ #endif /* _ASM_S390_CPU_MCF_H */
...@@ -21,8 +21,6 @@ ...@@ -21,8 +21,6 @@
#define CR0_INTERRUPT_KEY_SUBMASK BIT(63 - 57) #define CR0_INTERRUPT_KEY_SUBMASK BIT(63 - 57)
#define CR0_MEASUREMENT_ALERT_SUBMASK BIT(63 - 58) #define CR0_MEASUREMENT_ALERT_SUBMASK BIT(63 - 58)
#define CR2_GUARDED_STORAGE BIT(63 - 59)
#define CR14_UNUSED_32 BIT(63 - 32) #define CR14_UNUSED_32 BIT(63 - 32)
#define CR14_UNUSED_33 BIT(63 - 33) #define CR14_UNUSED_33 BIT(63 - 33)
#define CR14_CHANNEL_REPORT_SUBMASK BIT(63 - 35) #define CR14_CHANNEL_REPORT_SUBMASK BIT(63 - 35)
......
...@@ -144,10 +144,6 @@ typedef s390_compat_regs compat_elf_gregset_t; ...@@ -144,10 +144,6 @@ typedef s390_compat_regs compat_elf_gregset_t;
#include <linux/sched/mm.h> /* for task_struct */ #include <linux/sched/mm.h> /* for task_struct */
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/vdso.h>
extern unsigned int vdso_enabled;
/* /*
* This is used to ensure we don't load something for the wrong architecture. * This is used to ensure we don't load something for the wrong architecture.
*/ */
...@@ -176,7 +172,7 @@ struct arch_elf_state { ...@@ -176,7 +172,7 @@ struct arch_elf_state {
!current->mm->context.alloc_pgste) { \ !current->mm->context.alloc_pgste) { \
set_thread_flag(TIF_PGSTE); \ set_thread_flag(TIF_PGSTE); \
set_pt_regs_flag(task_pt_regs(current), \ set_pt_regs_flag(task_pt_regs(current), \
PIF_SYSCALL_RESTART); \ PIF_EXECVE_PGSTE_RESTART); \
_state->rc = -EAGAIN; \ _state->rc = -EAGAIN; \
} \ } \
_state->rc; \ _state->rc; \
...@@ -268,11 +264,10 @@ do { \ ...@@ -268,11 +264,10 @@ do { \
#define STACK_RND_MASK MMAP_RND_MASK #define STACK_RND_MASK MMAP_RND_MASK
/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */ /* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
#define ARCH_DLINFO \ #define ARCH_DLINFO \
do { \ do { \
if (vdso_enabled) \ NEW_AUX_ENT(AT_SYSINFO_EHDR, \
NEW_AUX_ENT(AT_SYSINFO_EHDR, \ (unsigned long)current->mm->context.vdso_base); \
(unsigned long)current->mm->context.vdso_base); \
} while (0) } while (0)
struct linux_binprm; struct linux_binprm;
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#define ARCH_EXIT_TO_USER_MODE_WORK (_TIF_GUARDED_STORAGE | _TIF_PER_TRAP) #define ARCH_EXIT_TO_USER_MODE_WORK (_TIF_GUARDED_STORAGE | _TIF_PER_TRAP)
void do_per_trap(struct pt_regs *regs); void do_per_trap(struct pt_regs *regs);
void do_syscall(struct pt_regs *regs);
#ifdef CONFIG_DEBUG_ENTRY #ifdef CONFIG_DEBUG_ENTRY
static __always_inline void arch_check_user_regs(struct pt_regs *regs) static __always_inline void arch_check_user_regs(struct pt_regs *regs)
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
#include <asm/asm-const.h> #include <asm/asm-const.h>
#include <linux/stringify.h> #include <linux/stringify.h>
#define __ALIGN .align 4, 0x07 #define __ALIGN .align 16, 0x07
#define __ALIGN_STR __stringify(__ALIGN) #define __ALIGN_STR __stringify(__ALIGN)
/* /*
......
...@@ -23,12 +23,16 @@ ...@@ -23,12 +23,16 @@
#define MCCK_CODE_SYSTEM_DAMAGE BIT(63) #define MCCK_CODE_SYSTEM_DAMAGE BIT(63)
#define MCCK_CODE_EXT_DAMAGE BIT(63 - 5) #define MCCK_CODE_EXT_DAMAGE BIT(63 - 5)
#define MCCK_CODE_CP BIT(63 - 9) #define MCCK_CODE_CP BIT(63 - 9)
#define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46) #define MCCK_CODE_STG_ERROR BIT(63 - 16)
#define MCCK_CODE_STG_KEY_ERROR BIT(63 - 18)
#define MCCK_CODE_STG_DEGRAD BIT(63 - 19)
#define MCCK_CODE_PSW_MWP_VALID BIT(63 - 20) #define MCCK_CODE_PSW_MWP_VALID BIT(63 - 20)
#define MCCK_CODE_PSW_IA_VALID BIT(63 - 23) #define MCCK_CODE_PSW_IA_VALID BIT(63 - 23)
#define MCCK_CODE_STG_FAIL_ADDR BIT(63 - 24)
#define MCCK_CODE_CR_VALID BIT(63 - 29) #define MCCK_CODE_CR_VALID BIT(63 - 29)
#define MCCK_CODE_GS_VALID BIT(63 - 36) #define MCCK_CODE_GS_VALID BIT(63 - 36)
#define MCCK_CODE_FC_VALID BIT(63 - 43) #define MCCK_CODE_FC_VALID BIT(63 - 43)
#define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
......
...@@ -29,12 +29,6 @@ static inline void preempt_count_set(int pc) ...@@ -29,12 +29,6 @@ static inline void preempt_count_set(int pc)
old, new) != old); old, new) != old);
} }
#define init_task_preempt_count(p) do { } while (0)
#define init_idle_preempt_count(p, cpu) do { \
S390_lowcore.preempt_count = PREEMPT_DISABLED; \
} while (0)
static inline void set_preempt_need_resched(void) static inline void set_preempt_need_resched(void)
{ {
__atomic_and(~PREEMPT_NEED_RESCHED, &S390_lowcore.preempt_count); __atomic_and(~PREEMPT_NEED_RESCHED, &S390_lowcore.preempt_count);
...@@ -88,12 +82,6 @@ static inline void preempt_count_set(int pc) ...@@ -88,12 +82,6 @@ static inline void preempt_count_set(int pc)
S390_lowcore.preempt_count = pc; S390_lowcore.preempt_count = pc;
} }
#define init_task_preempt_count(p) do { } while (0)
#define init_idle_preempt_count(p, cpu) do { \
S390_lowcore.preempt_count = PREEMPT_DISABLED; \
} while (0)
static inline void set_preempt_need_resched(void) static inline void set_preempt_need_resched(void)
{ {
} }
...@@ -130,6 +118,10 @@ static inline bool should_resched(int preempt_offset) ...@@ -130,6 +118,10 @@ static inline bool should_resched(int preempt_offset)
#endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
#define init_task_preempt_count(p) do { } while (0)
/* Deferred to CPU bringup time */
#define init_idle_preempt_count(p, cpu) do { } while (0)
#ifdef CONFIG_PREEMPTION #ifdef CONFIG_PREEMPTION
extern void preempt_schedule(void); extern void preempt_schedule(void);
#define __preempt_schedule() preempt_schedule() #define __preempt_schedule() preempt_schedule()
......
...@@ -11,15 +11,15 @@ ...@@ -11,15 +11,15 @@
#include <uapi/asm/ptrace.h> #include <uapi/asm/ptrace.h>
#include <asm/tpi.h> #include <asm/tpi.h>
#define PIF_SYSCALL 0 /* inside a system call */ #define PIF_SYSCALL 0 /* inside a system call */
#define PIF_SYSCALL_RESTART 1 /* restart the current system call */ #define PIF_EXECVE_PGSTE_RESTART 1 /* restart execve for PGSTE binaries */
#define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */ #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_SYSCALL_RESTART BIT(PIF_SYSCALL_RESTART) #define _PIF_EXECVE_PGSTE_RESTART BIT(PIF_EXECVE_PGSTE_RESTART)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET) #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__
...@@ -162,6 +162,14 @@ static inline int test_pt_regs_flag(struct pt_regs *regs, int flag) ...@@ -162,6 +162,14 @@ static inline int test_pt_regs_flag(struct pt_regs *regs, int flag)
return !!(regs->flags & (1UL << flag)); return !!(regs->flags & (1UL << flag));
} }
static inline int test_and_clear_pt_regs_flag(struct pt_regs *regs, int flag)
{
int ret = test_pt_regs_flag(regs, flag);
clear_pt_regs_flag(regs, flag);
return ret;
}
/* /*
* These are defined as per linux/ptrace.h, which see. * These are defined as per linux/ptrace.h, which see.
*/ */
......
...@@ -159,6 +159,8 @@ static inline unsigned long kaslr_offset(void) ...@@ -159,6 +159,8 @@ static inline unsigned long kaslr_offset(void)
return __kaslr_offset; return __kaslr_offset;
} }
extern int is_full_image;
static inline u32 gen_lpswe(unsigned long addr) static inline u32 gen_lpswe(unsigned long addr)
{ {
BUILD_BUG_ON(addr > 0xfff); BUILD_BUG_ON(addr > 0xfff);
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef __ASM_S390_SOFTIRQ_STACK_H
#define __ASM_S390_SOFTIRQ_STACK_H
#include <asm/lowcore.h>
#include <asm/stacktrace.h>
static inline void do_softirq_own_stack(void)
{
call_on_stack(0, S390_lowcore.async_stack, void, __do_softirq);
}
#endif /* __ASM_S390_SOFTIRQ_STACK_H */
...@@ -74,23 +74,6 @@ struct stack_frame { ...@@ -74,23 +74,6 @@ struct stack_frame {
((unsigned long)__builtin_frame_address(0) - \ ((unsigned long)__builtin_frame_address(0) - \
offsetof(struct stack_frame, back_chain)) offsetof(struct stack_frame, back_chain))
#define CALL_ARGS_0() \
register unsigned long r2 asm("2")
#define CALL_ARGS_1(arg1) \
register unsigned long r2 asm("2") = (unsigned long)(arg1)
#define CALL_ARGS_2(arg1, arg2) \
CALL_ARGS_1(arg1); \
register unsigned long r3 asm("3") = (unsigned long)(arg2)
#define CALL_ARGS_3(arg1, arg2, arg3) \
CALL_ARGS_2(arg1, arg2); \
register unsigned long r4 asm("4") = (unsigned long)(arg3)
#define CALL_ARGS_4(arg1, arg2, arg3, arg4) \
CALL_ARGS_3(arg1, arg2, arg3); \
register unsigned long r4 asm("5") = (unsigned long)(arg4)
#define CALL_ARGS_5(arg1, arg2, arg3, arg4, arg5) \
CALL_ARGS_4(arg1, arg2, arg3, arg4); \
register unsigned long r4 asm("6") = (unsigned long)(arg5)
/* /*
* To keep this simple mark register 2-6 as being changed (volatile) * To keep this simple mark register 2-6 as being changed (volatile)
* by the called function, even though register 6 is saved/nonvolatile. * by the called function, even though register 6 is saved/nonvolatile.
...@@ -109,34 +92,113 @@ struct stack_frame { ...@@ -109,34 +92,113 @@ struct stack_frame {
#define CALL_CLOBBER_1 CALL_CLOBBER_2, "3" #define CALL_CLOBBER_1 CALL_CLOBBER_2, "3"
#define CALL_CLOBBER_0 CALL_CLOBBER_1 #define CALL_CLOBBER_0 CALL_CLOBBER_1
#define CALL_ON_STACK(fn, stack, nr, args...) \ #define CALL_LARGS_0(...) \
long dummy = 0
#define CALL_LARGS_1(t1, a1) \
long arg1 = (long)(t1)(a1)
#define CALL_LARGS_2(t1, a1, t2, a2) \
CALL_LARGS_1(t1, a1); \
long arg2 = (long)(t2)(a2)
#define CALL_LARGS_3(t1, a1, t2, a2, t3, a3) \
CALL_LARGS_2(t1, a1, t2, a2); \
long arg3 = (long)(t3)(a3)
#define CALL_LARGS_4(t1, a1, t2, a2, t3, a3, t4, a4) \
CALL_LARGS_3(t1, a1, t2, a2, t3, a3); \
long arg4 = (long)(t4)(a4)
#define CALL_LARGS_5(t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \
CALL_LARGS_4(t1, a1, t2, a2, t3, a3, t4, a4); \
long arg5 = (long)(t5)(a5)
#define CALL_REGS_0 \
register long r2 asm("2") = dummy
#define CALL_REGS_1 \
register long r2 asm("2") = arg1
#define CALL_REGS_2 \
CALL_REGS_1; \
register long r3 asm("3") = arg2
#define CALL_REGS_3 \
CALL_REGS_2; \
register long r4 asm("4") = arg3
#define CALL_REGS_4 \
CALL_REGS_3; \
register long r5 asm("5") = arg4
#define CALL_REGS_5 \
CALL_REGS_4; \
register long r6 asm("6") = arg5
#define CALL_TYPECHECK_0(...)
#define CALL_TYPECHECK_1(t, a, ...) \
typecheck(t, a)
#define CALL_TYPECHECK_2(t, a, ...) \
CALL_TYPECHECK_1(__VA_ARGS__); \
typecheck(t, a)
#define CALL_TYPECHECK_3(t, a, ...) \
CALL_TYPECHECK_2(__VA_ARGS__); \
typecheck(t, a)
#define CALL_TYPECHECK_4(t, a, ...) \
CALL_TYPECHECK_3(__VA_ARGS__); \
typecheck(t, a)
#define CALL_TYPECHECK_5(t, a, ...) \
CALL_TYPECHECK_4(__VA_ARGS__); \
typecheck(t, a)
#define CALL_PARM_0(...) void
#define CALL_PARM_1(t, a, ...) t
#define CALL_PARM_2(t, a, ...) t, CALL_PARM_1(__VA_ARGS__)
#define CALL_PARM_3(t, a, ...) t, CALL_PARM_2(__VA_ARGS__)
#define CALL_PARM_4(t, a, ...) t, CALL_PARM_3(__VA_ARGS__)
#define CALL_PARM_5(t, a, ...) t, CALL_PARM_4(__VA_ARGS__)
#define CALL_PARM_6(t, a, ...) t, CALL_PARM_5(__VA_ARGS__)
/*
* Use call_on_stack() to call a function switching to a specified
* stack. Proper sign and zero extension of function arguments is
* done. Usage:
*
* rc = call_on_stack(nr, stack, rettype, fn, t1, a1, t2, a2, ...)
*
* - nr specifies the number of function arguments of fn.
* - stack specifies the stack to be used.
* - fn is the function to be called.
* - rettype is the return type of fn.
* - t1, a1, ... are pairs, where t1 must match the type of the first
* argument of fn, t2 the second, etc. a1 is the corresponding
* first function argument (not name), etc.
*/
#define call_on_stack(nr, stack, rettype, fn, ...) \
({ \ ({ \
rettype (*__fn)(CALL_PARM_##nr(__VA_ARGS__)) = fn; \
unsigned long frame = current_frame_address(); \ unsigned long frame = current_frame_address(); \
CALL_ARGS_##nr(args); \ unsigned long __stack = stack; \
unsigned long prev; \ unsigned long prev; \
CALL_LARGS_##nr(__VA_ARGS__); \
CALL_REGS_##nr; \
\ \
CALL_TYPECHECK_##nr(__VA_ARGS__); \
asm volatile( \ asm volatile( \
" la %[_prev],0(15)\n" \ " lgr %[_prev],15\n" \
" lg 15,%[_stack]\n" \ " lg 15,%[_stack]\n" \
" stg %[_frame],%[_bc](15)\n" \ " stg %[_frame],%[_bc](15)\n" \
" brasl 14,%[_fn]\n" \ " brasl 14,%[_fn]\n" \
" la 15,0(%[_prev])\n" \ " lgr 15,%[_prev]\n" \
: [_prev] "=&a" (prev), CALL_FMT_##nr \ : [_prev] "=&d" (prev), CALL_FMT_##nr \
: [_stack] "R" (stack), \ : [_stack] "R" (__stack), \
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \ [_bc] "i" (offsetof(struct stack_frame, back_chain)), \
[_frame] "d" (frame), \ [_frame] "d" (frame), \
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \ [_fn] "X" (__fn) : CALL_CLOBBER_##nr); \
r2; \ (rettype)r2; \
}) })
#define CALL_ON_STACK_NORETURN(fn, stack) \ #define call_on_stack_noreturn(fn, stack) \
({ \ ({ \
void (*__fn)(void) = fn; \
\
asm volatile( \ asm volatile( \
" la 15,0(%[_stack])\n" \ " la 15,0(%[_stack])\n" \
" xc %[_bc](8,15),%[_bc](15)\n" \ " xc %[_bc](8,15),%[_bc](15)\n" \
" brasl 14,%[_fn]\n" \ " brasl 14,%[_fn]\n" \
::[_bc] "i" (offsetof(struct stack_frame, back_chain)), \ ::[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
[_stack] "a" (stack), [_fn] "X" (fn)); \ [_stack] "a" (stack), [_fn] "X" (__fn)); \
BUG(); \ BUG(); \
}) })
......
...@@ -73,6 +73,10 @@ enum uv_cmds_inst { ...@@ -73,6 +73,10 @@ enum uv_cmds_inst {
BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22,
}; };
enum uv_feat_ind {
BIT_UV_FEAT_MISC = 0,
};
struct uv_cb_header { struct uv_cb_header {
u16 len; u16 len;
u16 cmd; /* Command Code */ u16 cmd; /* Command Code */
...@@ -97,7 +101,8 @@ struct uv_cb_qui { ...@@ -97,7 +101,8 @@ struct uv_cb_qui {
u64 max_guest_stor_addr; u64 max_guest_stor_addr;
u8 reserved88[158 - 136]; u8 reserved88[158 - 136];
u16 max_guest_cpu_id; u16 max_guest_cpu_id;
u8 reserveda0[200 - 160]; u64 uv_feature_indications;
u8 reserveda0[200 - 168];
} __packed __aligned(8); } __packed __aligned(8);
/* Initialize Ultravisor */ /* Initialize Ultravisor */
...@@ -274,6 +279,7 @@ struct uv_info { ...@@ -274,6 +279,7 @@ struct uv_info {
unsigned long max_sec_stor_addr; unsigned long max_sec_stor_addr;
unsigned int max_num_sec_conf; unsigned int max_num_sec_conf;
unsigned short max_guest_cpu_id; unsigned short max_guest_cpu_id;
unsigned long uv_feature_indications;
}; };
extern struct uv_info uv_info; extern struct uv_info uv_info;
......
...@@ -4,18 +4,31 @@ ...@@ -4,18 +4,31 @@
#include <vdso/datapage.h> #include <vdso/datapage.h>
/* Default link address for the vDSO */ #ifndef __ASSEMBLY__
#define VDSO64_LBASE 0
#define __VVAR_PAGES 2 #include <generated/vdso64-offsets.h>
#ifdef CONFIG_COMPAT
#include <generated/vdso32-offsets.h>
#endif
#define VDSO_VERSION_STRING LINUX_2.6.29 #define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
#ifdef CONFIG_COMPAT
#ifndef __ASSEMBLY__ #define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name))
#else
#define VDSO32_SYMBOL(tsk, name) (-1UL)
#endif
extern struct vdso_data *vdso_data; extern struct vdso_data *vdso_data;
int vdso_getcpu_init(void); int vdso_getcpu_init(void);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
/* Default link address for the vDSO */
#define VDSO_LBASE 0
#define __VVAR_PAGES 2
#define VDSO_VERSION_STRING LINUX_2.6.29
#endif /* __S390_VDSO_H__ */ #endif /* __S390_VDSO_H__ */
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include <asm/timex.h> #include <asm/timex.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <asm/vdso.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#define vdso_calc_delta __arch_vdso_calc_delta #define vdso_calc_delta __arch_vdso_calc_delta
......
...@@ -71,10 +71,10 @@ obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o ...@@ -71,10 +71,10 @@ obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_events.o perf_regs.o
obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf_diag.o
obj-$(CONFIG_TRACEPOINTS) += trace.o obj-$(CONFIG_TRACEPOINTS) += trace.o
obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_PGSTE)) += uv.o obj-$(findstring y, $(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) $(CONFIG_PGSTE)) += uv.o
# vdso # vdso
obj-y += vdso64/ obj-y += vdso64/
obj-$(CONFIG_COMPAT) += vdso32/
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include <linux/pgtable.h> #include <linux/pgtable.h>
#include <asm/idle.h> #include <asm/idle.h>
#include <asm/gmap.h> #include <asm/gmap.h>
#include <asm/nmi.h>
#include <asm/setup.h>
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
int main(void) int main(void)
...@@ -108,7 +106,6 @@ int main(void) ...@@ -108,7 +106,6 @@ int main(void)
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);
OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock); OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
OFFSET(__LC_CLOCK_COMPARATOR, lowcore, clock_comparator);
OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock); OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock);
OFFSET(__LC_CURRENT, lowcore, current_task); OFFSET(__LC_CURRENT, lowcore, current_task);
OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack); OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
...@@ -145,9 +142,6 @@ int main(void) ...@@ -145,9 +142,6 @@ int main(void)
OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area); OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area);
OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb); OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb);
BLANK(); BLANK();
/* extended machine check save area */
OFFSET(__MCESA_GS_SAVE_AREA, mcesa, guarded_storage_save_area);
BLANK();
/* gmap/sie offsets */ /* gmap/sie offsets */
OFFSET(__GMAP_ASCE, gmap, asce); OFFSET(__GMAP_ASCE, gmap, asce);
OFFSET(__SIE_PROG0C, kvm_s390_sie_block, prog0c); OFFSET(__SIE_PROG0C, kvm_s390_sie_block, prog0c);
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include <asm/vdso.h>
#include "compat_linux.h" #include "compat_linux.h"
#include "compat_ptrace.h" #include "compat_ptrace.h"
#include "entry.h" #include "entry.h"
...@@ -118,7 +119,6 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -118,7 +119,6 @@ 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;
} }
...@@ -304,11 +304,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, ...@@ -304,11 +304,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set,
restorer = (unsigned long __force) restorer = (unsigned long __force)
ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE;
} else { } else {
/* Signal frames without vectors registers are short ! */ restorer = VDSO32_SYMBOL(current, sigreturn);
__u16 __user *svc = (void __user *) frame + frame_size - 2;
if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc))
return -EFAULT;
restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE;
} }
/* Set up registers for signal handler */ /* Set up registers for signal handler */
...@@ -371,10 +367,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, ...@@ -371,10 +367,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set,
restorer = (unsigned long __force) restorer = (unsigned long __force)
ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE;
} else { } else {
__u16 __user *svc = &frame->svc_insn; restorer = VDSO32_SYMBOL(current, rt_sigreturn);
if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc))
return -EFAULT;
restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE;
} }
/* Create siginfo on the signal stack */ /* Create siginfo on the signal stack */
......
...@@ -33,6 +33,8 @@ ...@@ -33,6 +33,8 @@
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include "entry.h" #include "entry.h"
int __bootdata(is_full_image);
static void __init reset_tod_clock(void) static void __init reset_tod_clock(void)
{ {
union tod_clock clk; union tod_clock clk;
...@@ -279,7 +281,7 @@ static void __init setup_boot_command_line(void) ...@@ -279,7 +281,7 @@ static void __init setup_boot_command_line(void)
static void __init check_image_bootable(void) static void __init check_image_bootable(void)
{ {
if (!memcmp(EP_STRING, (void *)EP_OFFSET, strlen(EP_STRING))) if (is_full_image)
return; return;
sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n"); sclp_early_printk("Linux kernel boot failure: An attempt to boot a vmlinux ELF image failed.\n");
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include <asm/alternative-asm.h> #include <asm/alternative-asm.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/cache.h> #include <asm/cache.h>
#include <asm/ctl_reg.h>
#include <asm/dwarf.h> #include <asm/dwarf.h>
#include <asm/errno.h> #include <asm/errno.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
...@@ -129,6 +128,24 @@ _LPP_OFFSET = __LC_LPP ...@@ -129,6 +128,24 @@ _LPP_OFFSET = __LC_LPP
"jnz .+8; .long 0xb2e8d000", 82 "jnz .+8; .long 0xb2e8d000", 82
.endm .endm
/*
* The CHKSTG macro jumps to the provided label in case the
* machine check interruption code reports one of unrecoverable
* storage errors:
* - Storage error uncorrected
* - Storage key error uncorrected
* - Storage degradation with Failing-storage-address validity
*/
.macro CHKSTG errlabel
TSTMSK __LC_MCCK_CODE,(MCCK_CODE_STG_ERROR|MCCK_CODE_STG_KEY_ERROR)
jnz \errlabel
TSTMSK __LC_MCCK_CODE,MCCK_CODE_STG_DEGRAD
jz oklabel\@
TSTMSK __LC_MCCK_CODE,MCCK_CODE_STG_FAIL_ADDR
jnz \errlabel
oklabel\@:
.endm
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
/* /*
* The OUTSIDE macro jumps to the provided label in case the value * The OUTSIDE macro jumps to the provided label in case the value
...@@ -148,6 +165,13 @@ _LPP_OFFSET = __LC_LPP ...@@ -148,6 +165,13 @@ _LPP_OFFSET = __LC_LPP
clgr %r14,%r13 clgr %r14,%r13
jhe \outside_label jhe \outside_label
.endm .endm
.macro SIEEXIT
lg %r9,__SF_SIE_CONTROL(%r15) # get control block pointer
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE
lctlg %c1,%c1,__LC_KERNEL_ASCE # load primary asce
larl %r9,sie_exit # skip forward to sie_exit
.endm
#endif #endif
GEN_BR_THUNK %r14 GEN_BR_THUNK %r14
...@@ -235,7 +259,6 @@ ENTRY(sie64a) ...@@ -235,7 +259,6 @@ ENTRY(sie64a)
# are some corner cases (e.g. runtime instrumentation) where ILC is unpredictable. # are some corner cases (e.g. runtime instrumentation) where ILC is unpredictable.
# Other instructions between sie64a and .Lsie_done should not cause program # Other instructions between sie64a and .Lsie_done should not cause program
# interrupts. So lets use 3 nops as a landing pad for all possible rewinds. # interrupts. So lets use 3 nops as a landing pad for all possible rewinds.
# See also .Lcleanup_sie
.Lrewind_pad6: .Lrewind_pad6:
nopr 7 nopr 7
.Lrewind_pad4: .Lrewind_pad4:
...@@ -341,10 +364,7 @@ ENTRY(pgm_check_handler) ...@@ -341,10 +364,7 @@ ENTRY(pgm_check_handler)
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
# cleanup critical section for program checks in sie64a # cleanup critical section for program checks in sie64a
OUTSIDE %r9,.Lsie_gmap,.Lsie_done,1f OUTSIDE %r9,.Lsie_gmap,.Lsie_done,1f
lg %r14,__SF_SIE_CONTROL(%r15) # get control block pointer SIEEXIT
ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE
lctlg %c1,%c1,__LC_KERNEL_ASCE # load primary asce
larl %r9,sie_exit # skip forward to sie_exit
lghi %r10,_PIF_GUEST_FAULT lghi %r10,_PIF_GUEST_FAULT
#endif #endif
1: tmhh %r8,0x4000 # PER bit set in old PSW ? 1: tmhh %r8,0x4000 # PER bit set in old PSW ?
...@@ -410,7 +430,8 @@ ENTRY(\name) ...@@ -410,7 +430,8 @@ ENTRY(\name)
jnz 1f jnz 1f
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
OUTSIDE %r9,.Lsie_gmap,.Lsie_done,0f OUTSIDE %r9,.Lsie_gmap,.Lsie_done,0f
brasl %r14,.Lcleanup_sie BPENTER __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
SIEEXIT
#endif #endif
0: CHECK_STACK __LC_SAVE_AREA_ASYNC 0: CHECK_STACK __LC_SAVE_AREA_ASYNC
aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE) aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
...@@ -484,8 +505,6 @@ ENTRY(mcck_int_handler) ...@@ -484,8 +505,6 @@ ENTRY(mcck_int_handler)
BPOFF BPOFF
la %r1,4095 # validate r1 la %r1,4095 # validate r1
spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # validate cpu timer spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # validate cpu timer
sckc __LC_CLOCK_COMPARATOR # validate comparator
lam %a0,%a15,__LC_AREGS_SAVE_AREA-4095(%r1) # validate acrs
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# validate gprs lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# validate gprs
lg %r12,__LC_CURRENT lg %r12,__LC_CURRENT
lmg %r8,%r9,__LC_MCK_OLD_PSW lmg %r8,%r9,__LC_MCK_OLD_PSW
...@@ -496,41 +515,7 @@ ENTRY(mcck_int_handler) ...@@ -496,41 +515,7 @@ ENTRY(mcck_int_handler)
la %r14,4095 la %r14,4095
lctlg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs lctlg %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r14) # validate ctl regs
ptlb ptlb
lg %r11,__LC_MCESAD-4095(%r14) # extended machine check save area lghi %r14,__LC_CPU_TIMER_SAVE_AREA
nill %r11,0xfc00 # MCESA_ORIGIN_MASK
TSTMSK __LC_CREGS_SAVE_AREA+16-4095(%r14),CR2_GUARDED_STORAGE
jno 0f
TSTMSK __LC_MCCK_CODE,MCCK_CODE_GS_VALID
jno 0f
.insn rxy,0xe3000000004d,0,__MCESA_GS_SAVE_AREA(%r11) # LGSC
0: l %r14,__LC_FP_CREG_SAVE_AREA-4095(%r14)
TSTMSK __LC_MCCK_CODE,MCCK_CODE_FC_VALID
jo 0f
sr %r14,%r14
0: sfpc %r14
TSTMSK __LC_MACHINE_FLAGS,MACHINE_FLAG_VX
jo 0f
lghi %r14,__LC_FPREGS_SAVE_AREA
ld %f0,0(%r14)
ld %f1,8(%r14)
ld %f2,16(%r14)
ld %f3,24(%r14)
ld %f4,32(%r14)
ld %f5,40(%r14)
ld %f6,48(%r14)
ld %f7,56(%r14)
ld %f8,64(%r14)
ld %f9,72(%r14)
ld %f10,80(%r14)
ld %f11,88(%r14)
ld %f12,96(%r14)
ld %f13,104(%r14)
ld %f14,112(%r14)
ld %f15,120(%r14)
j 1f
0: VLM %v0,%v15,0,%r11
VLM %v16,%v31,256,%r11
1: lghi %r14,__LC_CPU_TIMER_SAVE_AREA
mvc __LC_MCCK_ENTER_TIMER(8),0(%r14) mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
TSTMSK __LC_MCCK_CODE,MCCK_CODE_CPU_TIMER_VALID TSTMSK __LC_MCCK_CODE,MCCK_CODE_CPU_TIMER_VALID
jo 3f jo 3f
...@@ -546,24 +531,29 @@ ENTRY(mcck_int_handler) ...@@ -546,24 +531,29 @@ ENTRY(mcck_int_handler)
3: TSTMSK __LC_MCCK_CODE,MCCK_CODE_PSW_MWP_VALID 3: TSTMSK __LC_MCCK_CODE,MCCK_CODE_PSW_MWP_VALID
jno .Lmcck_panic jno .Lmcck_panic
tmhh %r8,0x0001 # interrupting from user ? tmhh %r8,0x0001 # interrupting from user ?
jnz 4f jnz 6f
TSTMSK __LC_MCCK_CODE,MCCK_CODE_PSW_IA_VALID TSTMSK __LC_MCCK_CODE,MCCK_CODE_PSW_IA_VALID
jno .Lmcck_panic jno .Lmcck_panic
4: ssm __LC_PGM_NEW_PSW # turn dat on, keep irqs off
tmhh %r8,0x0001 # interrupting from user ?
jnz .Lmcck_user
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
OUTSIDE %r9,.Lsie_gmap,.Lsie_done,.Lmcck_stack OUTSIDE %r9,.Lsie_gmap,.Lsie_done,6f
OUTSIDE %r9,.Lsie_entry,.Lsie_skip,5f OUTSIDE %r9,.Lsie_entry,.Lsie_skip,4f
oi __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST oi __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST
5: brasl %r14,.Lcleanup_sie j 5f
#endif 4: CHKSTG .Lmcck_panic
5: larl %r14,.Lstosm_tmp
stosm 0(%r14),0x04 # turn dat on, keep irqs off
BPENTER __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
SIEEXIT
j .Lmcck_stack j .Lmcck_stack
.Lmcck_user: #endif
6: CHKSTG .Lmcck_panic
larl %r14,.Lstosm_tmp
stosm 0(%r14),0x04 # turn dat on, keep irqs off
tmhh %r8,0x0001 # interrupting from user ?
jz .Lmcck_stack
BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP BPENTER __TI_flags(%r12),_TIF_ISOLATE_BP
.Lmcck_stack: .Lmcck_stack:
lg %r15,__LC_MCCK_STACK lg %r15,__LC_MCCK_STACK
.Lmcck_skip:
la %r11,STACK_FRAME_OVERHEAD(%r15) la %r11,STACK_FRAME_OVERHEAD(%r15)
stctg %c1,%c1,__PT_CR1(%r11) stctg %c1,%c1,__PT_CR1(%r11)
lctlg %c1,%c1,__LC_KERNEL_ASCE lctlg %c1,%c1,__LC_KERNEL_ASCE
...@@ -605,8 +595,33 @@ ENTRY(mcck_int_handler) ...@@ -605,8 +595,33 @@ ENTRY(mcck_int_handler)
b __LC_RETURN_MCCK_LPSWE b __LC_RETURN_MCCK_LPSWE
.Lmcck_panic: .Lmcck_panic:
lg %r15,__LC_NODAT_STACK /*
j .Lmcck_skip * Iterate over all possible CPU addresses in the range 0..0xffff
* and stop each CPU using signal processor. Use compare and swap
* to allow just one CPU-stopper and prevent concurrent CPUs from
* stopping each other while leaving the others running.
*/
lhi %r5,0
lhi %r6,1
larl %r7,.Lstop_lock
cs %r5,%r6,0(%r7) # single CPU-stopper only
jnz 4f
larl %r7,.Lthis_cpu
stap 0(%r7) # this CPU address
lh %r4,0(%r7)
nilh %r4,0
lhi %r0,1
sll %r0,16 # CPU counter
lhi %r3,0 # next CPU address
0: cr %r3,%r4
je 2f
1: sigp %r1,%r3,SIGP_STOP # stop next CPU
brc SIGP_CC_BUSY,1b
2: ahi %r3,1
brct %r0,0b
3: sigp %r1,%r4,SIGP_STOP # stop this CPU
brc SIGP_CC_BUSY,3b
4: j 4b
ENDPROC(mcck_int_handler) ENDPROC(mcck_int_handler)
# #
...@@ -657,15 +672,11 @@ ENTRY(stack_overflow) ...@@ -657,15 +672,11 @@ ENTRY(stack_overflow)
ENDPROC(stack_overflow) ENDPROC(stack_overflow)
#endif #endif
#if IS_ENABLED(CONFIG_KVM) .section .data, "aw"
.Lcleanup_sie: .align 4
BPENTER __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST) .Lstop_lock: .long 0
lg %r9,__SF_SIE_CONTROL(%r15) # get control block pointer .Lthis_cpu: .short 0
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE .Lstosm_tmp: .byte 0
lctlg %c1,%c1,__LC_KERNEL_ASCE
larl %r9,sie_exit # skip forward to sie_exit
BR_EX %r14,%r13
#endif
.section .rodata, "a" .section .rodata, "a"
#define SYSCALL(esame,emu) .quad __s390x_ ## esame #define SYSCALL(esame,emu) .quad __s390x_ ## esame
.globl sys_call_table .globl sys_call_table
......
...@@ -110,15 +110,17 @@ static int on_async_stack(void) ...@@ -110,15 +110,17 @@ static int on_async_stack(void)
{ {
unsigned long frame = current_frame_address(); unsigned long frame = current_frame_address();
return !!!((S390_lowcore.async_stack - frame) >> (PAGE_SHIFT + THREAD_SIZE_ORDER)); return ((S390_lowcore.async_stack ^ frame) & ~(THREAD_SIZE - 1)) == 0;
} }
static void do_irq_async(struct pt_regs *regs, int irq) static void do_irq_async(struct pt_regs *regs, int irq)
{ {
if (on_async_stack()) if (on_async_stack()) {
do_IRQ(regs, irq); do_IRQ(regs, irq);
else } else {
CALL_ON_STACK(do_IRQ, S390_lowcore.async_stack, 2, regs, irq); call_on_stack(2, S390_lowcore.async_stack, void, do_IRQ,
struct pt_regs *, regs, int, irq);
}
} }
static int irq_pending(struct pt_regs *regs) static int irq_pending(struct pt_regs *regs)
...@@ -265,24 +267,6 @@ unsigned int arch_dynirq_lower_bound(unsigned int from) ...@@ -265,24 +267,6 @@ unsigned int arch_dynirq_lower_bound(unsigned int from)
return from < NR_IRQS_BASE ? NR_IRQS_BASE : from; return from < NR_IRQS_BASE ? NR_IRQS_BASE : from;
} }
/*
* Switch to the asynchronous interrupt stack for softirq execution.
*/
void do_softirq_own_stack(void)
{
unsigned long old, new;
old = current_stack_pointer();
/* Check against async. stack address range. */
new = S390_lowcore.async_stack;
if (((new - old) >> (PAGE_SHIFT + THREAD_SIZE_ORDER)) != 0) {
CALL_ON_STACK(__do_softirq, new, 0);
} else {
/* We are already on the async stack. */
__do_softirq();
}
}
/* /*
* ext_int_hash[index] is the list head for all external interrupts that hash * ext_int_hash[index] is the list head for all external interrupts that hash
* to this index. * to this index.
......
...@@ -92,11 +92,6 @@ static void copy_instruction(struct kprobe *p) ...@@ -92,11 +92,6 @@ static void copy_instruction(struct kprobe *p)
} }
NOKPROBE_SYMBOL(copy_instruction); NOKPROBE_SYMBOL(copy_instruction);
static inline int is_kernel_addr(void *addr)
{
return addr < (void *)_end;
}
static int s390_get_insn_slot(struct kprobe *p) static int s390_get_insn_slot(struct kprobe *p)
{ {
/* /*
...@@ -105,7 +100,7 @@ static int s390_get_insn_slot(struct kprobe *p) ...@@ -105,7 +100,7 @@ static int s390_get_insn_slot(struct kprobe *p)
* field can be patched and executed within the insn slot. * field can be patched and executed within the insn slot.
*/ */
p->ainsn.insn = NULL; p->ainsn.insn = NULL;
if (is_kernel_addr(p->addr)) if (is_kernel((unsigned long)p->addr))
p->ainsn.insn = get_s390_insn_slot(); p->ainsn.insn = get_s390_insn_slot();
else if (is_module_addr(p->addr)) else if (is_module_addr(p->addr))
p->ainsn.insn = get_insn_slot(); p->ainsn.insn = get_insn_slot();
...@@ -117,7 +112,7 @@ static void s390_free_insn_slot(struct kprobe *p) ...@@ -117,7 +112,7 @@ static void s390_free_insn_slot(struct kprobe *p)
{ {
if (!p->ainsn.insn) if (!p->ainsn.insn)
return; return;
if (is_kernel_addr(p->addr)) if (is_kernel((unsigned long)p->addr))
free_s390_insn_slot(p->ainsn.insn, 0); free_s390_insn_slot(p->ainsn.insn, 0);
else else
free_insn_slot(p->ainsn.insn, 0); free_insn_slot(p->ainsn.insn, 0);
......
...@@ -132,7 +132,8 @@ static bool kdump_csum_valid(struct kimage *image) ...@@ -132,7 +132,8 @@ static bool kdump_csum_valid(struct kimage *image)
int rc; int rc;
preempt_disable(); preempt_disable();
rc = CALL_ON_STACK(do_start_kdump, S390_lowcore.nodat_stack, 1, image); rc = call_on_stack(1, S390_lowcore.nodat_stack, unsigned long, do_start_kdump,
unsigned long, (unsigned long)image);
preempt_enable(); preempt_enable();
return rc == 0; return rc == 0;
#else #else
......
...@@ -189,12 +189,16 @@ void noinstr s390_handle_mcck(void) ...@@ -189,12 +189,16 @@ void noinstr s390_handle_mcck(void)
* returns 0 if all required registers are available * returns 0 if all required registers are available
* returns 1 otherwise * returns 1 otherwise
*/ */
static int notrace s390_check_registers(union mci mci, int umode) static int notrace s390_validate_registers(union mci mci, int umode)
{ {
struct mcesa *mcesa;
void *fpt_save_area;
union ctlreg2 cr2; union ctlreg2 cr2;
int kill_task; int kill_task;
u64 zero;
kill_task = 0; kill_task = 0;
zero = 0;
if (!mci.gr) { if (!mci.gr) {
/* /*
...@@ -205,14 +209,6 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -205,14 +209,6 @@ static int notrace s390_check_registers(union mci mci, int umode)
s390_handle_damage(); s390_handle_damage();
kill_task = 1; kill_task = 1;
} }
/* Check control registers */
if (!mci.cr) {
/*
* Control registers have unknown contents.
* Can't recover and therefore stopping machine.
*/
s390_handle_damage();
}
if (!mci.fp) { if (!mci.fp) {
/* /*
* Floating point registers can't be restored. If the * Floating point registers can't be restored. If the
...@@ -225,35 +221,89 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -225,35 +221,89 @@ static int notrace s390_check_registers(union mci mci, int umode)
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; kill_task = 1;
} }
fpt_save_area = &S390_lowcore.floating_pt_save_area;
if (!mci.fc) { if (!mci.fc) {
/* /*
* Floating point control register can't be restored. * Floating point control register can't be restored.
* If the kernel currently uses the floating pointer * If the kernel currently uses the floating pointer
* registers and needs the FPC register the system is * registers and needs the FPC register the system is
* stopped. If the process has its floating pointer * stopped. If the process has its floating pointer
* registers loaded it is terminated. * registers loaded it is terminated. Otherwise the
* FPC is just validated.
*/ */
if (S390_lowcore.fpu_flags & KERNEL_FPC) if (S390_lowcore.fpu_flags & KERNEL_FPC)
s390_handle_damage(); s390_handle_damage();
asm volatile(
" lfpc %0\n"
:
: "Q" (zero));
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; kill_task = 1;
} else {
asm volatile(
" lfpc %0\n"
:
: "Q" (S390_lowcore.fpt_creg_save_area));
} }
if (MACHINE_HAS_VX) { mcesa = (struct mcesa *)(S390_lowcore.mcesad & MCESA_ORIGIN_MASK);
if (!MACHINE_HAS_VX) {
/* Validate floating point registers */
asm volatile(
" ld 0,0(%0)\n"
" ld 1,8(%0)\n"
" ld 2,16(%0)\n"
" ld 3,24(%0)\n"
" ld 4,32(%0)\n"
" ld 5,40(%0)\n"
" ld 6,48(%0)\n"
" ld 7,56(%0)\n"
" ld 8,64(%0)\n"
" ld 9,72(%0)\n"
" ld 10,80(%0)\n"
" ld 11,88(%0)\n"
" ld 12,96(%0)\n"
" ld 13,104(%0)\n"
" ld 14,112(%0)\n"
" ld 15,120(%0)\n"
:
: "a" (fpt_save_area)
: "memory");
} else {
/* Validate vector registers */
union ctlreg0 cr0;
if (!mci.vr) { if (!mci.vr) {
/* /*
* Vector registers can't be restored. If the kernel * Vector registers can't be restored. If the kernel
* currently uses vector registers the system is * currently uses vector registers the system is
* stopped. If the process has its vector registers * stopped. If the process has its vector registers
* loaded it is terminated. * loaded it is terminated. Otherwise just validate
* the registers.
*/ */
if (S390_lowcore.fpu_flags & KERNEL_VXR) if (S390_lowcore.fpu_flags & KERNEL_VXR)
s390_handle_damage(); s390_handle_damage();
if (!test_cpu_flag(CIF_FPU)) if (!test_cpu_flag(CIF_FPU))
kill_task = 1; kill_task = 1;
} }
cr0.val = S390_lowcore.cregs_save_area[0];
cr0.afp = cr0.vx = 1;
__ctl_load(cr0.val, 0, 0);
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */
:
: "Q" (*(struct vx_array *)mcesa->vector_save_area)
: "1");
__ctl_load(S390_lowcore.cregs_save_area[0], 0, 0);
} }
/* Check if access registers are valid */ /* Validate access registers */
asm volatile(
" lam 0,15,0(%0)\n"
:
: "a" (&S390_lowcore.access_regs_save_area)
: "memory");
if (!mci.ar) { if (!mci.ar) {
/* /*
* Access registers have unknown contents. * Access registers have unknown contents.
...@@ -261,7 +311,7 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -261,7 +311,7 @@ static int notrace s390_check_registers(union mci mci, int umode)
*/ */
kill_task = 1; kill_task = 1;
} }
/* Check guarded storage registers */ /* Validate guarded storage registers */
cr2.val = S390_lowcore.cregs_save_area[2]; cr2.val = S390_lowcore.cregs_save_area[2];
if (cr2.gse) { if (cr2.gse) {
if (!mci.gs) { if (!mci.gs) {
...@@ -271,31 +321,26 @@ static int notrace s390_check_registers(union mci mci, int umode) ...@@ -271,31 +321,26 @@ static int notrace s390_check_registers(union mci mci, int umode)
* It has to be terminated. * It has to be terminated.
*/ */
kill_task = 1; kill_task = 1;
} else {
load_gs_cb((struct gs_cb *)mcesa->guarded_storage_save_area);
} }
} }
/* Check if old PSW is valid */ /*
if (!mci.wp) { * The getcpu vdso syscall reads CPU number from the programmable
/* * field of the TOD clock. Disregard the TOD programmable register
* Can't tell if we come from user or kernel mode * validity bit and load the CPU number into the TOD programmable
* -> stopping machine. * field unconditionally.
*/ */
s390_handle_damage(); set_tod_programmable_field(raw_smp_processor_id());
} /* Validate clock comparator register */
/* Check for invalid kernel instruction address */ set_clock_comparator(S390_lowcore.clock_comparator);
if (!mci.ia && !umode) {
/*
* The instruction address got lost while running
* in the kernel -> stopping machine.
*/
s390_handle_damage();
}
if (!mci.ms || !mci.pm || !mci.ia) if (!mci.ms || !mci.pm || !mci.ia)
kill_task = 1; kill_task = 1;
return kill_task; return kill_task;
} }
NOKPROBE_SYMBOL(s390_check_registers); NOKPROBE_SYMBOL(s390_validate_registers);
/* /*
* Backup the guest's machine check info to its description block * Backup the guest's machine check info to its description block
...@@ -353,11 +398,6 @@ int notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -353,11 +398,6 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
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);
if (mci.sd) {
/* System damage -> stopping machine */
s390_handle_damage();
}
/* /*
* Reinject the instruction processing damages' machine checks * Reinject the instruction processing damages' machine checks
* including Delayed Access Exception into the guest * including Delayed Access Exception into the guest
...@@ -398,7 +438,7 @@ int notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -398,7 +438,7 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
s390_handle_damage(); s390_handle_damage();
} }
} }
if (s390_check_registers(mci, user_mode(regs))) { if (s390_validate_registers(mci, user_mode(regs))) {
/* /*
* Couldn't restore all register contents for the * Couldn't restore all register contents for the
* user space process -> mark task for termination. * user space process -> mark task for termination.
...@@ -428,21 +468,6 @@ int notrace s390_do_machine_check(struct pt_regs *regs) ...@@ -428,21 +468,6 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
mcck_pending = 1; mcck_pending = 1;
} }
/*
* Reinject storage related machine checks into the guest if they
* happen when the guest is running.
*/
if (!test_cpu_flag(CIF_MCCK_GUEST)) {
if (mci.se)
/* Storage error uncorrected */
s390_handle_damage();
if (mci.ke)
/* Storage key-error uncorrected */
s390_handle_damage();
if (mci.ds && mci.fa)
/* Storage degradation */
s390_handle_damage();
}
if (mci.cp) { if (mci.cp) {
/* Channel report word pending */ /* Channel report word pending */
mcck->channel_report = 1; mcck->channel_report = 1;
......
This diff is collapsed.
...@@ -29,7 +29,11 @@ DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = { ...@@ -29,7 +29,11 @@ DEFINE_PER_CPU(struct cpu_cf_events, cpu_cf_events) = {
}, },
.alert = ATOMIC64_INIT(0), .alert = ATOMIC64_INIT(0),
.state = 0, .state = 0,
.dev_state = 0,
.flags = 0, .flags = 0,
.used = 0,
.usedss = 0,
.sets = 0
}; };
/* Indicator whether the CPU-Measurement Counter Facility Support is ready */ /* Indicator whether the CPU-Measurement Counter Facility Support is ready */
static bool cpum_cf_initalized; static bool cpum_cf_initalized;
...@@ -96,25 +100,10 @@ bool kernel_cpumcf_avail(void) ...@@ -96,25 +100,10 @@ bool kernel_cpumcf_avail(void)
} }
EXPORT_SYMBOL(kernel_cpumcf_avail); EXPORT_SYMBOL(kernel_cpumcf_avail);
/* Reserve/release functions for sharing perf hardware */
static DEFINE_SPINLOCK(cpumcf_owner_lock);
static void *cpumcf_owner;
/* Initialize the CPU-measurement counter facility */ /* Initialize the CPU-measurement counter facility */
int __kernel_cpumcf_begin(void) int __kernel_cpumcf_begin(void)
{ {
int flags = PMC_INIT; int flags = PMC_INIT;
int err = 0;
spin_lock(&cpumcf_owner_lock);
if (cpumcf_owner)
err = -EBUSY;
else
cpumcf_owner = __builtin_return_address(0);
spin_unlock(&cpumcf_owner_lock);
if (err)
return err;
on_each_cpu(cpum_cf_setup_cpu, &flags, 1); on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_register(IRQ_SUBCLASS_MEASUREMENT_ALERT);
...@@ -144,10 +133,6 @@ void __kernel_cpumcf_end(void) ...@@ -144,10 +133,6 @@ void __kernel_cpumcf_end(void)
on_each_cpu(cpum_cf_setup_cpu, &flags, 1); on_each_cpu(cpum_cf_setup_cpu, &flags, 1);
irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT); irq_subclass_unregister(IRQ_SUBCLASS_MEASUREMENT_ALERT);
spin_lock(&cpumcf_owner_lock);
cpumcf_owner = NULL;
spin_unlock(&cpumcf_owner_lock);
} }
EXPORT_SYMBOL(__kernel_cpumcf_end); EXPORT_SYMBOL(__kernel_cpumcf_end);
...@@ -161,11 +146,13 @@ static int cpum_cf_setup(unsigned int cpu, int flags) ...@@ -161,11 +146,13 @@ static int cpum_cf_setup(unsigned int cpu, int flags)
static int cpum_cf_online_cpu(unsigned int cpu) static int cpum_cf_online_cpu(unsigned int cpu)
{ {
return cpum_cf_setup(cpu, PMC_INIT); cpum_cf_setup(cpu, PMC_INIT);
return cfset_online_cpu(cpu);
} }
static int cpum_cf_offline_cpu(unsigned int cpu) static int cpum_cf_offline_cpu(unsigned int cpu)
{ {
cfset_offline_cpu(cpu);
return cpum_cf_setup(cpu, PMC_RELEASE); return cpum_cf_setup(cpu, PMC_RELEASE);
} }
......
This diff is collapsed.
...@@ -166,6 +166,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -166,6 +166,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
p->thread.acrs[1] = (unsigned int)tls; p->thread.acrs[1] = (unsigned int)tls;
} }
} }
/*
* s390 stores the svc return address in arch_data when calling
* sigreturn()/restart_syscall() via vdso. 1 means no valid address
* stored.
*/
p->restart_block.arch_data = 1;
return 0; return 0;
} }
......
...@@ -354,7 +354,7 @@ void __init arch_call_rest_init(void) ...@@ -354,7 +354,7 @@ void __init arch_call_rest_init(void)
set_task_stack_end_magic(current); set_task_stack_end_magic(current);
stack += STACK_INIT_OFFSET; stack += STACK_INIT_OFFSET;
S390_lowcore.kernel_stack = stack; S390_lowcore.kernel_stack = stack;
CALL_ON_STACK_NORETURN(rest_init, stack); call_on_stack_noreturn(rest_init, stack);
} }
static void __init setup_lowcore_dat_off(void) static void __init setup_lowcore_dat_off(void)
...@@ -442,6 +442,7 @@ static void __init setup_lowcore_dat_off(void) ...@@ -442,6 +442,7 @@ static void __init setup_lowcore_dat_off(void)
lc->br_r1_trampoline = 0x07f1; /* br %r1 */ lc->br_r1_trampoline = 0x07f1; /* br %r1 */
lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW); lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW);
lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW); lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW);
lc->preempt_count = PREEMPT_DISABLED;
set_prefix((u32)(unsigned long) lc); set_prefix((u32)(unsigned long) lc);
lowcore_ptr[0] = lc; lowcore_ptr[0] = lc;
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include <asm/vdso.h>
#include "entry.h" #include "entry.h"
/* /*
...@@ -171,7 +172,6 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -171,7 +172,6 @@ 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;
} }
...@@ -334,15 +334,10 @@ static int setup_frame(int sig, struct k_sigaction *ka, ...@@ -334,15 +334,10 @@ static int setup_frame(int sig, struct k_sigaction *ka,
/* Set up to return from userspace. If provided, use a stub /* Set up to return from userspace. If provided, use a stub
already in userspace. */ already in userspace. */
if (ka->sa.sa_flags & SA_RESTORER) { if (ka->sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ka->sa.sa_restorer; restorer = (unsigned long) ka->sa.sa_restorer;
} else { else
/* Signal frame without vector registers are short ! */ restorer = VDSO64_SYMBOL(current, sigreturn);
__u16 __user *svc = (void __user *) frame + frame_size - 2;
if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc))
return -EFAULT;
restorer = (unsigned long) svc;
}
/* Set up registers for signal handler */ /* Set up registers for signal handler */
regs->gprs[14] = restorer; regs->gprs[14] = restorer;
...@@ -397,14 +392,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, ...@@ -397,14 +392,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
/* Set up to return from userspace. If provided, use a stub /* Set up to return from userspace. If provided, use a stub
already in userspace. */ already in userspace. */
if (ksig->ka.sa.sa_flags & SA_RESTORER) { if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ksig->ka.sa.sa_restorer; restorer = (unsigned long) ksig->ka.sa.sa_restorer;
} else { else
__u16 __user *svc = &frame->svc_insn; restorer = VDSO64_SYMBOL(current, rt_sigreturn);
if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc))
return -EFAULT;
restorer = (unsigned long) svc;
}
/* Create siginfo on the signal stack */ /* Create siginfo on the signal stack */
if (copy_siginfo_to_user(&frame->info, &ksig->info)) if (copy_siginfo_to_user(&frame->info, &ksig->info))
...@@ -501,7 +492,7 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) ...@@ -501,7 +492,7 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
} }
/* 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);
...@@ -517,14 +508,20 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal) ...@@ -517,14 +508,20 @@ void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
switch (regs->gprs[2]) { switch (regs->gprs[2]) {
case -ERESTART_RESTARTBLOCK: case -ERESTART_RESTARTBLOCK:
/* Restart with sys_restart_syscall */ /* Restart with sys_restart_syscall */
regs->int_code = __NR_restart_syscall; regs->gprs[2] = regs->orig_gpr2;
fallthrough; current->restart_block.arch_data = regs->psw.addr;
if (is_compat_task())
regs->psw.addr = VDSO32_SYMBOL(current, restart_syscall);
else
regs->psw.addr = VDSO64_SYMBOL(current, restart_syscall);
if (test_thread_flag(TIF_SINGLE_STEP))
clear_thread_flag(TIF_PER_TRAP);
break;
case -ERESTARTNOHAND: case -ERESTARTNOHAND:
case -ERESTARTSYS: case -ERESTARTSYS:
case -ERESTARTNOINTR: case -ERESTARTNOINTR:
/* 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_RESTART); regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
if (test_thread_flag(TIF_SINGLE_STEP)) if (test_thread_flag(TIF_SINGLE_STEP))
clear_thread_flag(TIF_PER_TRAP); clear_thread_flag(TIF_PER_TRAP);
break; break;
......
...@@ -210,6 +210,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) ...@@ -210,6 +210,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
lc->br_r1_trampoline = 0x07f1; /* br %r1 */ lc->br_r1_trampoline = 0x07f1; /* br %r1 */
lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW); lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW);
lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW); lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW);
lc->preempt_count = PREEMPT_DISABLED;
if (nmi_alloc_per_cpu(lc)) if (nmi_alloc_per_cpu(lc))
goto out; goto out;
lowcore_ptr[cpu] = lc; lowcore_ptr[cpu] = lc;
...@@ -300,24 +301,28 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data) ...@@ -300,24 +301,28 @@ static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
pcpu_sigp_retry(pcpu, SIGP_RESTART, 0); pcpu_sigp_retry(pcpu, SIGP_RESTART, 0);
} }
typedef void (pcpu_delegate_fn)(void *);
/* /*
* Call function via PSW restart on pcpu and stop the current cpu. * Call function via PSW restart on pcpu and stop the current cpu.
*/ */
static void __pcpu_delegate(void (*func)(void*), void *data) static void __pcpu_delegate(pcpu_delegate_fn *func, void *data)
{ {
func(data); /* should not return */ func(data); /* should not return */
} }
static void __no_sanitize_address pcpu_delegate(struct pcpu *pcpu, static void __no_sanitize_address pcpu_delegate(struct pcpu *pcpu,
void (*func)(void *), pcpu_delegate_fn *func,
void *data, unsigned long stack) void *data, unsigned long stack)
{ {
struct lowcore *lc = lowcore_ptr[pcpu - pcpu_devices]; struct lowcore *lc = lowcore_ptr[pcpu - pcpu_devices];
unsigned long source_cpu = stap(); unsigned long source_cpu = stap();
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT); __load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
if (pcpu->address == source_cpu) if (pcpu->address == source_cpu) {
CALL_ON_STACK(__pcpu_delegate, stack, 2, func, data); call_on_stack(2, stack, void, __pcpu_delegate,
pcpu_delegate_fn *, func, void *, data);
}
/* Stop target cpu (if func returns this stops the current cpu). */ /* Stop target cpu (if func returns this stops the current cpu). */
pcpu_sigp_retry(pcpu, SIGP_STOP, 0); pcpu_sigp_retry(pcpu, SIGP_STOP, 0);
/* Restart func on the target cpu and stop the current cpu. */ /* Restart func on the target cpu and stop the current cpu. */
...@@ -898,7 +903,7 @@ static void __no_sanitize_address smp_start_secondary(void *cpuvoid) ...@@ -898,7 +903,7 @@ static void __no_sanitize_address smp_start_secondary(void *cpuvoid)
S390_lowcore.restart_source = -1UL; S390_lowcore.restart_source = -1UL;
__ctl_load(S390_lowcore.cregs_save_area, 0, 15); __ctl_load(S390_lowcore.cregs_save_area, 0, 15);
__load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT); __load_psw_mask(PSW_KERNEL_BITS | PSW_MASK_DAT);
CALL_ON_STACK_NORETURN(smp_init_secondary, S390_lowcore.kernel_stack); call_on_stack_noreturn(smp_init_secondary, S390_lowcore.kernel_stack);
} }
/* Upping and downing of CPUs */ /* Upping and downing of CPUs */
......
...@@ -108,7 +108,7 @@ SYSCALL_DEFINE0(ni_syscall) ...@@ -108,7 +108,7 @@ SYSCALL_DEFINE0(ni_syscall)
return -ENOSYS; return -ENOSYS;
} }
void do_syscall(struct pt_regs *regs) static void do_syscall(struct pt_regs *regs)
{ {
unsigned long nr; unsigned long nr;
...@@ -121,6 +121,10 @@ void do_syscall(struct pt_regs *regs) ...@@ -121,6 +121,10 @@ void do_syscall(struct pt_regs *regs)
regs->gprs[2] = nr; regs->gprs[2] = nr;
if (nr == __NR_restart_syscall && !(current->restart_block.arch_data & 1)) {
regs->psw.addr = current->restart_block.arch_data;
current->restart_block.arch_data = 1;
}
nr = syscall_enter_from_user_mode_work(regs, nr); nr = syscall_enter_from_user_mode_work(regs, nr);
/* /*
...@@ -130,13 +134,16 @@ void do_syscall(struct pt_regs *regs) ...@@ -130,13 +134,16 @@ void do_syscall(struct pt_regs *regs)
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here * work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
* and if set, the syscall will be skipped. * and if set, the syscall will be skipped.
*/ */
if (!test_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)) {
regs->gprs[2] = -ENOSYS; if (unlikely(test_and_clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)))
if (likely(nr < NR_syscalls)) goto out;
regs->gprs[2] = current->thread.sys_call_table[nr](regs); regs->gprs[2] = -ENOSYS;
} else { if (likely(nr >= NR_syscalls))
clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET); goto out;
} do {
regs->gprs[2] = current->thread.sys_call_table[nr](regs);
} while (test_and_clear_pt_regs_flag(regs, PIF_EXECVE_PGSTE_RESTART));
out:
syscall_exit_to_user_mode_work(regs); syscall_exit_to_user_mode_work(regs);
} }
...@@ -154,13 +161,8 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap) ...@@ -154,13 +161,8 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
if (per_trap) if (per_trap)
set_thread_flag(TIF_PER_TRAP); set_thread_flag(TIF_PER_TRAP);
for (;;) { regs->flags = 0;
regs->flags = 0; set_pt_regs_flag(regs, PIF_SYSCALL);
set_pt_regs_flag(regs, PIF_SYSCALL); do_syscall(regs);
do_syscall(regs);
if (!test_pt_regs_flag(regs, PIF_SYSCALL_RESTART))
break;
local_irq_enable();
}
exit_to_user_mode(); exit_to_user_mode();
} }
...@@ -277,6 +277,8 @@ static void __init test_monitor_call(void) ...@@ -277,6 +277,8 @@ static void __init test_monitor_call(void)
{ {
int val = 1; int val = 1;
if (!IS_ENABLED(CONFIG_BUG))
return;
asm volatile( asm volatile(
" mc 0,0\n" " mc 0,0\n"
"0: xgr %0,%0\n" "0: xgr %0,%0\n"
...@@ -299,10 +301,9 @@ static void (*pgm_check_table[128])(struct pt_regs *regs); ...@@ -299,10 +301,9 @@ static void (*pgm_check_table[128])(struct pt_regs *regs);
void noinstr __do_pgm_check(struct pt_regs *regs) void noinstr __do_pgm_check(struct pt_regs *regs)
{ {
unsigned long last_break = S390_lowcore.breaking_event_addr; unsigned long last_break = S390_lowcore.breaking_event_addr;
unsigned int trapnr, syscall_redirect = 0; unsigned int trapnr;
irqentry_state_t state; irqentry_state_t state;
add_random_kstack_offset();
regs->int_code = *(u32 *)&S390_lowcore.pgm_ilc; regs->int_code = *(u32 *)&S390_lowcore.pgm_ilc;
regs->int_parm_long = S390_lowcore.trans_exc_code; regs->int_parm_long = S390_lowcore.trans_exc_code;
...@@ -344,18 +345,9 @@ void noinstr __do_pgm_check(struct pt_regs *regs) ...@@ -344,18 +345,9 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
trapnr = regs->int_code & PGM_INT_CODE_MASK; trapnr = regs->int_code & PGM_INT_CODE_MASK;
if (trapnr) if (trapnr)
pgm_check_table[trapnr](regs); pgm_check_table[trapnr](regs);
syscall_redirect = user_mode(regs) && test_pt_regs_flag(regs, PIF_SYSCALL);
out: out:
local_irq_disable(); local_irq_disable();
irqentry_exit(regs, state); 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();
}
} }
/* /*
......
...@@ -358,6 +358,15 @@ static ssize_t uv_query_facilities(struct kobject *kobj, ...@@ -358,6 +358,15 @@ static ssize_t uv_query_facilities(struct kobject *kobj,
static struct kobj_attribute uv_query_facilities_attr = static struct kobj_attribute uv_query_facilities_attr =
__ATTR(facilities, 0444, uv_query_facilities, NULL); __ATTR(facilities, 0444, uv_query_facilities, NULL);
static ssize_t uv_query_feature_indications(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%lx\n", uv_info.uv_feature_indications);
}
static struct kobj_attribute uv_query_feature_indications_attr =
__ATTR(feature_indications, 0444, uv_query_feature_indications, NULL);
static ssize_t uv_query_max_guest_cpus(struct kobject *kobj, static ssize_t uv_query_max_guest_cpus(struct kobject *kobj,
struct kobj_attribute *attr, char *page) struct kobj_attribute *attr, char *page)
{ {
...@@ -390,6 +399,7 @@ static struct kobj_attribute uv_query_max_guest_addr_attr = ...@@ -390,6 +399,7 @@ static struct kobj_attribute uv_query_max_guest_addr_attr =
static struct attribute *uv_query_attrs[] = { static struct attribute *uv_query_attrs[] = {
&uv_query_facilities_attr.attr, &uv_query_facilities_attr.attr,
&uv_query_feature_indications_attr.attr,
&uv_query_max_guest_cpus_attr.attr, &uv_query_max_guest_cpus_attr.attr,
&uv_query_max_guest_vms_attr.attr, &uv_query_max_guest_vms_attr.attr,
&uv_query_max_guest_addr_attr.attr, &uv_query_max_guest_addr_attr.attr,
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
#include <asm/vdso.h> #include <asm/vdso.h>
extern char vdso64_start[], vdso64_end[]; extern char vdso64_start[], vdso64_end[];
static unsigned int vdso_pages; extern char vdso32_start[], vdso32_end[];
static struct vm_special_mapping vvar_mapping; static struct vm_special_mapping vvar_mapping;
...@@ -37,18 +37,6 @@ enum vvar_pages { ...@@ -37,18 +37,6 @@ enum vvar_pages {
VVAR_NR_PAGES, VVAR_NR_PAGES,
}; };
unsigned int __read_mostly vdso_enabled = 1;
static int __init vdso_setup(char *str)
{
bool enabled;
if (!kstrtobool(str, &enabled))
vdso_enabled = enabled;
return 1;
}
__setup("vdso=", vdso_setup);
#ifdef CONFIG_TIME_NS #ifdef CONFIG_TIME_NS
struct vdso_data *arch_get_vdso_data(void *vvar_page) struct vdso_data *arch_get_vdso_data(void *vvar_page)
{ {
...@@ -155,7 +143,12 @@ static struct vm_special_mapping vvar_mapping = { ...@@ -155,7 +143,12 @@ static struct vm_special_mapping vvar_mapping = {
.fault = vvar_fault, .fault = vvar_fault,
}; };
static struct vm_special_mapping vdso_mapping = { static struct vm_special_mapping vdso64_mapping = {
.name = "[vdso]",
.mremap = vdso_mremap,
};
static struct vm_special_mapping vdso32_mapping = {
.name = "[vdso]", .name = "[vdso]",
.mremap = vdso_mremap, .mremap = vdso_mremap,
}; };
...@@ -171,16 +164,22 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ...@@ -171,16 +164,22 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{ {
unsigned long vdso_text_len, vdso_mapping_len; unsigned long vdso_text_len, vdso_mapping_len;
unsigned long vvar_start, vdso_text_start; unsigned long vvar_start, vdso_text_start;
struct vm_special_mapping *vdso_mapping;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma;
int rc; int rc;
BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES); BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES);
if (!vdso_enabled || is_compat_task())
return 0;
if (mmap_write_lock_killable(mm)) if (mmap_write_lock_killable(mm))
return -EINTR; return -EINTR;
vdso_text_len = vdso_pages << PAGE_SHIFT;
if (is_compat_task()) {
vdso_text_len = vdso32_end - vdso32_start;
vdso_mapping = &vdso32_mapping;
} else {
vdso_text_len = vdso64_end - vdso64_start;
vdso_mapping = &vdso64_mapping;
}
vdso_mapping_len = vdso_text_len + VVAR_NR_PAGES * PAGE_SIZE; vdso_mapping_len = vdso_text_len + VVAR_NR_PAGES * PAGE_SIZE;
vvar_start = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0); vvar_start = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
rc = vvar_start; rc = vvar_start;
...@@ -198,7 +197,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ...@@ -198,7 +197,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len, vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len,
VM_READ|VM_EXEC| VM_READ|VM_EXEC|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
&vdso_mapping); vdso_mapping);
if (IS_ERR(vma)) { if (IS_ERR(vma)) {
do_munmap(mm, vvar_start, PAGE_SIZE, NULL); do_munmap(mm, vvar_start, PAGE_SIZE, NULL);
rc = PTR_ERR(vma); rc = PTR_ERR(vma);
...@@ -211,21 +210,25 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) ...@@ -211,21 +210,25 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
return rc; return rc;
} }
static int __init vdso_init(void) static struct page ** __init vdso_setup_pages(void *start, void *end)
{ {
struct page **pages; int pages = (end - start) >> PAGE_SHIFT;
struct page **pagelist;
int i; int i;
vdso_pages = (vdso64_end - vdso64_start) >> PAGE_SHIFT; pagelist = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL);
pages = kcalloc(vdso_pages + 1, sizeof(struct page *), GFP_KERNEL); if (!pagelist)
if (!pages) { panic("%s: Cannot allocate page list for VDSO", __func__);
vdso_enabled = 0; for (i = 0; i < pages; i++)
return -ENOMEM; pagelist[i] = virt_to_page(start + i * PAGE_SIZE);
} return pagelist;
for (i = 0; i < vdso_pages; i++) }
pages[i] = virt_to_page(vdso64_start + i * PAGE_SIZE);
pages[vdso_pages] = NULL; static int __init vdso_init(void)
vdso_mapping.pages = pages; {
vdso64_mapping.pages = vdso_setup_pages(vdso64_start, vdso64_end);
if (IS_ENABLED(CONFIG_COMPAT))
vdso32_mapping.pages = vdso_setup_pages(vdso32_start, vdso32_end);
return 0; return 0;
} }
arch_initcall(vdso_init); arch_initcall(vdso_init);
# SPDX-License-Identifier: GPL-2.0-only
vdso32.lds
# SPDX-License-Identifier: GPL-2.0
# List of files in the vdso
KCOV_INSTRUMENT := n
ARCH_REL_TYPE_ABS := R_390_COPY|R_390_GLOB_DAT|R_390_JMP_SLOT|R_390_RELATIVE
ARCH_REL_TYPE_ABS += R_390_GOT|R_390_PLT
include $(srctree)/lib/vdso/Makefile
obj-vdso32 = vdso_user_wrapper-32.o note-32.o
# Build rules
targets := $(obj-vdso32) vdso32.so vdso32.so.dbg
obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
KBUILD_AFLAGS += -DBUILD_VDSO
KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
KBUILD_AFLAGS_32 := $(filter-out -m64,$(KBUILD_AFLAGS))
KBUILD_AFLAGS_32 += -m31 -s
KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_32 += -m31 -fPIC -shared -fno-common -fno-builtin
LDFLAGS_vdso32.so.dbg += -fPIC -shared -nostdlib -soname=linux-vdso32.so.1 \
--hash-style=both --build-id=sha1 -melf_s390 -T
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_32)
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_32)
obj-y += vdso32_wrapper.o
CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
# Disable gcov profiling, ubsan and kasan for VDSO code
GCOV_PROFILE := n
UBSAN_SANITIZE := n
KASAN_SANITIZE := n
# Force dependency (incbin is bad)
$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
$(obj)/vdso32.so.dbg: $(src)/vdso32.lds $(obj-vdso32) FORCE
$(call if_changed,ld)
# strip rule for the .so file
$(obj)/%.so: OBJCOPYFLAGS := -S
$(obj)/%.so: $(obj)/%.so.dbg FORCE
$(call if_changed,objcopy)
$(obj-vdso32): %-32.o: %.S FORCE
$(call if_changed_dep,vdso32as)
# actual build commands
quiet_cmd_vdso32as = VDSO32A $@
cmd_vdso32as = $(CC) $(a_flags) -c -o $@ $<
quiet_cmd_vdso32cc = VDSO32C $@
cmd_vdso32cc = $(CC) $(c_flags) -c -o $@ $<
# install commands for the unstripped file
quiet_cmd_vdso_install = INSTALL $@
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
vdso32.so: $(obj)/vdso32.so.dbg
@mkdir -p $(MODLIB)/vdso
$(call cmd,vdso_install)
vdso_install: vdso32.so
# Generate VDSO offsets using helper script
gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
quiet_cmd_vdsosym = VDSOSYM $@
cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
include/generated/vdso32-offsets.h: $(obj)/vdso32.so.dbg FORCE
$(call if_changed,vdsosym)
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Match symbols in the DSO that look like VDSO_*; produce a header file
# of constant offsets into the shared object.
#
# Doing this inside the Makefile will break the $(filter-out) function,
# causing Kbuild to rebuild the vdso-offsets header file every time.
#
# Inspired by arm64 version.
#
LC_ALL=C
sed -n 's/\([0-9a-f]*\) . __kernel_compat_\(.*\)/\#define vdso32_offset_\2\t0x\1/p'
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
* Here we can supply some information useful to userland.
*/
#include <linux/uts.h>
#include <linux/version.h>
#include <linux/elfnote.h>
ELFNOTE_START(Linux, 0, "a")
.long LINUX_VERSION_CODE
ELFNOTE_END
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This is the infamous ld script for the 64 bits vdso
* library
*/
#include <asm/page.h>
#include <asm/vdso.h>
OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
OUTPUT_ARCH(s390:31-bit)
ENTRY(_start)
SECTIONS
{
PROVIDE(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE);
#ifdef CONFIG_TIME_NS
PROVIDE(_timens_data = _vdso_data + PAGE_SIZE);
#endif
. = VDSO_LBASE + SIZEOF_HEADERS;
.hash : { *(.hash) } :text
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.note : { *(.note.*) } :text :note
. = ALIGN(16);
.text : {
*(.text .stub .text.* .gnu.linkonce.t.*)
} :text
PROVIDE(__etext = .);
PROVIDE(_etext = .);
PROVIDE(etext = .);
/*
* Other stuff is appended to the text segment:
*/
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.dynamic : { *(.dynamic) } :text :dynamic
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
.eh_frame : { KEEP (*(.eh_frame)) } :text
.gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) }
.rela.dyn ALIGN(8) : { *(.rela.dyn) }
.got ALIGN(8) : { *(.got .toc) }
_end = .;
PROVIDE(end = .);
/*
* Stabs debugging sections are here too.
*/
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
/*
* DWARF debug sections.
* Symbols in the DWARF debugging sections are relative to the
* beginning of the section so we begin them at 0.
*/
/* DWARF 1 */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2 */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2 */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3 */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : {
*(.note.GNU-stack)
*(.branch_lt)
*(.data .data.* .gnu.linkonce.d.* .sdata*)
*(.bss .sbss .dynbss .dynsbss)
}
}
/*
* Very old versions of ld do not recognize this name token; use the constant.
*/
#define PT_GNU_EH_FRAME 0x6474e550
/*
* We must supply the ELF program headers explicitly to get just one
* PT_LOAD segment, and set the flags explicitly to make segments read-only.
*/
PHDRS
{
text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
note PT_NOTE FLAGS(4); /* PF_R */
eh_frame_hdr PT_GNU_EH_FRAME;
}
/*
* This controls what symbols we export from the DSO.
*/
VERSION
{
VDSO_VERSION_STRING {
global:
/*
* Has to be there for the kernel to find
*/
__kernel_compat_restart_syscall;
__kernel_compat_rt_sigreturn;
__kernel_compat_sigreturn;
local: *;
};
}
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/page.h>
__PAGE_ALIGNED_DATA
.globl vdso32_start, vdso32_end
.balign PAGE_SIZE
vdso32_start:
.incbin "arch/s390/kernel/vdso32/vdso32.so"
.balign PAGE_SIZE
vdso32_end:
.previous
/* SPDX-License-Identifier: GPL-2.0 */
#include <asm/unistd.h>
#include <asm/dwarf.h>
.macro vdso_syscall func,syscall
.globl __kernel_compat_\func
.type __kernel_compat_\func,@function
.align 8
__kernel_compat_\func:
CFI_STARTPROC
svc \syscall
/* Make sure we notice when a syscall returns, which shouldn't happen */
.word 0
CFI_ENDPROC
.size __kernel_compat_\func,.-__kernel_compat_\func
.endm
vdso_syscall restart_syscall,__NR_restart_syscall
vdso_syscall sigreturn,__NR_sigreturn
vdso_syscall rt_sigreturn,__NR_rt_sigreturn
...@@ -74,3 +74,11 @@ vdso64.so: $(obj)/vdso64.so.dbg ...@@ -74,3 +74,11 @@ vdso64.so: $(obj)/vdso64.so.dbg
$(call cmd,vdso_install) $(call cmd,vdso_install)
vdso_install: vdso64.so vdso_install: vdso64.so
# Generate VDSO offsets using helper script
gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
quiet_cmd_vdsosym = VDSOSYM $@
cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
include/generated/vdso64-offsets.h: $(obj)/vdso64.so.dbg FORCE
$(call if_changed,vdsosym)
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Match symbols in the DSO that look like VDSO_*; produce a header file
# of constant offsets into the shared object.
#
# Doing this inside the Makefile will break the $(filter-out) function,
# causing Kbuild to rebuild the vdso-offsets header file every time.
#
# Inspired by arm64 version.
#
LC_ALL=C
sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso64_offset_\2\t0x\1/p'
...@@ -17,7 +17,7 @@ SECTIONS ...@@ -17,7 +17,7 @@ SECTIONS
#ifdef CONFIG_TIME_NS #ifdef CONFIG_TIME_NS
PROVIDE(_timens_data = _vdso_data + PAGE_SIZE); PROVIDE(_timens_data = _vdso_data + PAGE_SIZE);
#endif #endif
. = VDSO64_LBASE + SIZEOF_HEADERS; . = VDSO_LBASE + SIZEOF_HEADERS;
.hash : { *(.hash) } :text .hash : { *(.hash) } :text
.gnu.hash : { *(.gnu.hash) } .gnu.hash : { *(.gnu.hash) }
...@@ -137,6 +137,9 @@ VERSION ...@@ -137,6 +137,9 @@ VERSION
__kernel_clock_gettime; __kernel_clock_gettime;
__kernel_clock_getres; __kernel_clock_getres;
__kernel_getcpu; __kernel_getcpu;
__kernel_restart_syscall;
__kernel_rt_sigreturn;
__kernel_sigreturn;
local: *; local: *;
}; };
} }
...@@ -37,3 +37,20 @@ vdso_func gettimeofday ...@@ -37,3 +37,20 @@ vdso_func gettimeofday
vdso_func clock_getres vdso_func clock_getres
vdso_func clock_gettime vdso_func clock_gettime
vdso_func getcpu vdso_func getcpu
.macro vdso_syscall func,syscall
.globl __kernel_\func
.type __kernel_\func,@function
.align 8
__kernel_\func:
CFI_STARTPROC
svc \syscall
/* Make sure we notice when a syscall returns, which shouldn't happen */
.word 0
CFI_ENDPROC
.size __kernel_\func,.-__kernel_\func
.endm
vdso_syscall restart_syscall,__NR_restart_syscall
vdso_syscall sigreturn,__NR_sigreturn
vdso_syscall rt_sigreturn,__NR_rt_sigreturn
...@@ -162,7 +162,7 @@ char *strcat(char *dest, const char *src) ...@@ -162,7 +162,7 @@ char *strcat(char *dest, const char *src)
" jo 0b\n" " jo 0b\n"
"1: mvst %[dummy],%[src]\n" "1: mvst %[dummy],%[src]\n"
" jo 1b\n" " jo 1b\n"
: [dummy] "=&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src) : [dummy] "+&a" (dummy), [dest] "+&a" (dest), [src] "+&a" (src)
: :
: "cc", "memory", "0"); : "cc", "memory", "0");
return ret; return ret;
......
...@@ -120,7 +120,7 @@ static struct unwindme *unwindme; ...@@ -120,7 +120,7 @@ static struct unwindme *unwindme;
#define UWM_REGS 0x2 /* Pass regs to test_unwind(). */ #define UWM_REGS 0x2 /* Pass regs to test_unwind(). */
#define UWM_SP 0x4 /* Pass sp to test_unwind(). */ #define UWM_SP 0x4 /* Pass sp to test_unwind(). */
#define UWM_CALLER 0x8 /* Unwind starting from caller. */ #define UWM_CALLER 0x8 /* Unwind starting from caller. */
#define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */ #define UWM_SWITCH_STACK 0x10 /* Use call_on_stack. */
#define UWM_IRQ 0x20 /* Unwind from irq context. */ #define UWM_IRQ 0x20 /* Unwind from irq context. */
#define UWM_PGM 0x40 /* Unwind from program check handler. */ #define UWM_PGM 0x40 /* Unwind from program check handler. */
...@@ -211,7 +211,8 @@ static noinline int unwindme_func2(struct unwindme *u) ...@@ -211,7 +211,8 @@ static noinline int unwindme_func2(struct unwindme *u)
if (u->flags & UWM_SWITCH_STACK) { if (u->flags & UWM_SWITCH_STACK) {
local_irq_save(flags); local_irq_save(flags);
local_mcck_disable(); local_mcck_disable();
rc = CALL_ON_STACK(unwindme_func3, S390_lowcore.nodat_stack, 1, u); rc = call_on_stack(1, S390_lowcore.nodat_stack,
int, unwindme_func3, struct unwindme *, u);
local_mcck_enable(); local_mcck_enable();
local_irq_restore(flags); local_irq_restore(flags);
return rc; return rc;
......
...@@ -224,7 +224,7 @@ static inline unsigned long copy_in_user_mvcos(void __user *to, const void __use ...@@ -224,7 +224,7 @@ static inline unsigned long copy_in_user_mvcos(void __user *to, const void __use
EX_TABLE(0b,3b) EX_TABLE(0b,3b)
: "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2) : "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
: [spec] "d" (0x810081UL) : [spec] "d" (0x810081UL)
: "cc", "memory"); : "cc", "memory", "0");
return size; return size;
} }
......
...@@ -285,26 +285,6 @@ static noinline void do_sigbus(struct pt_regs *regs) ...@@ -285,26 +285,6 @@ static noinline void do_sigbus(struct pt_regs *regs)
(void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK)); (void __user *)(regs->int_parm_long & __FAIL_ADDR_MASK));
} }
static noinline int signal_return(struct pt_regs *regs)
{
u16 instruction;
int rc;
rc = __get_user(instruction, (u16 __user *) regs->psw.addr);
if (rc)
return rc;
if (instruction == 0x0a77) {
set_pt_regs_flag(regs, PIF_SYSCALL);
regs->int_code = 0x00040077;
return 0;
} else if (instruction == 0x0aad) {
set_pt_regs_flag(regs, PIF_SYSCALL);
regs->int_code = 0x000400ad;
return 0;
}
return -EACCES;
}
static noinline void do_fault_error(struct pt_regs *regs, int access, static noinline void do_fault_error(struct pt_regs *regs, int access,
vm_fault_t fault) vm_fault_t fault)
{ {
...@@ -312,9 +292,6 @@ static noinline void do_fault_error(struct pt_regs *regs, int access, ...@@ -312,9 +292,6 @@ static noinline void do_fault_error(struct pt_regs *regs, int access,
switch (fault) { switch (fault) {
case VM_FAULT_BADACCESS: case VM_FAULT_BADACCESS:
if (access == VM_EXEC && signal_return(regs) == 0)
break;
fallthrough;
case VM_FAULT_BADMAP: case VM_FAULT_BADMAP:
/* Bad memory access. Check if it is kernel or user space. */ /* Bad memory access. Check if it is kernel or user space. */
if (user_mode(regs)) { if (user_mode(regs)) {
...@@ -792,6 +769,32 @@ void do_secure_storage_access(struct pt_regs *regs) ...@@ -792,6 +769,32 @@ void do_secure_storage_access(struct pt_regs *regs)
struct page *page; struct page *page;
int rc; int rc;
/*
* bit 61 tells us if the address is valid, if it's not we
* have a major problem and should stop the kernel or send a
* SIGSEGV to the process. Unfortunately bit 61 is not
* reliable without the misc UV feature so we need to check
* for that as well.
*/
if (test_bit_inv(BIT_UV_FEAT_MISC, &uv_info.uv_feature_indications) &&
!test_bit_inv(61, &regs->int_parm_long)) {
/*
* When this happens, userspace did something that it
* was not supposed to do, e.g. branching into secure
* memory. Trigger a segmentation fault.
*/
if (user_mode(regs)) {
send_sig(SIGSEGV, current, 0);
return;
}
/*
* The kernel should never run into this case and we
* have no way out of this situation.
*/
panic("Unexpected PGM 0x3d with TEID bit 61=0");
}
switch (get_fault_type(regs)) { switch (get_fault_type(regs)) {
case USER_FAULT: case USER_FAULT:
mm = current->mm; mm = current->mm;
......
...@@ -125,12 +125,18 @@ static unsigned long __no_sanitize_address _memcpy_real(unsigned long dest, ...@@ -125,12 +125,18 @@ static unsigned long __no_sanitize_address _memcpy_real(unsigned long dest,
*/ */
int memcpy_real(void *dest, void *src, size_t count) int memcpy_real(void *dest, void *src, size_t count)
{ {
unsigned long _dest = (unsigned long)dest;
unsigned long _src = (unsigned long)src;
unsigned long _count = (unsigned long)count;
int rc; int rc;
if (S390_lowcore.nodat_stack != 0) { if (S390_lowcore.nodat_stack != 0) {
preempt_disable(); preempt_disable();
rc = CALL_ON_STACK(_memcpy_real, S390_lowcore.nodat_stack, 3, rc = call_on_stack(3, S390_lowcore.nodat_stack,
dest, src, count); unsigned long, _memcpy_real,
unsigned long, _dest,
unsigned long, _src,
unsigned long, _count);
preempt_enable(); preempt_enable();
return rc; return rc;
} }
...@@ -139,8 +145,7 @@ int memcpy_real(void *dest, void *src, size_t count) ...@@ -139,8 +145,7 @@ int memcpy_real(void *dest, void *src, size_t count)
* not set up yet. Just call _memcpy_real on the early boot * not set up yet. Just call _memcpy_real on the early boot
* stack * stack
*/ */
return _memcpy_real((unsigned long) dest,(unsigned long) src, return _memcpy_real(_dest, _src, _count);
(unsigned long) count);
} }
/* /*
......
...@@ -61,6 +61,9 @@ static char *aqm_str; ...@@ -61,6 +61,9 @@ static char *aqm_str;
module_param_named(aqmask, aqm_str, charp, 0440); module_param_named(aqmask, aqm_str, charp, 0440);
MODULE_PARM_DESC(aqmask, "AP bus domain mask."); MODULE_PARM_DESC(aqmask, "AP bus domain mask.");
atomic_t ap_max_msg_size = ATOMIC_INIT(AP_DEFAULT_MAX_MSG_SIZE);
EXPORT_SYMBOL(ap_max_msg_size);
static struct device *ap_root_device; static struct device *ap_root_device;
/* Hashtable of all queue devices on the AP bus */ /* Hashtable of all queue devices on the AP bus */
...@@ -316,11 +319,24 @@ EXPORT_SYMBOL(ap_test_config_ctrl_domain); ...@@ -316,11 +319,24 @@ EXPORT_SYMBOL(ap_test_config_ctrl_domain);
* Returns true if TAPQ succeeded and the info is filled or * Returns true if TAPQ succeeded and the info is filled or
* false otherwise. * false otherwise.
*/ */
static bool ap_queue_info(ap_qid_t qid, int *q_type, static bool ap_queue_info(ap_qid_t qid, int *q_type, unsigned int *q_fac,
unsigned int *q_fac, int *q_depth, bool *q_decfg) int *q_depth, int *q_ml, bool *q_decfg)
{ {
struct ap_queue_status status; struct ap_queue_status status;
unsigned long info = 0; union {
unsigned long value;
struct {
unsigned int fac : 32; /* facility bits */
unsigned int at : 8; /* ap type */
unsigned int _res1 : 8;
unsigned int _res2 : 4;
unsigned int ml : 4; /* apxl ml */
unsigned int _res3 : 4;
unsigned int qd : 4; /* queue depth */
} tapq_gr2;
} tapq_info;
tapq_info.value = 0;
/* make sure we don't run into a specifiation exception */ /* make sure we don't run into a specifiation exception */
if (AP_QID_CARD(qid) > ap_max_adapter_id || if (AP_QID_CARD(qid) > ap_max_adapter_id ||
...@@ -328,7 +344,7 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, ...@@ -328,7 +344,7 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type,
return false; return false;
/* call TAPQ on this APQN */ /* call TAPQ on this APQN */
status = ap_test_queue(qid, ap_apft_available(), &info); status = ap_test_queue(qid, ap_apft_available(), &tapq_info.value);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
case AP_RESPONSE_RESET_IN_PROGRESS: case AP_RESPONSE_RESET_IN_PROGRESS:
...@@ -340,11 +356,12 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type, ...@@ -340,11 +356,12 @@ static bool ap_queue_info(ap_qid_t qid, int *q_type,
* info should be filled. All bits 0 is not possible as * info should be filled. All bits 0 is not possible as
* there is at least one of the mode bits set. * there is at least one of the mode bits set.
*/ */
if (WARN_ON_ONCE(!info)) if (WARN_ON_ONCE(!tapq_info.value))
return false; return false;
*q_type = (int)((info >> 24) & 0xff); *q_type = tapq_info.tapq_gr2.at;
*q_fac = (unsigned int)(info >> 32); *q_fac = tapq_info.tapq_gr2.fac;
*q_depth = (int)(info & 0xff); *q_depth = tapq_info.tapq_gr2.qd;
*q_ml = tapq_info.tapq_gr2.ml;
*q_decfg = status.response_code == AP_RESPONSE_DECONFIGURED; *q_decfg = status.response_code == AP_RESPONSE_DECONFIGURED;
switch (*q_type) { switch (*q_type) {
/* For CEX2 and CEX3 the available functions /* For CEX2 and CEX3 the available functions
...@@ -1516,7 +1533,7 @@ static inline void ap_scan_domains(struct ap_card *ac) ...@@ -1516,7 +1533,7 @@ static inline void ap_scan_domains(struct ap_card *ac)
unsigned int func; unsigned int func;
struct device *dev; struct device *dev;
struct ap_queue *aq; struct ap_queue *aq;
int rc, dom, depth, type; int rc, dom, depth, type, ml;
/* /*
* Go through the configuration for the domains and compare them * Go through the configuration for the domains and compare them
...@@ -1540,7 +1557,7 @@ static inline void ap_scan_domains(struct ap_card *ac) ...@@ -1540,7 +1557,7 @@ static inline void ap_scan_domains(struct ap_card *ac)
continue; continue;
} }
/* domain is valid, get info from this APQN */ /* domain is valid, get info from this APQN */
if (!ap_queue_info(qid, &type, &func, &depth, &decfg)) { if (!ap_queue_info(qid, &type, &func, &depth, &ml, &decfg)) {
if (aq) { if (aq) {
AP_DBF_INFO( AP_DBF_INFO(
"%s(%d,%d) ap_queue_info() not successful, rm queue device\n", "%s(%d,%d) ap_queue_info() not successful, rm queue device\n",
...@@ -1639,7 +1656,7 @@ static inline void ap_scan_adapter(int ap) ...@@ -1639,7 +1656,7 @@ static inline void ap_scan_adapter(int ap)
unsigned int func; unsigned int func;
struct device *dev; struct device *dev;
struct ap_card *ac; struct ap_card *ac;
int rc, dom, depth, type, comp_type; int rc, dom, depth, type, comp_type, ml;
/* Is there currently a card device for this adapter ? */ /* Is there currently a card device for this adapter ? */
dev = bus_find_device(&ap_bus_type, NULL, dev = bus_find_device(&ap_bus_type, NULL,
...@@ -1668,7 +1685,8 @@ static inline void ap_scan_adapter(int ap) ...@@ -1668,7 +1685,8 @@ static inline void ap_scan_adapter(int ap)
for (dom = 0; dom <= ap_max_domain_id; dom++) for (dom = 0; dom <= ap_max_domain_id; dom++)
if (ap_test_config_usage_domain(dom)) { if (ap_test_config_usage_domain(dom)) {
qid = AP_MKQID(ap, dom); qid = AP_MKQID(ap, dom);
if (ap_queue_info(qid, &type, &func, &depth, &decfg)) if (ap_queue_info(qid, &type, &func,
&depth, &ml, &decfg))
break; break;
} }
if (dom > ap_max_domain_id) { if (dom > ap_max_domain_id) {
...@@ -1737,7 +1755,7 @@ static inline void ap_scan_adapter(int ap) ...@@ -1737,7 +1755,7 @@ static inline void ap_scan_adapter(int ap)
__func__, ap, type); __func__, ap, type);
return; return;
} }
ac = ap_card_create(ap, depth, type, comp_type, func); ac = ap_card_create(ap, depth, type, comp_type, func, ml);
if (!ac) { if (!ac) {
AP_DBF_WARN("%s(%d) ap_card_create() failed\n", AP_DBF_WARN("%s(%d) ap_card_create() failed\n",
__func__, ap); __func__, ap);
...@@ -1748,6 +1766,12 @@ static inline void ap_scan_adapter(int ap) ...@@ -1748,6 +1766,12 @@ static inline void ap_scan_adapter(int ap)
dev->bus = &ap_bus_type; dev->bus = &ap_bus_type;
dev->parent = ap_root_device; dev->parent = ap_root_device;
dev_set_name(dev, "card%02x", ap); dev_set_name(dev, "card%02x", ap);
/* maybe enlarge ap_max_msg_size to support this card */
if (ac->maxmsgsize > atomic_read(&ap_max_msg_size)) {
atomic_set(&ap_max_msg_size, ac->maxmsgsize);
AP_DBF_INFO("%s(%d) ap_max_msg_size update to %d byte\n",
__func__, ap, atomic_read(&ap_max_msg_size));
}
/* Register the new card device with AP bus */ /* Register the new card device with AP bus */
rc = device_register(dev); rc = device_register(dev);
if (rc) { if (rc) {
......
...@@ -25,8 +25,11 @@ ...@@ -25,8 +25,11 @@
#define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */ #define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */
#define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */
#define AP_POLL_TIME 1 /* Time in ticks between receive polls. */ #define AP_POLL_TIME 1 /* Time in ticks between receive polls. */
#define AP_DEFAULT_MAX_MSG_SIZE (12 * 1024)
#define AP_TAPQ_ML_FIELD_CHUNK_SIZE (4096)
extern int ap_domain_index; extern int ap_domain_index;
extern atomic_t ap_max_msg_size;
extern DECLARE_HASHTABLE(ap_queues, 8); extern DECLARE_HASHTABLE(ap_queues, 8);
extern spinlock_t ap_queues_lock; extern spinlock_t ap_queues_lock;
...@@ -167,6 +170,7 @@ struct ap_card { ...@@ -167,6 +170,7 @@ struct ap_card {
unsigned int functions; /* AP device function bitfield. */ unsigned int functions; /* AP device function bitfield. */
int queue_depth; /* AP queue depth.*/ int queue_depth; /* AP queue depth.*/
int id; /* AP card number. */ int id; /* AP card number. */
unsigned int maxmsgsize; /* AP msg limit for this card */
bool config; /* configured state */ bool config; /* configured state */
atomic64_t total_request_count; /* # requests ever for this AP device.*/ atomic64_t total_request_count; /* # requests ever for this AP device.*/
}; };
...@@ -228,7 +232,8 @@ struct ap_message { ...@@ -228,7 +232,8 @@ struct ap_message {
struct list_head list; /* Request queueing. */ struct list_head list; /* Request queueing. */
unsigned long long psmid; /* Message id. */ unsigned long long psmid; /* Message id. */
void *msg; /* Pointer to message buffer. */ void *msg; /* Pointer to message buffer. */
unsigned int len; /* Message length. */ unsigned int len; /* actual msg len in msg buffer */
unsigned int bufsize; /* allocated msg buffer size */
u16 flags; /* Flags, see AP_MSG_FLAG_xxx */ u16 flags; /* Flags, see AP_MSG_FLAG_xxx */
struct ap_fi fi; /* Failure Injection cmd */ struct ap_fi fi; /* Failure Injection cmd */
int rc; /* Return code for this message */ int rc; /* Return code for this message */
...@@ -290,8 +295,8 @@ void ap_queue_prepare_remove(struct ap_queue *aq); ...@@ -290,8 +295,8 @@ void ap_queue_prepare_remove(struct ap_queue *aq);
void ap_queue_remove(struct ap_queue *aq); void ap_queue_remove(struct ap_queue *aq);
void ap_queue_init_state(struct ap_queue *aq); void ap_queue_init_state(struct ap_queue *aq);
struct ap_card *ap_card_create(int id, int queue_depth, int raw_device_type, struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
int comp_device_type, unsigned int functions); int comp_type, unsigned int functions, int ml);
struct ap_perms { struct ap_perms {
unsigned long ioctlm[BITS_TO_LONGS(AP_IOCTLS)]; unsigned long ioctlm[BITS_TO_LONGS(AP_IOCTLS)];
......
...@@ -174,6 +174,16 @@ static ssize_t config_store(struct device *dev, ...@@ -174,6 +174,16 @@ static ssize_t config_store(struct device *dev,
static DEVICE_ATTR_RW(config); static DEVICE_ATTR_RW(config);
static ssize_t max_msg_size_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ap_card *ac = to_ap_card(dev);
return scnprintf(buf, PAGE_SIZE, "%u\n", ac->maxmsgsize);
}
static DEVICE_ATTR_RO(max_msg_size);
static struct attribute *ap_card_dev_attrs[] = { static struct attribute *ap_card_dev_attrs[] = {
&dev_attr_hwtype.attr, &dev_attr_hwtype.attr,
&dev_attr_raw_hwtype.attr, &dev_attr_raw_hwtype.attr,
...@@ -184,6 +194,7 @@ static struct attribute *ap_card_dev_attrs[] = { ...@@ -184,6 +194,7 @@ static struct attribute *ap_card_dev_attrs[] = {
&dev_attr_pendingq_count.attr, &dev_attr_pendingq_count.attr,
&dev_attr_modalias.attr, &dev_attr_modalias.attr,
&dev_attr_config.attr, &dev_attr_config.attr,
&dev_attr_max_msg_size.attr,
NULL NULL
}; };
...@@ -209,7 +220,7 @@ static void ap_card_device_release(struct device *dev) ...@@ -209,7 +220,7 @@ static void ap_card_device_release(struct device *dev)
} }
struct ap_card *ap_card_create(int id, int queue_depth, int raw_type, struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
int comp_type, unsigned int functions) int comp_type, unsigned int functions, int ml)
{ {
struct ap_card *ac; struct ap_card *ac;
...@@ -223,5 +234,8 @@ struct ap_card *ap_card_create(int id, int queue_depth, int raw_type, ...@@ -223,5 +234,8 @@ struct ap_card *ap_card_create(int id, int queue_depth, int raw_type,
ac->queue_depth = queue_depth; ac->queue_depth = queue_depth;
ac->functions = functions; ac->functions = functions;
ac->id = id; ac->id = id;
ac->maxmsgsize = ml > 0 ?
ml * AP_TAPQ_ML_FIELD_CHUNK_SIZE : AP_DEFAULT_MAX_MSG_SIZE;
return ac; return ac;
} }
...@@ -101,7 +101,7 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length) ...@@ -101,7 +101,7 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
if (msg == NULL) if (msg == NULL)
return -EINVAL; return -EINVAL;
status = ap_dqap(qid, psmid, msg, length); status = ap_dqap(qid, psmid, msg, length, NULL, NULL);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
return 0; return 0;
...@@ -136,9 +136,24 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) ...@@ -136,9 +136,24 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq)
struct ap_queue_status status; struct ap_queue_status status;
struct ap_message *ap_msg; struct ap_message *ap_msg;
bool found = false; bool found = false;
size_t reslen;
unsigned long resgr0 = 0;
int parts = 0;
/*
* DQAP loop until response code and resgr0 indicate that
* the msg is totally received. As we use the very same buffer
* the msg is overwritten with each invocation. That's intended
* and the receiver of the msg is informed with a msg rc code
* of EMSGSIZE in such a case.
*/
do {
status = ap_dqap(aq->qid, &aq->reply->psmid,
aq->reply->msg, aq->reply->bufsize,
&reslen, &resgr0);
parts++;
} while (status.response_code == 0xFF && resgr0 != 0);
status = ap_dqap(aq->qid, &aq->reply->psmid,
aq->reply->msg, aq->reply->len);
switch (status.response_code) { switch (status.response_code) {
case AP_RESPONSE_NORMAL: case AP_RESPONSE_NORMAL:
aq->queue_count = max_t(int, 0, aq->queue_count - 1); aq->queue_count = max_t(int, 0, aq->queue_count - 1);
...@@ -150,7 +165,12 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq) ...@@ -150,7 +165,12 @@ static struct ap_queue_status ap_sm_recv(struct ap_queue *aq)
continue; continue;
list_del_init(&ap_msg->list); list_del_init(&ap_msg->list);
aq->pendingq_count--; aq->pendingq_count--;
ap_msg->receive(aq, ap_msg, aq->reply); if (parts > 1) {
ap_msg->rc = -EMSGSIZE;
ap_msg->receive(aq, ap_msg, NULL);
} else {
ap_msg->receive(aq, ap_msg, aq->reply);
}
found = true; found = true;
break; break;
} }
......
...@@ -900,6 +900,9 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms, ...@@ -900,6 +900,9 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
if (xcRB->user_defined != AUTOSELECT && if (xcRB->user_defined != AUTOSELECT &&
xcRB->user_defined != zc->card->id) xcRB->user_defined != zc->card->id)
continue; continue;
/* check if request size exceeds card max msg size */
if (ap_msg.len > zc->card->maxmsgsize)
continue;
/* check if device node has admission for this card */ /* check if device node has admission for this card */
if (!zcrypt_check_card(perms, zc->card->id)) if (!zcrypt_check_card(perms, zc->card->id))
continue; continue;
...@@ -1068,6 +1071,9 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms, ...@@ -1068,6 +1071,9 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
if (targets && if (targets &&
!is_desired_ep11_card(zc->card->id, target_num, targets)) !is_desired_ep11_card(zc->card->id, target_num, targets))
continue; continue;
/* check if request size exceeds card max msg size */
if (ap_msg.len > zc->card->maxmsgsize)
continue;
/* check if device node has admission for this card */ /* check if device node has admission for this card */
if (!zcrypt_check_card(perms, zc->card->id)) if (!zcrypt_check_card(perms, zc->card->id))
continue; continue;
......
...@@ -28,9 +28,6 @@ ...@@ -28,9 +28,6 @@
#define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */ #define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */
#define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */ #define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */
#define CEX4A_MAX_MESSAGE_SIZE MSGTYPE50_CRB3_MAX_MSG_SIZE
#define CEX4C_MAX_MESSAGE_SIZE MSGTYPE06_MAX_MSG_SIZE
/* Waiting time for requests to be processed. /* Waiting time for requests to be processed.
* Currently there are some types of request which are not deterministic. * Currently there are some types of request which are not deterministic.
* But the maximum time limit managed by the stomper code is set to 60sec. * But the maximum time limit managed by the stomper code is set to 60sec.
...@@ -605,19 +602,19 @@ static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev) ...@@ -605,19 +602,19 @@ static int zcrypt_cex4_queue_probe(struct ap_device *ap_dev)
int rc; int rc;
if (ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL)) { if (ap_test_bit(&aq->card->functions, AP_FUNC_ACCEL)) {
zq = zcrypt_queue_alloc(CEX4A_MAX_MESSAGE_SIZE); zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq) if (!zq)
return -ENOMEM; return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE50_NAME, zq->ops = zcrypt_msgtype(MSGTYPE50_NAME,
MSGTYPE50_VARIANT_DEFAULT); MSGTYPE50_VARIANT_DEFAULT);
} else if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) { } else if (ap_test_bit(&aq->card->functions, AP_FUNC_COPRO)) {
zq = zcrypt_queue_alloc(CEX4C_MAX_MESSAGE_SIZE); zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq) if (!zq)
return -ENOMEM; return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE06_NAME, zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
MSGTYPE06_VARIANT_DEFAULT); MSGTYPE06_VARIANT_DEFAULT);
} else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11)) { } else if (ap_test_bit(&aq->card->functions, AP_FUNC_EP11)) {
zq = zcrypt_queue_alloc(CEX4C_MAX_MESSAGE_SIZE); zq = zcrypt_queue_alloc(aq->card->maxmsgsize);
if (!zq) if (!zq)
return -ENOMEM; return -ENOMEM;
zq->ops = zcrypt_msgtype(MSGTYPE06_NAME, zq->ops = zcrypt_msgtype(MSGTYPE06_NAME,
......
...@@ -442,11 +442,13 @@ static void zcrypt_cex2a_receive(struct ap_queue *aq, ...@@ -442,11 +442,13 @@ static void zcrypt_cex2a_receive(struct ap_queue *aq,
goto out; /* ap_msg->rc indicates the error */ goto out; /* ap_msg->rc indicates the error */
t80h = reply->msg; t80h = reply->msg;
if (t80h->type == TYPE80_RSP_CODE) { if (t80h->type == TYPE80_RSP_CODE) {
if (aq->ap_dev.device_type == AP_DEVICE_TYPE_CEX2A) len = t80h->len;
len = min_t(int, CEX2A_MAX_RESPONSE_SIZE, t80h->len); if (len > reply->bufsize || len > msg->bufsize) {
else msg->rc = -EMSGSIZE;
len = min_t(int, CEX3A_MAX_RESPONSE_SIZE, t80h->len); } else {
memcpy(msg->msg, reply->msg, len); memcpy(msg->msg, reply->msg, len);
msg->len = len;
}
} else } else
memcpy(msg->msg, reply->msg, sizeof(error_reply)); memcpy(msg->msg, reply->msg, sizeof(error_reply));
out: out:
...@@ -469,10 +471,9 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_queue *zq, ...@@ -469,10 +471,9 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_queue *zq,
struct completion work; struct completion work;
int rc; int rc;
if (zq->zcard->user_space_type == ZCRYPT_CEX2A) ap_msg->bufsize = (zq->zcard->user_space_type == ZCRYPT_CEX2A) ?
ap_msg->msg = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE, GFP_KERNEL); MSGTYPE50_CRB2_MAX_MSG_SIZE : MSGTYPE50_CRB3_MAX_MSG_SIZE;
else ap_msg->msg = kmalloc(ap_msg->bufsize, GFP_KERNEL);
ap_msg->msg = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE, GFP_KERNEL);
if (!ap_msg->msg) if (!ap_msg->msg)
return -ENOMEM; return -ENOMEM;
ap_msg->receive = zcrypt_cex2a_receive; ap_msg->receive = zcrypt_cex2a_receive;
...@@ -515,10 +516,9 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_queue *zq, ...@@ -515,10 +516,9 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_queue *zq,
struct completion work; struct completion work;
int rc; int rc;
if (zq->zcard->user_space_type == ZCRYPT_CEX2A) ap_msg->bufsize = (zq->zcard->user_space_type == ZCRYPT_CEX2A) ?
ap_msg->msg = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE, GFP_KERNEL); MSGTYPE50_CRB2_MAX_MSG_SIZE : MSGTYPE50_CRB3_MAX_MSG_SIZE;
else ap_msg->msg = kmalloc(ap_msg->bufsize, GFP_KERNEL);
ap_msg->msg = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE, GFP_KERNEL);
if (!ap_msg->msg) if (!ap_msg->msg)
return -ENOMEM; return -ENOMEM;
ap_msg->receive = zcrypt_cex2a_receive; ap_msg->receive = zcrypt_cex2a_receive;
......
This diff is collapsed.
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
#define MSGTYPE06_VARIANT_NORNG 1 #define MSGTYPE06_VARIANT_NORNG 1
#define MSGTYPE06_VARIANT_EP11 2 #define MSGTYPE06_VARIANT_EP11 2
#define MSGTYPE06_MAX_MSG_SIZE (12*1024)
/** /**
* The type 6 message family is associated with CEXxC/CEXxP cards. * The type 6 message family is associated with CEXxC/CEXxP cards.
* *
......
...@@ -111,17 +111,17 @@ bool zcrypt_queue_force_online(struct zcrypt_queue *zq, int online) ...@@ -111,17 +111,17 @@ bool zcrypt_queue_force_online(struct zcrypt_queue *zq, int online)
return false; return false;
} }
struct zcrypt_queue *zcrypt_queue_alloc(size_t max_response_size) struct zcrypt_queue *zcrypt_queue_alloc(size_t reply_buf_size)
{ {
struct zcrypt_queue *zq; struct zcrypt_queue *zq;
zq = kzalloc(sizeof(struct zcrypt_queue), GFP_KERNEL); zq = kzalloc(sizeof(struct zcrypt_queue), GFP_KERNEL);
if (!zq) if (!zq)
return NULL; return NULL;
zq->reply.msg = kmalloc(max_response_size, GFP_KERNEL); zq->reply.msg = kmalloc(reply_buf_size, GFP_KERNEL);
if (!zq->reply.msg) if (!zq->reply.msg)
goto out_free; goto out_free;
zq->reply.len = max_response_size; zq->reply.bufsize = reply_buf_size;
INIT_LIST_HEAD(&zq->list); INIT_LIST_HEAD(&zq->list);
kref_init(&zq->refcount); kref_init(&zq->refcount);
return zq; return zq;
......
...@@ -171,7 +171,6 @@ enum cpuhp_state { ...@@ -171,7 +171,6 @@ enum cpuhp_state {
CPUHP_AP_PERF_X86_CSTATE_ONLINE, CPUHP_AP_PERF_X86_CSTATE_ONLINE,
CPUHP_AP_PERF_X86_IDXD_ONLINE, CPUHP_AP_PERF_X86_IDXD_ONLINE,
CPUHP_AP_PERF_S390_CF_ONLINE, CPUHP_AP_PERF_S390_CF_ONLINE,
CPUHP_AP_PERF_S390_CFD_ONLINE,
CPUHP_AP_PERF_S390_SF_ONLINE, CPUHP_AP_PERF_S390_SF_ONLINE,
CPUHP_AP_PERF_ARM_CCI_ONLINE, CPUHP_AP_PERF_ARM_CCI_ONLINE,
CPUHP_AP_PERF_ARM_CCN_ONLINE, CPUHP_AP_PERF_ARM_CCN_ONLINE,
......
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