s390 updates for the 5.5 merge window #2
- Make stack unwinder reliable and suitable for livepatching. Add unwinder testing module. - Fixes for CALL_ON_STACK helper used for stack switching. - Fix unwinding from bpf code. - Fix getcpu and remove compat support in vdso code. - Fix address space control registers initialization. - Save KASLR offset for early dumps. - Handle new FILTERED_BY_HYPERVISOR reply code in crypto code. - Minor perf code cleanup and potential memory leak fix. - Add couple of error messages for corner cases during PCI device creation. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEE3QHqV+H2a8xAv27vjYWKoQLXFBgFAl3mUEMACgkQjYWKoQLX FBgHBgf/Ui3sgKGozvIAwy2kQ3oPtCdsmnTKEhLdhYT0cKMWNkA/jc13vn37ZqSk vMhawMjgjHhn4CLSjxKRGCprYViXIgnF2XrCywTDsBoj87QwB6/dME1gXJRW+/Rm OPvO+8D+210Ow0Xip3xXSRIPNFsUINCQeCCEtQCOuhGMdQPC0VIKgYtgvk1TAo1E +DycHbZ0e+uEp6zvVSsoP9wrkXw/L9krTDnjHncQ7FULJAYnBhY+qaeNTek09QAT j3Ywh5/fYR11c62W6fjb1lQHLb75L0aeK7Q5r5WspxG5LwiR2ncYWOQ4BQPZoUXq GjdNvwRmvEkB3IbnpLp/ft7sqsPn2w== =CoqQ -----END PGP SIGNATURE----- Merge tag 's390-5.5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux Pull more s390 updates from Vasily Gorbik: - Make stack unwinder reliable and suitable for livepatching. Add unwinder testing module. - Fixes for CALL_ON_STACK helper used for stack switching. - Fix unwinding from bpf code. - Fix getcpu and remove compat support in vdso code. - Fix address space control registers initialization. - Save KASLR offset for early dumps. - Handle new FILTERED_BY_HYPERVISOR reply code in crypto code. - Minor perf code cleanup and potential memory leak fix. - Add couple of error messages for corner cases during PCI device creation. * tag 's390-5.5-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (33 commits) s390: remove compat vdso code s390/livepatch: Implement reliable stack tracing for the consistency model s390/unwind: add stack pointer alignment sanity checks s390/unwind: filter out unreliable bogus %r14 s390/unwind: start unwinding from reliable state s390/test_unwind: add program check context tests s390/test_unwind: add irq context tests s390/test_unwind: print verbose unwinding results s390/test_unwind: add CALL_ON_STACK tests s390: fix register clobbering in CALL_ON_STACK s390/test_unwind: require that unwinding ended successfully s390/unwind: add a test for the internal API s390/unwind: always inline get_stack_pointer s390/pci: add error message on device number limit s390/pci: add error message for UID collision s390/cpum_sf: Check for SDBT and SDB consistency s390/cpum_sf: Use TEAR_REG macro consistantly s390/cpum_sf: Remove unnecessary check for pending SDBs s390/cpum_sf: Replace function name in debug statements s390/kaslr: store KASLR offset for early dumps ...
This commit is contained in:
commit
01d1dff646
@ -170,6 +170,7 @@ config S390
|
|||||||
select HAVE_PERF_EVENTS
|
select HAVE_PERF_EVENTS
|
||||||
select HAVE_RCU_TABLE_FREE
|
select HAVE_RCU_TABLE_FREE
|
||||||
select HAVE_REGS_AND_STACK_ACCESS_API
|
select HAVE_REGS_AND_STACK_ACCESS_API
|
||||||
|
select HAVE_RELIABLE_STACKTRACE
|
||||||
select HAVE_RSEQ
|
select HAVE_RSEQ
|
||||||
select HAVE_SYSCALL_TRACEPOINTS
|
select HAVE_SYSCALL_TRACEPOINTS
|
||||||
select HAVE_VIRT_CPU_ACCOUNTING
|
select HAVE_VIRT_CPU_ACCOUNTING
|
||||||
@ -426,9 +427,6 @@ config COMPAT
|
|||||||
(and some other stuff like libraries and such) is needed for
|
(and some other stuff like libraries and such) is needed for
|
||||||
executing 31 bit applications. It is safe to say "Y".
|
executing 31 bit applications. It is safe to say "Y".
|
||||||
|
|
||||||
config COMPAT_VDSO
|
|
||||||
def_bool COMPAT && !CC_IS_CLANG
|
|
||||||
|
|
||||||
config SYSVIPC_COMPAT
|
config SYSVIPC_COMPAT
|
||||||
def_bool y if COMPAT && SYSVIPC
|
def_bool y if COMPAT && SYSVIPC
|
||||||
|
|
||||||
@ -1018,3 +1016,17 @@ config S390_GUEST
|
|||||||
the KVM hypervisor.
|
the KVM hypervisor.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|
||||||
|
menu "Selftests"
|
||||||
|
|
||||||
|
config S390_UNWIND_SELFTEST
|
||||||
|
def_tristate n
|
||||||
|
prompt "Test unwind functions"
|
||||||
|
help
|
||||||
|
This option enables s390 specific stack unwinder testing kernel
|
||||||
|
module. This option is not useful for distributions or general
|
||||||
|
kernels, but only for kernel developers working on architecture code.
|
||||||
|
|
||||||
|
Say N if you are unsure.
|
||||||
|
|
||||||
|
endmenu
|
||||||
|
@ -157,7 +157,6 @@ zfcpdump:
|
|||||||
|
|
||||||
vdso_install:
|
vdso_install:
|
||||||
$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso64 $@
|
$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso64 $@
|
||||||
$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso32 $@
|
|
||||||
|
|
||||||
archclean:
|
archclean:
|
||||||
$(Q)$(MAKE) $(clean)=$(boot)
|
$(Q)$(MAKE) $(clean)=$(boot)
|
||||||
|
@ -170,6 +170,11 @@ void startup_kernel(void)
|
|||||||
handle_relocs(__kaslr_offset);
|
handle_relocs(__kaslr_offset);
|
||||||
|
|
||||||
if (__kaslr_offset) {
|
if (__kaslr_offset) {
|
||||||
|
/*
|
||||||
|
* Save KASLR offset for early dumps, before vmcore_info is set.
|
||||||
|
* Mark as uneven to distinguish from real vmcore_info pointer.
|
||||||
|
*/
|
||||||
|
S390_lowcore.vmcore_info = __kaslr_offset | 0x1UL;
|
||||||
/* Clear non-relocated kernel */
|
/* Clear non-relocated kernel */
|
||||||
if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED))
|
if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED))
|
||||||
memset(img, 0, vmlinux.image_size);
|
memset(img, 0, vmlinux.image_size);
|
||||||
|
@ -313,7 +313,7 @@ static inline unsigned long *trailer_entry_ptr(unsigned long v)
|
|||||||
return (unsigned long *) ret;
|
return (unsigned long *) ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return if the entry in the sample data block table (sdbt)
|
/* Return true if the entry in the sample data block table (sdbt)
|
||||||
* is a link to the next sdbt */
|
* is a link to the next sdbt */
|
||||||
static inline int is_link_entry(unsigned long *s)
|
static inline int is_link_entry(unsigned long *s)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <asm/stacktrace.h>
|
||||||
|
|
||||||
/* Per-CPU flags for PMU states */
|
/* Per-CPU flags for PMU states */
|
||||||
#define PMU_F_RESERVED 0x1000
|
#define PMU_F_RESERVED 0x1000
|
||||||
@ -73,4 +74,10 @@ struct perf_sf_sde_regs {
|
|||||||
#define SDB_FULL_BLOCKS(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_FULL_BLOCKS)
|
#define SDB_FULL_BLOCKS(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_FULL_BLOCKS)
|
||||||
#define SAMPLE_FREQ_MODE(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_FREQ_MODE)
|
#define SAMPLE_FREQ_MODE(hwc) (SAMPL_FLAGS(hwc) & PERF_CPUM_SF_FREQ_MODE)
|
||||||
|
|
||||||
|
#define perf_arch_fetch_caller_regs(regs, __ip) do { \
|
||||||
|
(regs)->psw.addr = (__ip); \
|
||||||
|
(regs)->gprs[15] = (unsigned long)__builtin_frame_address(0) - \
|
||||||
|
offsetof(struct stack_frame, back_chain); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#endif /* _ASM_S390_PERF_EVENT_H */
|
#endif /* _ASM_S390_PERF_EVENT_H */
|
||||||
|
@ -310,7 +310,7 @@ void enabled_wait(void);
|
|||||||
/*
|
/*
|
||||||
* Function to drop a processor into disabled wait state
|
* Function to drop a processor into disabled wait state
|
||||||
*/
|
*/
|
||||||
static inline void __noreturn disabled_wait(void)
|
static __always_inline void __noreturn disabled_wait(void)
|
||||||
{
|
{
|
||||||
psw_t psw;
|
psw_t psw;
|
||||||
|
|
||||||
|
@ -33,12 +33,12 @@ static inline bool on_stack(struct stack_info *info,
|
|||||||
return addr >= info->begin && addr + len <= info->end;
|
return addr >= info->begin && addr + len <= info->end;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long get_stack_pointer(struct task_struct *task,
|
static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (regs)
|
if (regs)
|
||||||
return (unsigned long) kernel_stack_pointer(regs);
|
return (unsigned long) kernel_stack_pointer(regs);
|
||||||
if (!task || task == current)
|
if (task == current)
|
||||||
return current_stack_pointer();
|
return current_stack_pointer();
|
||||||
return (unsigned long) task->thread.ksp;
|
return (unsigned long) task->thread.ksp;
|
||||||
}
|
}
|
||||||
@ -62,6 +62,17 @@ struct stack_frame {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlike current_stack_pointer() which simply returns current value of %r15
|
||||||
|
* current_frame_address() returns function stack frame address, which matches
|
||||||
|
* %r15 upon function invocation. It may differ from %r15 later if function
|
||||||
|
* allocates stack for local variables or new stack frame to call other
|
||||||
|
* functions.
|
||||||
|
*/
|
||||||
|
#define current_frame_address() \
|
||||||
|
((unsigned long)__builtin_frame_address(0) - \
|
||||||
|
offsetof(struct stack_frame, back_chain))
|
||||||
|
|
||||||
#define CALL_ARGS_0() \
|
#define CALL_ARGS_0() \
|
||||||
register unsigned long r2 asm("2")
|
register unsigned long r2 asm("2")
|
||||||
#define CALL_ARGS_1(arg1) \
|
#define CALL_ARGS_1(arg1) \
|
||||||
@ -95,20 +106,33 @@ struct stack_frame {
|
|||||||
|
|
||||||
#define CALL_ON_STACK(fn, stack, nr, args...) \
|
#define CALL_ON_STACK(fn, stack, nr, args...) \
|
||||||
({ \
|
({ \
|
||||||
|
unsigned long frame = current_frame_address(); \
|
||||||
CALL_ARGS_##nr(args); \
|
CALL_ARGS_##nr(args); \
|
||||||
unsigned long prev; \
|
unsigned long prev; \
|
||||||
\
|
\
|
||||||
asm volatile( \
|
asm volatile( \
|
||||||
" la %[_prev],0(15)\n" \
|
" la %[_prev],0(15)\n" \
|
||||||
" la 15,0(%[_stack])\n" \
|
" lg 15,%[_stack]\n" \
|
||||||
" stg %[_prev],%[_bc](15)\n" \
|
" stg %[_frame],%[_bc](15)\n" \
|
||||||
" brasl 14,%[_fn]\n" \
|
" brasl 14,%[_fn]\n" \
|
||||||
" la 15,0(%[_prev])\n" \
|
" la 15,0(%[_prev])\n" \
|
||||||
: [_prev] "=&a" (prev), CALL_FMT_##nr \
|
: [_prev] "=&a" (prev), CALL_FMT_##nr \
|
||||||
[_stack] "a" (stack), \
|
[_stack] "R" (stack), \
|
||||||
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
|
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
|
||||||
|
[_frame] "d" (frame), \
|
||||||
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \
|
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \
|
||||||
r2; \
|
r2; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
#define CALL_ON_STACK_NORETURN(fn, stack) \
|
||||||
|
({ \
|
||||||
|
asm volatile( \
|
||||||
|
" la 15,0(%[_stack])\n" \
|
||||||
|
" xc %[_bc](8,15),%[_bc](15)\n" \
|
||||||
|
" brasl 14,%[_fn]\n" \
|
||||||
|
::[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
|
||||||
|
[_stack] "a" (stack), [_fn] "X" (fn)); \
|
||||||
|
BUG(); \
|
||||||
|
})
|
||||||
|
|
||||||
#endif /* _ASM_S390_STACKTRACE_H */
|
#endif /* _ASM_S390_STACKTRACE_H */
|
||||||
|
@ -35,7 +35,6 @@ struct unwind_state {
|
|||||||
struct task_struct *task;
|
struct task_struct *task;
|
||||||
struct pt_regs *regs;
|
struct pt_regs *regs;
|
||||||
unsigned long sp, ip;
|
unsigned long sp, ip;
|
||||||
bool reuse_sp;
|
|
||||||
int graph_idx;
|
int graph_idx;
|
||||||
bool reliable;
|
bool reliable;
|
||||||
bool error;
|
bool error;
|
||||||
@ -59,10 +58,11 @@ static inline bool unwind_error(struct unwind_state *state)
|
|||||||
static inline void unwind_start(struct unwind_state *state,
|
static inline void unwind_start(struct unwind_state *state,
|
||||||
struct task_struct *task,
|
struct task_struct *task,
|
||||||
struct pt_regs *regs,
|
struct pt_regs *regs,
|
||||||
unsigned long sp)
|
unsigned long first_frame)
|
||||||
{
|
{
|
||||||
sp = sp ? : get_stack_pointer(task, regs);
|
task = task ?: current;
|
||||||
__unwind_start(state, task, regs, sp);
|
first_frame = first_frame ?: get_stack_pointer(task, regs);
|
||||||
|
__unwind_start(state, task, regs, first_frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
|
static inline struct pt_regs *unwind_get_entry_regs(struct unwind_state *state)
|
||||||
|
@ -41,8 +41,17 @@ struct vdso_data {
|
|||||||
struct vdso_per_cpu_data {
|
struct vdso_per_cpu_data {
|
||||||
__u64 ectg_timer_base;
|
__u64 ectg_timer_base;
|
||||||
__u64 ectg_user_time;
|
__u64 ectg_user_time;
|
||||||
__u32 cpu_nr;
|
/*
|
||||||
__u32 node_id;
|
* Note: node_id and cpu_nr must be at adjacent memory locations.
|
||||||
|
* VDSO userspace must read both values with a single instruction.
|
||||||
|
*/
|
||||||
|
union {
|
||||||
|
__u64 getcpu_val;
|
||||||
|
struct {
|
||||||
|
__u32 node_id;
|
||||||
|
__u32 cpu_nr;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct vdso_data *vdso_data;
|
extern struct vdso_data *vdso_data;
|
||||||
|
@ -81,4 +81,3 @@ obj-$(CONFIG_TRACEPOINTS) += trace.o
|
|||||||
|
|
||||||
# vdso
|
# vdso
|
||||||
obj-y += vdso64/
|
obj-y += vdso64/
|
||||||
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
|
|
||||||
|
@ -78,8 +78,7 @@ int main(void)
|
|||||||
OFFSET(__VDSO_TS_END, vdso_data, ts_end);
|
OFFSET(__VDSO_TS_END, vdso_data, ts_end);
|
||||||
OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base);
|
OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base);
|
||||||
OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time);
|
OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time);
|
||||||
OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr);
|
OFFSET(__VDSO_GETCPU_VAL, vdso_per_cpu_data, getcpu_val);
|
||||||
OFFSET(__VDSO_NODE_ID, vdso_per_cpu_data, node_id);
|
|
||||||
BLANK();
|
BLANK();
|
||||||
/* constants used by the vdso */
|
/* constants used by the vdso */
|
||||||
DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME);
|
DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME);
|
||||||
|
@ -38,6 +38,7 @@ const char *stack_type_name(enum stack_type type)
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(stack_type_name);
|
||||||
|
|
||||||
static inline bool in_stack(unsigned long sp, struct stack_info *info,
|
static inline bool in_stack(unsigned long sp, struct stack_info *info,
|
||||||
enum stack_type type, unsigned long low,
|
enum stack_type type, unsigned long low,
|
||||||
@ -93,7 +94,9 @@ int get_stack_info(unsigned long sp, struct task_struct *task,
|
|||||||
if (!sp)
|
if (!sp)
|
||||||
goto unknown;
|
goto unknown;
|
||||||
|
|
||||||
task = task ? : current;
|
/* Sanity check: ABI requires SP to be aligned 8 bytes. */
|
||||||
|
if (sp & 0x7)
|
||||||
|
goto unknown;
|
||||||
|
|
||||||
/* Check per-task stack */
|
/* Check per-task stack */
|
||||||
if (in_task_stack(sp, task, info))
|
if (in_task_stack(sp, task, info))
|
||||||
@ -128,8 +131,6 @@ void show_stack(struct task_struct *task, unsigned long *stack)
|
|||||||
struct unwind_state state;
|
struct unwind_state state;
|
||||||
|
|
||||||
printk("Call Trace:\n");
|
printk("Call Trace:\n");
|
||||||
if (!task)
|
|
||||||
task = current;
|
|
||||||
unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
|
unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
|
||||||
printk(state.reliable ? " [<%016lx>] %pSR \n" :
|
printk(state.reliable ? " [<%016lx>] %pSR \n" :
|
||||||
"([<%016lx>] %pSR)\n",
|
"([<%016lx>] %pSR)\n",
|
||||||
|
@ -31,7 +31,7 @@ ENTRY(startup_continue)
|
|||||||
#
|
#
|
||||||
larl %r14,init_task
|
larl %r14,init_task
|
||||||
stg %r14,__LC_CURRENT
|
stg %r14,__LC_CURRENT
|
||||||
larl %r15,init_thread_union+THREAD_SIZE-STACK_FRAME_OVERHEAD
|
larl %r15,init_thread_union+THREAD_SIZE-STACK_FRAME_OVERHEAD-__PT_SIZE
|
||||||
#ifdef CONFIG_KASAN
|
#ifdef CONFIG_KASAN
|
||||||
brasl %r14,kasan_early_init
|
brasl %r14,kasan_early_init
|
||||||
#endif
|
#endif
|
||||||
|
@ -164,7 +164,9 @@ static bool kdump_csum_valid(struct kimage *image)
|
|||||||
#ifdef CONFIG_CRASH_DUMP
|
#ifdef CONFIG_CRASH_DUMP
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
rc = CALL_ON_STACK(do_start_kdump, S390_lowcore.nodat_stack, 1, image);
|
rc = CALL_ON_STACK(do_start_kdump, S390_lowcore.nodat_stack, 1, image);
|
||||||
|
preempt_enable();
|
||||||
return rc == 0;
|
return rc == 0;
|
||||||
#else
|
#else
|
||||||
return false;
|
return false;
|
||||||
@ -254,10 +256,10 @@ void arch_crash_save_vmcoreinfo(void)
|
|||||||
VMCOREINFO_SYMBOL(lowcore_ptr);
|
VMCOREINFO_SYMBOL(lowcore_ptr);
|
||||||
VMCOREINFO_SYMBOL(high_memory);
|
VMCOREINFO_SYMBOL(high_memory);
|
||||||
VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS);
|
VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS);
|
||||||
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
|
|
||||||
vmcoreinfo_append_str("SDMA=%lx\n", __sdma);
|
vmcoreinfo_append_str("SDMA=%lx\n", __sdma);
|
||||||
vmcoreinfo_append_str("EDMA=%lx\n", __edma);
|
vmcoreinfo_append_str("EDMA=%lx\n", __edma);
|
||||||
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
|
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
|
||||||
|
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
|
||||||
}
|
}
|
||||||
|
|
||||||
void machine_shutdown(void)
|
void machine_shutdown(void)
|
||||||
|
@ -156,8 +156,8 @@ static void free_sampling_buffer(struct sf_buffer *sfb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 5, "%s freed sdbt %p\n", __func__,
|
debug_sprintf_event(sfdbg, 5, "%s: freed sdbt %#lx\n", __func__,
|
||||||
sfb->sdbt);
|
(unsigned long)sfb->sdbt);
|
||||||
memset(sfb, 0, sizeof(*sfb));
|
memset(sfb, 0, sizeof(*sfb));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
unsigned long num_sdb, gfp_t gfp_flags)
|
unsigned long num_sdb, gfp_t gfp_flags)
|
||||||
{
|
{
|
||||||
int i, rc;
|
int i, rc;
|
||||||
unsigned long *new, *tail;
|
unsigned long *new, *tail, *tail_prev = NULL;
|
||||||
|
|
||||||
if (!sfb->sdbt || !sfb->tail)
|
if (!sfb->sdbt || !sfb->tail)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -213,9 +213,10 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
*/
|
*/
|
||||||
if (sfb->sdbt != get_next_sdbt(tail)) {
|
if (sfb->sdbt != get_next_sdbt(tail)) {
|
||||||
debug_sprintf_event(sfdbg, 3, "%s: "
|
debug_sprintf_event(sfdbg, 3, "%s: "
|
||||||
"sampling buffer is not linked: origin %p"
|
"sampling buffer is not linked: origin %#lx"
|
||||||
" tail %p\n", __func__,
|
" tail %#lx\n", __func__,
|
||||||
(void *)sfb->sdbt, (void *)tail);
|
(unsigned long)sfb->sdbt,
|
||||||
|
(unsigned long)tail);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +233,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
sfb->num_sdbt++;
|
sfb->num_sdbt++;
|
||||||
/* Link current page to tail of chain */
|
/* Link current page to tail of chain */
|
||||||
*tail = (unsigned long)(void *) new + 1;
|
*tail = (unsigned long)(void *) new + 1;
|
||||||
|
tail_prev = tail;
|
||||||
tail = new;
|
tail = new;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,18 +243,30 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
* issue, a new realloc call (if required) might succeed.
|
* issue, a new realloc call (if required) might succeed.
|
||||||
*/
|
*/
|
||||||
rc = alloc_sample_data_block(tail, gfp_flags);
|
rc = alloc_sample_data_block(tail, gfp_flags);
|
||||||
if (rc)
|
if (rc) {
|
||||||
|
/* Undo last SDBT. An SDBT with no SDB at its first
|
||||||
|
* entry but with an SDBT entry instead can not be
|
||||||
|
* handled by the interrupt handler code.
|
||||||
|
* Avoid this situation.
|
||||||
|
*/
|
||||||
|
if (tail_prev) {
|
||||||
|
sfb->num_sdbt--;
|
||||||
|
free_page((unsigned long) new);
|
||||||
|
tail = tail_prev;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
sfb->num_sdb++;
|
sfb->num_sdb++;
|
||||||
tail++;
|
tail++;
|
||||||
|
tail_prev = new = NULL; /* Allocated at least one SBD */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Link sampling buffer to its origin */
|
/* Link sampling buffer to its origin */
|
||||||
*tail = (unsigned long) sfb->sdbt + 1;
|
*tail = (unsigned long) sfb->sdbt + 1;
|
||||||
sfb->tail = tail;
|
sfb->tail = tail;
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 4, "realloc_sampling_buffer: new buffer"
|
debug_sprintf_event(sfdbg, 4, "%s: new buffer"
|
||||||
" settings: sdbt %lu sdb %lu\n",
|
" settings: sdbt %lu sdb %lu\n", __func__,
|
||||||
sfb->num_sdbt, sfb->num_sdb);
|
sfb->num_sdbt, sfb->num_sdb);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@ -292,12 +306,13 @@ static int alloc_sampling_buffer(struct sf_buffer *sfb, unsigned long num_sdb)
|
|||||||
rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL);
|
rc = realloc_sampling_buffer(sfb, num_sdb, GFP_KERNEL);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
free_sampling_buffer(sfb);
|
free_sampling_buffer(sfb);
|
||||||
debug_sprintf_event(sfdbg, 4, "alloc_sampling_buffer: "
|
debug_sprintf_event(sfdbg, 4, "%s: "
|
||||||
"realloc_sampling_buffer failed with rc %i\n", rc);
|
"realloc_sampling_buffer failed with rc %i\n",
|
||||||
|
__func__, rc);
|
||||||
} else
|
} else
|
||||||
debug_sprintf_event(sfdbg, 4,
|
debug_sprintf_event(sfdbg, 4,
|
||||||
"alloc_sampling_buffer: tear %p dear %p\n",
|
"%s: tear %#lx dear %#lx\n", __func__,
|
||||||
sfb->sdbt, (void *)*sfb->sdbt);
|
(unsigned long)sfb->sdbt, (unsigned long)*sfb->sdbt);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,8 +480,8 @@ static void sfb_account_overflows(struct cpu_hw_sf *cpuhw,
|
|||||||
if (num)
|
if (num)
|
||||||
sfb_account_allocs(num, hwc);
|
sfb_account_allocs(num, hwc);
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 5, "sfb: overflow: overflow %llu ratio %lu"
|
debug_sprintf_event(sfdbg, 5, "%s: overflow %llu ratio %lu num %lu\n",
|
||||||
" num %lu\n", OVERFLOW_REG(hwc), ratio, num);
|
__func__, OVERFLOW_REG(hwc), ratio, num);
|
||||||
OVERFLOW_REG(hwc) = 0;
|
OVERFLOW_REG(hwc) = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,13 +519,13 @@ static void extend_sampling_buffer(struct sf_buffer *sfb,
|
|||||||
*/
|
*/
|
||||||
rc = realloc_sampling_buffer(sfb, num, GFP_ATOMIC);
|
rc = realloc_sampling_buffer(sfb, num, GFP_ATOMIC);
|
||||||
if (rc)
|
if (rc)
|
||||||
debug_sprintf_event(sfdbg, 5, "sfb: extend: realloc "
|
debug_sprintf_event(sfdbg, 5, "%s: realloc failed with rc %i\n",
|
||||||
"failed with rc %i\n", rc);
|
__func__, rc);
|
||||||
|
|
||||||
if (sfb_has_pending_allocs(sfb, hwc))
|
if (sfb_has_pending_allocs(sfb, hwc))
|
||||||
debug_sprintf_event(sfdbg, 5, "sfb: extend: "
|
debug_sprintf_event(sfdbg, 5, "%s: "
|
||||||
"req %lu alloc %lu remaining %lu\n",
|
"req %lu alloc %lu remaining %lu\n",
|
||||||
num, sfb->num_sdb - num_old,
|
__func__, num, sfb->num_sdb - num_old,
|
||||||
sfb_pending_allocs(sfb, hwc));
|
sfb_pending_allocs(sfb, hwc));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,13 +615,6 @@ static void hw_init_period(struct hw_perf_event *hwc, u64 period)
|
|||||||
local64_set(&hwc->period_left, hwc->sample_period);
|
local64_set(&hwc->period_left, hwc->sample_period);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hw_reset_registers(struct hw_perf_event *hwc,
|
|
||||||
unsigned long *sdbt_origin)
|
|
||||||
{
|
|
||||||
/* (Re)set to first sample-data-block-table */
|
|
||||||
TEAR_REG(hwc) = (unsigned long) sdbt_origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned long hw_limit_rate(const struct hws_qsi_info_block *si,
|
static unsigned long hw_limit_rate(const struct hws_qsi_info_block *si,
|
||||||
unsigned long rate)
|
unsigned long rate)
|
||||||
{
|
{
|
||||||
@ -698,9 +706,9 @@ static unsigned long getrate(bool freq, unsigned long sample,
|
|||||||
*/
|
*/
|
||||||
if (sample_rate_to_freq(si, rate) >
|
if (sample_rate_to_freq(si, rate) >
|
||||||
sysctl_perf_event_sample_rate) {
|
sysctl_perf_event_sample_rate) {
|
||||||
debug_sprintf_event(sfdbg, 1,
|
debug_sprintf_event(sfdbg, 1, "%s: "
|
||||||
"Sampling rate exceeds maximum "
|
"Sampling rate exceeds maximum "
|
||||||
"perf sample rate\n");
|
"perf sample rate\n", __func__);
|
||||||
rate = 0;
|
rate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -745,10 +753,9 @@ static int __hw_perf_event_init_rate(struct perf_event *event,
|
|||||||
attr->sample_period = rate;
|
attr->sample_period = rate;
|
||||||
SAMPL_RATE(hwc) = rate;
|
SAMPL_RATE(hwc) = rate;
|
||||||
hw_init_period(hwc, SAMPL_RATE(hwc));
|
hw_init_period(hwc, SAMPL_RATE(hwc));
|
||||||
debug_sprintf_event(sfdbg, 4, "__hw_perf_event_init_rate:"
|
debug_sprintf_event(sfdbg, 4, "%s: cpu %d period %#llx freq %d,%#lx\n",
|
||||||
"cpu:%d period:%#llx freq:%d,%#lx\n", event->cpu,
|
__func__, event->cpu, event->attr.sample_period,
|
||||||
event->attr.sample_period, event->attr.freq,
|
event->attr.freq, SAMPLE_FREQ_MODE(hwc));
|
||||||
SAMPLE_FREQ_MODE(hwc));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -951,8 +958,7 @@ static void cpumsf_pmu_enable(struct pmu *pmu)
|
|||||||
* buffer extents
|
* buffer extents
|
||||||
*/
|
*/
|
||||||
sfb_account_overflows(cpuhw, hwc);
|
sfb_account_overflows(cpuhw, hwc);
|
||||||
if (sfb_has_pending_allocs(&cpuhw->sfb, hwc))
|
extend_sampling_buffer(&cpuhw->sfb, hwc);
|
||||||
extend_sampling_buffer(&cpuhw->sfb, hwc);
|
|
||||||
}
|
}
|
||||||
/* Rate may be adjusted with ioctl() */
|
/* Rate may be adjusted with ioctl() */
|
||||||
cpuhw->lsctl.interval = SAMPL_RATE(&cpuhw->event->hw);
|
cpuhw->lsctl.interval = SAMPL_RATE(&cpuhw->event->hw);
|
||||||
@ -973,12 +979,11 @@ static void cpumsf_pmu_enable(struct pmu *pmu)
|
|||||||
/* Load current program parameter */
|
/* Load current program parameter */
|
||||||
lpp(&S390_lowcore.lpp);
|
lpp(&S390_lowcore.lpp);
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 6, "pmu_enable: es %i cs %i ed %i cd %i "
|
debug_sprintf_event(sfdbg, 6, "%s: es %i cs %i ed %i cd %i "
|
||||||
"interval %#lx tear %p dear %p\n",
|
"interval %#lx tear %#lx dear %#lx\n", __func__,
|
||||||
cpuhw->lsctl.es, cpuhw->lsctl.cs, cpuhw->lsctl.ed,
|
cpuhw->lsctl.es, cpuhw->lsctl.cs, cpuhw->lsctl.ed,
|
||||||
cpuhw->lsctl.cd, cpuhw->lsctl.interval,
|
cpuhw->lsctl.cd, cpuhw->lsctl.interval,
|
||||||
(void *) cpuhw->lsctl.tear,
|
cpuhw->lsctl.tear, cpuhw->lsctl.dear);
|
||||||
(void *) cpuhw->lsctl.dear);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cpumsf_pmu_disable(struct pmu *pmu)
|
static void cpumsf_pmu_disable(struct pmu *pmu)
|
||||||
@ -1019,8 +1024,8 @@ static void cpumsf_pmu_disable(struct pmu *pmu)
|
|||||||
cpuhw->lsctl.dear = si.dear;
|
cpuhw->lsctl.dear = si.dear;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
debug_sprintf_event(sfdbg, 3, "cpumsf_pmu_disable: "
|
debug_sprintf_event(sfdbg, 3, "%s: qsi() failed with err %i\n",
|
||||||
"qsi() failed with err %i\n", err);
|
__func__, err);
|
||||||
|
|
||||||
cpuhw->flags &= ~PMU_F_ENABLED;
|
cpuhw->flags &= ~PMU_F_ENABLED;
|
||||||
}
|
}
|
||||||
@ -1265,9 +1270,9 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
|
|||||||
sampl_overflow += te->overflow;
|
sampl_overflow += te->overflow;
|
||||||
|
|
||||||
/* Timestamps are valid for full sample-data-blocks only */
|
/* Timestamps are valid for full sample-data-blocks only */
|
||||||
debug_sprintf_event(sfdbg, 6, "%s: sdbt %p "
|
debug_sprintf_event(sfdbg, 6, "%s: sdbt %#lx "
|
||||||
"overflow %llu timestamp %#llx\n",
|
"overflow %llu timestamp %#llx\n",
|
||||||
__func__, sdbt, te->overflow,
|
__func__, (unsigned long)sdbt, te->overflow,
|
||||||
(te->f) ? trailer_timestamp(te) : 0ULL);
|
(te->f) ? trailer_timestamp(te) : 0ULL);
|
||||||
|
|
||||||
/* Collect all samples from a single sample-data-block and
|
/* Collect all samples from a single sample-data-block and
|
||||||
@ -1312,8 +1317,10 @@ static void hw_perf_event_update(struct perf_event *event, int flush_all)
|
|||||||
sampl_overflow, 1 + num_sdb);
|
sampl_overflow, 1 + num_sdb);
|
||||||
if (sampl_overflow || event_overflow)
|
if (sampl_overflow || event_overflow)
|
||||||
debug_sprintf_event(sfdbg, 4, "%s: "
|
debug_sprintf_event(sfdbg, 4, "%s: "
|
||||||
"overflow stats: sample %llu event %llu\n",
|
"overflows: sample %llu event %llu"
|
||||||
__func__, sampl_overflow, event_overflow);
|
" total %llu num_sdb %llu\n",
|
||||||
|
__func__, sampl_overflow, event_overflow,
|
||||||
|
OVERFLOW_REG(hwc), num_sdb);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define AUX_SDB_INDEX(aux, i) ((i) % aux->sfb.num_sdb)
|
#define AUX_SDB_INDEX(aux, i) ((i) % aux->sfb.num_sdb)
|
||||||
@ -1424,10 +1431,10 @@ static int aux_output_begin(struct perf_output_handle *handle,
|
|||||||
cpuhw->lsctl.tear = base + offset * sizeof(unsigned long);
|
cpuhw->lsctl.tear = base + offset * sizeof(unsigned long);
|
||||||
cpuhw->lsctl.dear = aux->sdb_index[head];
|
cpuhw->lsctl.dear = aux->sdb_index[head];
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 6, "aux_output_begin: "
|
debug_sprintf_event(sfdbg, 6, "%s: "
|
||||||
"head->alert_mark->empty_mark (num_alert, range)"
|
"head->alert_mark->empty_mark (num_alert, range)"
|
||||||
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx) "
|
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx) "
|
||||||
"tear index %#lx, tear %#lx dear %#lx\n",
|
"tear index %#lx, tear %#lx dear %#lx\n", __func__,
|
||||||
aux->head, aux->alert_mark, aux->empty_mark,
|
aux->head, aux->alert_mark, aux->empty_mark,
|
||||||
AUX_SDB_NUM_ALERT(aux), range,
|
AUX_SDB_NUM_ALERT(aux), range,
|
||||||
head / CPUM_SF_SDB_PER_TABLE,
|
head / CPUM_SF_SDB_PER_TABLE,
|
||||||
@ -1571,7 +1578,9 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||||||
pr_err("The AUX buffer with %lu pages for the "
|
pr_err("The AUX buffer with %lu pages for the "
|
||||||
"diagnostic-sampling mode is full\n",
|
"diagnostic-sampling mode is full\n",
|
||||||
num_sdb);
|
num_sdb);
|
||||||
debug_sprintf_event(sfdbg, 1, "AUX buffer used up\n");
|
debug_sprintf_event(sfdbg, 1,
|
||||||
|
"%s: AUX buffer used up\n",
|
||||||
|
__func__);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (WARN_ON_ONCE(!aux))
|
if (WARN_ON_ONCE(!aux))
|
||||||
@ -1594,23 +1603,25 @@ static void hw_collect_aux(struct cpu_hw_sf *cpuhw)
|
|||||||
perf_aux_output_end(&cpuhw->handle, size);
|
perf_aux_output_end(&cpuhw->handle, size);
|
||||||
pr_err("Sample data caused the AUX buffer with %lu "
|
pr_err("Sample data caused the AUX buffer with %lu "
|
||||||
"pages to overflow\n", num_sdb);
|
"pages to overflow\n", num_sdb);
|
||||||
debug_sprintf_event(sfdbg, 1, "head %#lx range %#lx "
|
debug_sprintf_event(sfdbg, 1, "%s: head %#lx range %#lx "
|
||||||
"overflow %#llx\n",
|
"overflow %#llx\n", __func__,
|
||||||
aux->head, range, overflow);
|
aux->head, range, overflow);
|
||||||
} else {
|
} else {
|
||||||
size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT;
|
size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT;
|
||||||
perf_aux_output_end(&cpuhw->handle, size);
|
perf_aux_output_end(&cpuhw->handle, size);
|
||||||
debug_sprintf_event(sfdbg, 6, "head %#lx alert %#lx "
|
debug_sprintf_event(sfdbg, 6, "%s: head %#lx alert %#lx "
|
||||||
"already full, try another\n",
|
"already full, try another\n",
|
||||||
|
__func__,
|
||||||
aux->head, aux->alert_mark);
|
aux->head, aux->alert_mark);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (done)
|
if (done)
|
||||||
debug_sprintf_event(sfdbg, 6, "aux_reset_buffer: "
|
debug_sprintf_event(sfdbg, 6, "%s: aux_reset_buffer "
|
||||||
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx)\n",
|
"[%#lx -> %#lx -> %#lx] (%#lx, %#lx)\n",
|
||||||
aux->head, aux->alert_mark, aux->empty_mark,
|
__func__, aux->head, aux->alert_mark,
|
||||||
AUX_SDB_NUM_ALERT(aux), range);
|
aux->empty_mark, AUX_SDB_NUM_ALERT(aux),
|
||||||
|
range);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1633,8 +1644,8 @@ static void aux_buffer_free(void *data)
|
|||||||
kfree(aux->sdb_index);
|
kfree(aux->sdb_index);
|
||||||
kfree(aux);
|
kfree(aux);
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 4, "aux_buffer_free: free "
|
debug_sprintf_event(sfdbg, 4, "%s: free "
|
||||||
"%lu SDBTs\n", num_sdbt);
|
"%lu SDBTs\n", __func__, num_sdbt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aux_sdb_init(unsigned long sdb)
|
static void aux_sdb_init(unsigned long sdb)
|
||||||
@ -1742,9 +1753,8 @@ static void *aux_buffer_setup(struct perf_event *event, void **pages,
|
|||||||
*/
|
*/
|
||||||
aux->empty_mark = sfb->num_sdb - 1;
|
aux->empty_mark = sfb->num_sdb - 1;
|
||||||
|
|
||||||
debug_sprintf_event(sfdbg, 4, "aux_buffer_setup: setup %lu SDBTs"
|
debug_sprintf_event(sfdbg, 4, "%s: setup %lu SDBTs and %lu SDBs\n",
|
||||||
" and %lu SDBs\n",
|
__func__, sfb->num_sdbt, sfb->num_sdb);
|
||||||
sfb->num_sdbt, sfb->num_sdb);
|
|
||||||
|
|
||||||
return aux;
|
return aux;
|
||||||
|
|
||||||
@ -1797,9 +1807,9 @@ static int cpumsf_pmu_check_period(struct perf_event *event, u64 value)
|
|||||||
event->attr.sample_period = rate;
|
event->attr.sample_period = rate;
|
||||||
SAMPL_RATE(&event->hw) = rate;
|
SAMPL_RATE(&event->hw) = rate;
|
||||||
hw_init_period(&event->hw, SAMPL_RATE(&event->hw));
|
hw_init_period(&event->hw, SAMPL_RATE(&event->hw));
|
||||||
debug_sprintf_event(sfdbg, 4, "cpumsf_pmu_check_period:"
|
debug_sprintf_event(sfdbg, 4, "%s:"
|
||||||
"cpu:%d value:%#llx period:%#llx freq:%d\n",
|
" cpu %d value %#llx period %#llx freq %d\n",
|
||||||
event->cpu, value,
|
__func__, event->cpu, value,
|
||||||
event->attr.sample_period, do_freq);
|
event->attr.sample_period, do_freq);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1875,7 +1885,7 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags)
|
|||||||
if (!SAMPL_DIAG_MODE(&event->hw)) {
|
if (!SAMPL_DIAG_MODE(&event->hw)) {
|
||||||
cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt;
|
cpuhw->lsctl.tear = (unsigned long) cpuhw->sfb.sdbt;
|
||||||
cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt;
|
cpuhw->lsctl.dear = *(unsigned long *) cpuhw->sfb.sdbt;
|
||||||
hw_reset_registers(&event->hw, cpuhw->sfb.sdbt);
|
TEAR_REG(&event->hw) = (unsigned long) cpuhw->sfb.sdbt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure sampling functions are in the disabled state. If disabled,
|
/* Ensure sampling functions are in the disabled state. If disabled,
|
||||||
@ -2030,7 +2040,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
|
|||||||
|
|
||||||
/* Report measurement alerts only for non-PRA codes */
|
/* Report measurement alerts only for non-PRA codes */
|
||||||
if (alert != CPU_MF_INT_SF_PRA)
|
if (alert != CPU_MF_INT_SF_PRA)
|
||||||
debug_sprintf_event(sfdbg, 6, "measurement alert: %#x\n",
|
debug_sprintf_event(sfdbg, 6, "%s: alert %#x\n", __func__,
|
||||||
alert);
|
alert);
|
||||||
|
|
||||||
/* Sampling authorization change request */
|
/* Sampling authorization change request */
|
||||||
|
@ -355,7 +355,6 @@ early_initcall(async_stack_realloc);
|
|||||||
|
|
||||||
void __init arch_call_rest_init(void)
|
void __init arch_call_rest_init(void)
|
||||||
{
|
{
|
||||||
struct stack_frame *frame;
|
|
||||||
unsigned long stack;
|
unsigned long stack;
|
||||||
|
|
||||||
stack = stack_alloc();
|
stack = stack_alloc();
|
||||||
@ -368,13 +367,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;
|
||||||
frame = (struct stack_frame *) stack;
|
CALL_ON_STACK_NORETURN(rest_init, stack);
|
||||||
memset(frame, 0, sizeof(*frame));
|
|
||||||
/* Branch to rest_init on the new stack, never returns */
|
|
||||||
asm volatile(
|
|
||||||
" la 15,0(%[_frame])\n"
|
|
||||||
" jg rest_init\n"
|
|
||||||
: : [_frame] "a" (frame));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init setup_lowcore_dat_off(void)
|
static void __init setup_lowcore_dat_off(void)
|
||||||
|
@ -262,10 +262,13 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
|
|||||||
lc->spinlock_index = 0;
|
lc->spinlock_index = 0;
|
||||||
lc->percpu_offset = __per_cpu_offset[cpu];
|
lc->percpu_offset = __per_cpu_offset[cpu];
|
||||||
lc->kernel_asce = S390_lowcore.kernel_asce;
|
lc->kernel_asce = S390_lowcore.kernel_asce;
|
||||||
|
lc->user_asce = S390_lowcore.kernel_asce;
|
||||||
lc->machine_flags = S390_lowcore.machine_flags;
|
lc->machine_flags = S390_lowcore.machine_flags;
|
||||||
lc->user_timer = lc->system_timer =
|
lc->user_timer = lc->system_timer =
|
||||||
lc->steal_timer = lc->avg_steal_timer = 0;
|
lc->steal_timer = lc->avg_steal_timer = 0;
|
||||||
__ctl_store(lc->cregs_save_area, 0, 15);
|
__ctl_store(lc->cregs_save_area, 0, 15);
|
||||||
|
lc->cregs_save_area[1] = lc->kernel_asce;
|
||||||
|
lc->cregs_save_area[7] = lc->vdso_asce;
|
||||||
save_access_regs((unsigned int *) lc->access_regs_save_area);
|
save_access_regs((unsigned int *) lc->access_regs_save_area);
|
||||||
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
|
memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
|
||||||
sizeof(lc->stfle_fac_list));
|
sizeof(lc->stfle_fac_list));
|
||||||
@ -844,6 +847,8 @@ static void smp_init_secondary(void)
|
|||||||
|
|
||||||
S390_lowcore.last_update_clock = get_tod_clock();
|
S390_lowcore.last_update_clock = get_tod_clock();
|
||||||
restore_access_regs(S390_lowcore.access_regs_save_area);
|
restore_access_regs(S390_lowcore.access_regs_save_area);
|
||||||
|
set_cpu_flag(CIF_ASCE_PRIMARY);
|
||||||
|
set_cpu_flag(CIF_ASCE_SECONDARY);
|
||||||
cpu_init();
|
cpu_init();
|
||||||
preempt_disable();
|
preempt_disable();
|
||||||
init_cpu_timer();
|
init_cpu_timer();
|
||||||
@ -871,7 +876,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(smp_init_secondary, S390_lowcore.kernel_stack, 0);
|
CALL_ON_STACK_NORETURN(smp_init_secondary, S390_lowcore.kernel_stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Upping and downing of CPUs */
|
/* Upping and downing of CPUs */
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <linux/stacktrace.h>
|
#include <linux/stacktrace.h>
|
||||||
#include <asm/stacktrace.h>
|
#include <asm/stacktrace.h>
|
||||||
#include <asm/unwind.h>
|
#include <asm/unwind.h>
|
||||||
|
#include <asm/kprobes.h>
|
||||||
|
|
||||||
void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
|
void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
|
||||||
struct task_struct *task, struct pt_regs *regs)
|
struct task_struct *task, struct pt_regs *regs)
|
||||||
@ -22,3 +23,45 @@ void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function returns an error if it detects any unreliable features of the
|
||||||
|
* stack. Otherwise it guarantees that the stack trace is reliable.
|
||||||
|
*
|
||||||
|
* If the task is not 'current', the caller *must* ensure the task is inactive.
|
||||||
|
*/
|
||||||
|
int arch_stack_walk_reliable(stack_trace_consume_fn consume_entry,
|
||||||
|
void *cookie, struct task_struct *task)
|
||||||
|
{
|
||||||
|
struct unwind_state state;
|
||||||
|
unsigned long addr;
|
||||||
|
|
||||||
|
unwind_for_each_frame(&state, task, NULL, 0) {
|
||||||
|
if (state.stack_info.type != STACK_TYPE_TASK)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (state.regs)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
addr = unwind_get_return_address(&state);
|
||||||
|
if (!addr)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
/*
|
||||||
|
* Mark stacktraces with kretprobed functions on them
|
||||||
|
* as unreliable.
|
||||||
|
*/
|
||||||
|
if (state.ip == (unsigned long)kretprobe_trampoline)
|
||||||
|
return -EINVAL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!consume_entry(cookie, addr, false))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for stack corruption */
|
||||||
|
if (unwind_error(&state))
|
||||||
|
return -EINVAL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@ -36,6 +36,12 @@ static bool update_stack_info(struct unwind_state *state, unsigned long sp)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_task_pt_regs(struct unwind_state *state,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
return task_pt_regs(state->task) == regs;
|
||||||
|
}
|
||||||
|
|
||||||
bool unwind_next_frame(struct unwind_state *state)
|
bool unwind_next_frame(struct unwind_state *state)
|
||||||
{
|
{
|
||||||
struct stack_info *info = &state->stack_info;
|
struct stack_info *info = &state->stack_info;
|
||||||
@ -46,20 +52,16 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||||||
|
|
||||||
regs = state->regs;
|
regs = state->regs;
|
||||||
if (unlikely(regs)) {
|
if (unlikely(regs)) {
|
||||||
if (state->reuse_sp) {
|
sp = state->sp;
|
||||||
sp = state->sp;
|
|
||||||
state->reuse_sp = false;
|
|
||||||
} else {
|
|
||||||
sp = READ_ONCE_NOCHECK(regs->gprs[15]);
|
|
||||||
if (unlikely(outside_of_stack(state, sp))) {
|
|
||||||
if (!update_stack_info(state, sp))
|
|
||||||
goto out_err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sf = (struct stack_frame *) sp;
|
sf = (struct stack_frame *) sp;
|
||||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||||
reliable = false;
|
reliable = false;
|
||||||
regs = NULL;
|
regs = NULL;
|
||||||
|
if (!__kernel_text_address(ip)) {
|
||||||
|
/* skip bogus %r14 */
|
||||||
|
state->regs = NULL;
|
||||||
|
return unwind_next_frame(state);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sf = (struct stack_frame *) state->sp;
|
sf = (struct stack_frame *) state->sp;
|
||||||
sp = READ_ONCE_NOCHECK(sf->back_chain);
|
sp = READ_ONCE_NOCHECK(sf->back_chain);
|
||||||
@ -76,15 +78,24 @@ bool unwind_next_frame(struct unwind_state *state)
|
|||||||
/* No back-chain, look for a pt_regs structure */
|
/* No back-chain, look for a pt_regs structure */
|
||||||
sp = state->sp + STACK_FRAME_OVERHEAD;
|
sp = state->sp + STACK_FRAME_OVERHEAD;
|
||||||
if (!on_stack(info, sp, sizeof(struct pt_regs)))
|
if (!on_stack(info, sp, sizeof(struct pt_regs)))
|
||||||
goto out_stop;
|
goto out_err;
|
||||||
regs = (struct pt_regs *) sp;
|
regs = (struct pt_regs *) sp;
|
||||||
if (READ_ONCE_NOCHECK(regs->psw.mask) & PSW_MASK_PSTATE)
|
if (is_task_pt_regs(state, regs))
|
||||||
goto out_stop;
|
goto out_stop;
|
||||||
ip = READ_ONCE_NOCHECK(regs->psw.addr);
|
ip = READ_ONCE_NOCHECK(regs->psw.addr);
|
||||||
|
sp = READ_ONCE_NOCHECK(regs->gprs[15]);
|
||||||
|
if (unlikely(outside_of_stack(state, sp))) {
|
||||||
|
if (!update_stack_info(state, sp))
|
||||||
|
goto out_err;
|
||||||
|
}
|
||||||
reliable = true;
|
reliable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sanity check: ABI requires SP to be aligned 8 bytes. */
|
||||||
|
if (sp & 0x7)
|
||||||
|
goto out_err;
|
||||||
|
|
||||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp);
|
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp);
|
||||||
|
|
||||||
/* Update unwind state */
|
/* Update unwind state */
|
||||||
@ -103,13 +114,11 @@ out_stop:
|
|||||||
EXPORT_SYMBOL_GPL(unwind_next_frame);
|
EXPORT_SYMBOL_GPL(unwind_next_frame);
|
||||||
|
|
||||||
void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||||
struct pt_regs *regs, unsigned long sp)
|
struct pt_regs *regs, unsigned long first_frame)
|
||||||
{
|
{
|
||||||
struct stack_info *info = &state->stack_info;
|
struct stack_info *info = &state->stack_info;
|
||||||
unsigned long *mask = &state->stack_mask;
|
|
||||||
bool reliable, reuse_sp;
|
|
||||||
struct stack_frame *sf;
|
struct stack_frame *sf;
|
||||||
unsigned long ip;
|
unsigned long ip, sp;
|
||||||
|
|
||||||
memset(state, 0, sizeof(*state));
|
memset(state, 0, sizeof(*state));
|
||||||
state->task = task;
|
state->task = task;
|
||||||
@ -121,25 +130,28 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the instruction pointer from pt_regs or the stack frame */
|
||||||
|
if (regs) {
|
||||||
|
ip = regs->psw.addr;
|
||||||
|
sp = regs->gprs[15];
|
||||||
|
} else if (task == current) {
|
||||||
|
sp = current_frame_address();
|
||||||
|
} else {
|
||||||
|
sp = task->thread.ksp;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get current stack pointer and initialize stack info */
|
/* Get current stack pointer and initialize stack info */
|
||||||
if (get_stack_info(sp, task, info, mask) != 0 ||
|
if (!update_stack_info(state, sp)) {
|
||||||
!on_stack(info, sp, sizeof(struct stack_frame))) {
|
|
||||||
/* Something is wrong with the stack pointer */
|
/* Something is wrong with the stack pointer */
|
||||||
info->type = STACK_TYPE_UNKNOWN;
|
info->type = STACK_TYPE_UNKNOWN;
|
||||||
state->error = true;
|
state->error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the instruction pointer from pt_regs or the stack frame */
|
if (!regs) {
|
||||||
if (regs) {
|
/* Stack frame is within valid stack */
|
||||||
ip = READ_ONCE_NOCHECK(regs->psw.addr);
|
sf = (struct stack_frame *)sp;
|
||||||
reliable = true;
|
|
||||||
reuse_sp = true;
|
|
||||||
} else {
|
|
||||||
sf = (struct stack_frame *) sp;
|
|
||||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||||
reliable = false;
|
|
||||||
reuse_sp = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
|
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
|
||||||
@ -147,7 +159,17 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
|||||||
/* Update unwind state */
|
/* Update unwind state */
|
||||||
state->sp = sp;
|
state->sp = sp;
|
||||||
state->ip = ip;
|
state->ip = ip;
|
||||||
state->reliable = reliable;
|
state->reliable = true;
|
||||||
state->reuse_sp = reuse_sp;
|
|
||||||
|
if (!first_frame)
|
||||||
|
return;
|
||||||
|
/* Skip through the call chain to the specified starting frame */
|
||||||
|
while (!unwind_done(state)) {
|
||||||
|
if (on_stack(&state->stack_info, first_frame, sizeof(struct stack_frame))) {
|
||||||
|
if (state->sp >= first_frame)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unwind_next_frame(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(__unwind_start);
|
EXPORT_SYMBOL_GPL(__unwind_start);
|
||||||
|
@ -29,13 +29,6 @@
|
|||||||
#include <asm/vdso.h>
|
#include <asm/vdso.h>
|
||||||
#include <asm/facility.h>
|
#include <asm/facility.h>
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT_VDSO
|
|
||||||
extern char vdso32_start, vdso32_end;
|
|
||||||
static void *vdso32_kbase = &vdso32_start;
|
|
||||||
static unsigned int vdso32_pages;
|
|
||||||
static struct page **vdso32_pagelist;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern char vdso64_start, vdso64_end;
|
extern char vdso64_start, vdso64_end;
|
||||||
static void *vdso64_kbase = &vdso64_start;
|
static void *vdso64_kbase = &vdso64_start;
|
||||||
static unsigned int vdso64_pages;
|
static unsigned int vdso64_pages;
|
||||||
@ -55,12 +48,6 @@ static vm_fault_t vdso_fault(const struct vm_special_mapping *sm,
|
|||||||
|
|
||||||
vdso_pagelist = vdso64_pagelist;
|
vdso_pagelist = vdso64_pagelist;
|
||||||
vdso_pages = vdso64_pages;
|
vdso_pages = vdso64_pages;
|
||||||
#ifdef CONFIG_COMPAT_VDSO
|
|
||||||
if (vma->vm_mm->context.compat_mm) {
|
|
||||||
vdso_pagelist = vdso32_pagelist;
|
|
||||||
vdso_pages = vdso32_pages;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (vmf->pgoff >= vdso_pages)
|
if (vmf->pgoff >= vdso_pages)
|
||||||
return VM_FAULT_SIGBUS;
|
return VM_FAULT_SIGBUS;
|
||||||
@ -76,10 +63,6 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
|
|||||||
unsigned long vdso_pages;
|
unsigned long vdso_pages;
|
||||||
|
|
||||||
vdso_pages = vdso64_pages;
|
vdso_pages = vdso64_pages;
|
||||||
#ifdef CONFIG_COMPAT_VDSO
|
|
||||||
if (vma->vm_mm->context.compat_mm)
|
|
||||||
vdso_pages = vdso32_pages;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if ((vdso_pages << PAGE_SHIFT) != vma->vm_end - vma->vm_start)
|
if ((vdso_pages << PAGE_SHIFT) != vma->vm_end - vma->vm_start)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -209,12 +192,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
|||||||
if (!vdso_enabled)
|
if (!vdso_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (is_compat_task())
|
||||||
|
return 0;
|
||||||
|
|
||||||
vdso_pages = vdso64_pages;
|
vdso_pages = vdso64_pages;
|
||||||
#ifdef CONFIG_COMPAT_VDSO
|
|
||||||
mm->context.compat_mm = is_compat_task();
|
|
||||||
if (mm->context.compat_mm)
|
|
||||||
vdso_pages = vdso32_pages;
|
|
||||||
#endif
|
|
||||||
/*
|
/*
|
||||||
* vDSO has a problem and was disabled, just don't "enable" it for
|
* vDSO has a problem and was disabled, just don't "enable" it for
|
||||||
* the process
|
* the process
|
||||||
@ -267,23 +248,6 @@ static int __init vdso_init(void)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
vdso_init_data(vdso_data);
|
vdso_init_data(vdso_data);
|
||||||
#ifdef CONFIG_COMPAT_VDSO
|
|
||||||
/* Calculate the size of the 32 bit vDSO */
|
|
||||||
vdso32_pages = ((&vdso32_end - &vdso32_start
|
|
||||||
+ PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
|
|
||||||
|
|
||||||
/* Make sure pages are in the correct state */
|
|
||||||
vdso32_pagelist = kcalloc(vdso32_pages + 1, sizeof(struct page *),
|
|
||||||
GFP_KERNEL);
|
|
||||||
BUG_ON(vdso32_pagelist == NULL);
|
|
||||||
for (i = 0; i < vdso32_pages - 1; i++) {
|
|
||||||
struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE);
|
|
||||||
get_page(pg);
|
|
||||||
vdso32_pagelist[i] = pg;
|
|
||||||
}
|
|
||||||
vdso32_pagelist[vdso32_pages - 1] = virt_to_page(vdso_data);
|
|
||||||
vdso32_pagelist[vdso32_pages] = NULL;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Calculate the size of the 64 bit vDSO */
|
/* Calculate the size of the 64 bit vDSO */
|
||||||
vdso64_pages = ((&vdso64_end - &vdso64_start
|
vdso64_pages = ((&vdso64_end - &vdso64_start
|
||||||
|
1
arch/s390/kernel/vdso32/.gitignore
vendored
1
arch/s390/kernel/vdso32/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
vdso32.lds
|
|
@ -1,66 +0,0 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
# List of files in the vdso, has to be asm only for now
|
|
||||||
|
|
||||||
KCOV_INSTRUMENT := n
|
|
||||||
|
|
||||||
obj-vdso32 = gettimeofday.o clock_getres.o clock_gettime.o note.o getcpu.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
|
|
||||||
|
|
||||||
KBUILD_AFLAGS_31 := $(filter-out -m64,$(KBUILD_AFLAGS))
|
|
||||||
KBUILD_AFLAGS_31 += -m31 -s
|
|
||||||
|
|
||||||
KBUILD_CFLAGS_31 := $(filter-out -m64,$(KBUILD_CFLAGS))
|
|
||||||
KBUILD_CFLAGS_31 += -m31 -fPIC -shared -fno-common -fno-builtin
|
|
||||||
KBUILD_CFLAGS_31 += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
|
|
||||||
-Wl,--hash-style=both
|
|
||||||
|
|
||||||
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_31)
|
|
||||||
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_31)
|
|
||||||
|
|
||||||
obj-y += vdso32_wrapper.o
|
|
||||||
extra-y += vdso32.lds
|
|
||||||
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
|
|
||||||
|
|
||||||
# link rule for the .so file, .lds has to be first
|
|
||||||
$(obj)/vdso32.so.dbg: $(src)/vdso32.lds $(obj-vdso32) FORCE
|
|
||||||
$(call if_changed,vdso32ld)
|
|
||||||
|
|
||||||
# strip rule for the .so file
|
|
||||||
$(obj)/%.so: OBJCOPYFLAGS := -S
|
|
||||||
$(obj)/%.so: $(obj)/%.so.dbg FORCE
|
|
||||||
$(call if_changed,objcopy)
|
|
||||||
|
|
||||||
# assembly rules for the .S files
|
|
||||||
$(obj-vdso32): %.o: %.S FORCE
|
|
||||||
$(call if_changed_dep,vdso32as)
|
|
||||||
|
|
||||||
# actual build commands
|
|
||||||
quiet_cmd_vdso32ld = VDSO32L $@
|
|
||||||
cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $(filter %.lds %.o,$^) -o $@
|
|
||||||
quiet_cmd_vdso32as = VDSO32A $@
|
|
||||||
cmd_vdso32as = $(CC) $(a_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
|
|
@ -1,44 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Userland implementation of clock_getres() for 32 bits processes in a
|
|
||||||
* s390 kernel for use in the vDSO
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2008
|
|
||||||
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
|
|
||||||
*/
|
|
||||||
#include <asm/vdso.h>
|
|
||||||
#include <asm/asm-offsets.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
#include <asm/dwarf.h>
|
|
||||||
|
|
||||||
.text
|
|
||||||
.align 4
|
|
||||||
.globl __kernel_clock_getres
|
|
||||||
.type __kernel_clock_getres,@function
|
|
||||||
__kernel_clock_getres:
|
|
||||||
CFI_STARTPROC
|
|
||||||
basr %r1,0
|
|
||||||
la %r1,4f-.(%r1)
|
|
||||||
chi %r2,__CLOCK_REALTIME
|
|
||||||
je 0f
|
|
||||||
chi %r2,__CLOCK_MONOTONIC
|
|
||||||
je 0f
|
|
||||||
la %r1,5f-4f(%r1)
|
|
||||||
chi %r2,__CLOCK_REALTIME_COARSE
|
|
||||||
je 0f
|
|
||||||
chi %r2,__CLOCK_MONOTONIC_COARSE
|
|
||||||
jne 3f
|
|
||||||
0: ltr %r3,%r3
|
|
||||||
jz 2f /* res == NULL */
|
|
||||||
1: l %r0,0(%r1)
|
|
||||||
xc 0(4,%r3),0(%r3) /* set tp->tv_sec to zero */
|
|
||||||
st %r0,4(%r3) /* store tp->tv_usec */
|
|
||||||
2: lhi %r2,0
|
|
||||||
br %r14
|
|
||||||
3: lhi %r1,__NR_clock_getres /* fallback to svc */
|
|
||||||
svc 0
|
|
||||||
br %r14
|
|
||||||
CFI_ENDPROC
|
|
||||||
4: .long __CLOCK_REALTIME_RES
|
|
||||||
5: .long __CLOCK_COARSE_RES
|
|
||||||
.size __kernel_clock_getres,.-__kernel_clock_getres
|
|
@ -1,179 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Userland implementation of clock_gettime() for 32 bits processes in a
|
|
||||||
* s390 kernel for use in the vDSO
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2008
|
|
||||||
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
|
|
||||||
*/
|
|
||||||
#include <asm/vdso.h>
|
|
||||||
#include <asm/asm-offsets.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
#include <asm/dwarf.h>
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
|
|
||||||
.text
|
|
||||||
.align 4
|
|
||||||
.globl __kernel_clock_gettime
|
|
||||||
.type __kernel_clock_gettime,@function
|
|
||||||
__kernel_clock_gettime:
|
|
||||||
CFI_STARTPROC
|
|
||||||
ahi %r15,-16
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
|
|
||||||
CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
|
|
||||||
basr %r5,0
|
|
||||||
0: al %r5,21f-0b(%r5) /* get &_vdso_data */
|
|
||||||
chi %r2,__CLOCK_REALTIME_COARSE
|
|
||||||
je 10f
|
|
||||||
chi %r2,__CLOCK_REALTIME
|
|
||||||
je 11f
|
|
||||||
chi %r2,__CLOCK_MONOTONIC_COARSE
|
|
||||||
je 9f
|
|
||||||
chi %r2,__CLOCK_MONOTONIC
|
|
||||||
jne 19f
|
|
||||||
|
|
||||||
/* CLOCK_MONOTONIC */
|
|
||||||
1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
|
|
||||||
tml %r4,0x0001 /* pending update ? loop */
|
|
||||||
jnz 1b
|
|
||||||
stcke 0(%r15) /* Store TOD clock */
|
|
||||||
lm %r0,%r1,1(%r15)
|
|
||||||
s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
|
|
||||||
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
|
|
||||||
brc 3,2f
|
|
||||||
ahi %r0,-1
|
|
||||||
2: ms %r0,__VDSO_TK_MULT(%r5) /* * tk->mult */
|
|
||||||
lr %r2,%r0
|
|
||||||
l %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
ltr %r1,%r1
|
|
||||||
mr %r0,%r0
|
|
||||||
jnm 3f
|
|
||||||
a %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
3: alr %r0,%r2
|
|
||||||
al %r0,__VDSO_WTOM_NSEC(%r5)
|
|
||||||
al %r1,__VDSO_WTOM_NSEC+4(%r5)
|
|
||||||
brc 12,5f
|
|
||||||
ahi %r0,1
|
|
||||||
5: l %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
|
|
||||||
srdl %r0,0(%r2) /* >> tk->shift */
|
|
||||||
l %r2,__VDSO_WTOM_SEC+4(%r5)
|
|
||||||
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
|
|
||||||
jne 1b
|
|
||||||
basr %r5,0
|
|
||||||
6: ltr %r0,%r0
|
|
||||||
jnz 7f
|
|
||||||
cl %r1,20f-6b(%r5)
|
|
||||||
jl 8f
|
|
||||||
7: ahi %r2,1
|
|
||||||
sl %r1,20f-6b(%r5)
|
|
||||||
brc 3,6b
|
|
||||||
ahi %r0,-1
|
|
||||||
j 6b
|
|
||||||
8: st %r2,0(%r3) /* store tp->tv_sec */
|
|
||||||
st %r1,4(%r3) /* store tp->tv_nsec */
|
|
||||||
lhi %r2,0
|
|
||||||
ahi %r15,16
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
|
|
||||||
CFI_RESTORE 15
|
|
||||||
br %r14
|
|
||||||
|
|
||||||
/* CLOCK_MONOTONIC_COARSE */
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
|
|
||||||
CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
|
|
||||||
9: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
|
|
||||||
tml %r4,0x0001 /* pending update ? loop */
|
|
||||||
jnz 9b
|
|
||||||
l %r2,__VDSO_WTOM_CRS_SEC+4(%r5)
|
|
||||||
l %r1,__VDSO_WTOM_CRS_NSEC+4(%r5)
|
|
||||||
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
|
|
||||||
jne 9b
|
|
||||||
j 8b
|
|
||||||
|
|
||||||
/* CLOCK_REALTIME_COARSE */
|
|
||||||
10: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
|
|
||||||
tml %r4,0x0001 /* pending update ? loop */
|
|
||||||
jnz 10b
|
|
||||||
l %r2,__VDSO_XTIME_CRS_SEC+4(%r5)
|
|
||||||
l %r1,__VDSO_XTIME_CRS_NSEC+4(%r5)
|
|
||||||
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
|
|
||||||
jne 10b
|
|
||||||
j 17f
|
|
||||||
|
|
||||||
/* CLOCK_REALTIME */
|
|
||||||
11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
|
|
||||||
tml %r4,0x0001 /* pending update ? loop */
|
|
||||||
jnz 11b
|
|
||||||
stcke 0(%r15) /* Store TOD clock */
|
|
||||||
lm %r0,%r1,__VDSO_TS_END(%r5) /* TOD steering end time */
|
|
||||||
s %r0,1(%r15) /* no - ts_steering_end */
|
|
||||||
sl %r1,5(%r15)
|
|
||||||
brc 3,22f
|
|
||||||
ahi %r0,-1
|
|
||||||
22: ltr %r0,%r0 /* past end of steering? */
|
|
||||||
jm 24f
|
|
||||||
srdl %r0,15 /* 1 per 2^16 */
|
|
||||||
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
|
|
||||||
jz 23f
|
|
||||||
lcr %r0,%r0 /* negative TOD offset */
|
|
||||||
lcr %r1,%r1
|
|
||||||
je 23f
|
|
||||||
ahi %r0,-1
|
|
||||||
23: a %r0,1(%r15) /* add TOD timestamp */
|
|
||||||
al %r1,5(%r15)
|
|
||||||
brc 12,25f
|
|
||||||
ahi %r0,1
|
|
||||||
j 25f
|
|
||||||
24: lm %r0,%r1,1(%r15) /* load TOD timestamp */
|
|
||||||
25: s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
|
|
||||||
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
|
|
||||||
brc 3,12f
|
|
||||||
ahi %r0,-1
|
|
||||||
12: ms %r0,__VDSO_TK_MULT(%r5) /* * tk->mult */
|
|
||||||
lr %r2,%r0
|
|
||||||
l %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
ltr %r1,%r1
|
|
||||||
mr %r0,%r0
|
|
||||||
jnm 13f
|
|
||||||
a %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
13: alr %r0,%r2
|
|
||||||
al %r0,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */
|
|
||||||
al %r1,__VDSO_XTIME_NSEC+4(%r5)
|
|
||||||
brc 12,14f
|
|
||||||
ahi %r0,1
|
|
||||||
14: l %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
|
|
||||||
srdl %r0,0(%r2) /* >> tk->shift */
|
|
||||||
l %r2,__VDSO_XTIME_SEC+4(%r5)
|
|
||||||
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
|
|
||||||
jne 11b
|
|
||||||
basr %r5,0
|
|
||||||
15: ltr %r0,%r0
|
|
||||||
jnz 16f
|
|
||||||
cl %r1,20f-15b(%r5)
|
|
||||||
jl 17f
|
|
||||||
16: ahi %r2,1
|
|
||||||
sl %r1,20f-15b(%r5)
|
|
||||||
brc 3,15b
|
|
||||||
ahi %r0,-1
|
|
||||||
j 15b
|
|
||||||
17: st %r2,0(%r3) /* store tp->tv_sec */
|
|
||||||
st %r1,4(%r3) /* store tp->tv_nsec */
|
|
||||||
lhi %r2,0
|
|
||||||
ahi %r15,16
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
|
|
||||||
CFI_RESTORE 15
|
|
||||||
br %r14
|
|
||||||
|
|
||||||
/* Fallback to system call */
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD+16
|
|
||||||
CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
|
|
||||||
19: lhi %r1,__NR_clock_gettime
|
|
||||||
svc 0
|
|
||||||
ahi %r15,16
|
|
||||||
CFI_DEF_CFA_OFFSET STACK_FRAME_OVERHEAD
|
|
||||||
CFI_RESTORE 15
|
|
||||||
br %r14
|
|
||||||
CFI_ENDPROC
|
|
||||||
|
|
||||||
20: .long 1000000000
|
|
||||||
21: .long _vdso_data - 0b
|
|
||||||
.size __kernel_clock_gettime,.-__kernel_clock_gettime
|
|
@ -1,33 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Userland implementation of getcpu() for 32 bits processes in a
|
|
||||||
* s390 kernel for use in the vDSO
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2016
|
|
||||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
|
||||||
*/
|
|
||||||
#include <asm/vdso.h>
|
|
||||||
#include <asm/asm-offsets.h>
|
|
||||||
#include <asm/dwarf.h>
|
|
||||||
|
|
||||||
.text
|
|
||||||
.align 4
|
|
||||||
.globl __kernel_getcpu
|
|
||||||
.type __kernel_getcpu,@function
|
|
||||||
__kernel_getcpu:
|
|
||||||
CFI_STARTPROC
|
|
||||||
la %r4,0
|
|
||||||
sacf 256
|
|
||||||
l %r5,__VDSO_CPU_NR(%r4)
|
|
||||||
l %r4,__VDSO_NODE_ID(%r4)
|
|
||||||
sacf 0
|
|
||||||
ltr %r2,%r2
|
|
||||||
jz 2f
|
|
||||||
st %r5,0(%r2)
|
|
||||||
2: ltr %r3,%r3
|
|
||||||
jz 3f
|
|
||||||
st %r4,0(%r3)
|
|
||||||
3: lhi %r2,0
|
|
||||||
br %r14
|
|
||||||
CFI_ENDPROC
|
|
||||||
.size __kernel_getcpu,.-__kernel_getcpu
|
|
@ -1,103 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* Userland implementation of gettimeofday() for 32 bits processes in a
|
|
||||||
* s390 kernel for use in the vDSO
|
|
||||||
*
|
|
||||||
* Copyright IBM Corp. 2008
|
|
||||||
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
|
|
||||||
*/
|
|
||||||
#include <asm/vdso.h>
|
|
||||||
#include <asm/asm-offsets.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
#include <asm/dwarf.h>
|
|
||||||
#include <asm/ptrace.h>
|
|
||||||
|
|
||||||
.text
|
|
||||||
.align 4
|
|
||||||
.globl __kernel_gettimeofday
|
|
||||||
.type __kernel_gettimeofday,@function
|
|
||||||
__kernel_gettimeofday:
|
|
||||||
CFI_STARTPROC
|
|
||||||
ahi %r15,-16
|
|
||||||
CFI_ADJUST_CFA_OFFSET 16
|
|
||||||
CFI_VAL_OFFSET 15, -STACK_FRAME_OVERHEAD
|
|
||||||
basr %r5,0
|
|
||||||
0: al %r5,13f-0b(%r5) /* get &_vdso_data */
|
|
||||||
1: ltr %r3,%r3 /* check if tz is NULL */
|
|
||||||
je 2f
|
|
||||||
mvc 0(8,%r3),__VDSO_TIMEZONE(%r5)
|
|
||||||
2: ltr %r2,%r2 /* check if tv is NULL */
|
|
||||||
je 10f
|
|
||||||
l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
|
|
||||||
tml %r4,0x0001 /* pending update ? loop */
|
|
||||||
jnz 1b
|
|
||||||
stcke 0(%r15) /* Store TOD clock */
|
|
||||||
lm %r0,%r1,__VDSO_TS_END(%r5) /* TOD steering end time */
|
|
||||||
s %r0,1(%r15)
|
|
||||||
sl %r1,5(%r15)
|
|
||||||
brc 3,14f
|
|
||||||
ahi %r0,-1
|
|
||||||
14: ltr %r0,%r0 /* past end of steering? */
|
|
||||||
jm 16f
|
|
||||||
srdl %r0,15 /* 1 per 2^16 */
|
|
||||||
tm __VDSO_TS_DIR+3(%r5),0x01 /* steering direction? */
|
|
||||||
jz 15f
|
|
||||||
lcr %r0,%r0 /* negative TOD offset */
|
|
||||||
lcr %r1,%r1
|
|
||||||
je 15f
|
|
||||||
ahi %r0,-1
|
|
||||||
15: a %r0,1(%r15) /* add TOD timestamp */
|
|
||||||
al %r1,5(%r15)
|
|
||||||
brc 12,17f
|
|
||||||
ahi %r0,1
|
|
||||||
j 17f
|
|
||||||
16: lm %r0,%r1,1(%r15) /* load TOD timestamp */
|
|
||||||
17: s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
|
|
||||||
sl %r1,__VDSO_XTIME_STAMP+4(%r5)
|
|
||||||
brc 3,3f
|
|
||||||
ahi %r0,-1
|
|
||||||
3: ms %r0,__VDSO_TK_MULT(%r5) /* * tk->mult */
|
|
||||||
st %r0,0(%r15)
|
|
||||||
l %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
ltr %r1,%r1
|
|
||||||
mr %r0,%r0
|
|
||||||
jnm 4f
|
|
||||||
a %r0,__VDSO_TK_MULT(%r5)
|
|
||||||
4: al %r0,0(%r15)
|
|
||||||
al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
|
|
||||||
al %r1,__VDSO_XTIME_NSEC+4(%r5)
|
|
||||||
brc 12,5f
|
|
||||||
ahi %r0,1
|
|
||||||
5: mvc 0(4,%r15),__VDSO_XTIME_SEC+4(%r5)
|
|
||||||
cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
|
|
||||||
jne 1b
|
|
||||||
l %r4,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */
|
|
||||||
srdl %r0,0(%r4) /* >> tk->shift */
|
|
||||||
l %r4,0(%r15) /* get tv_sec from stack */
|
|
||||||
basr %r5,0
|
|
||||||
6: ltr %r0,%r0
|
|
||||||
jnz 7f
|
|
||||||
cl %r1,11f-6b(%r5)
|
|
||||||
jl 8f
|
|
||||||
7: ahi %r4,1
|
|
||||||
sl %r1,11f-6b(%r5)
|
|
||||||
brc 3,6b
|
|
||||||
ahi %r0,-1
|
|
||||||
j 6b
|
|
||||||
8: st %r4,0(%r2) /* store tv->tv_sec */
|
|
||||||
ltr %r1,%r1
|
|
||||||
m %r0,12f-6b(%r5)
|
|
||||||
jnm 9f
|
|
||||||
al %r0,12f-6b(%r5)
|
|
||||||
9: srl %r0,6
|
|
||||||
st %r0,4(%r2) /* store tv->tv_usec */
|
|
||||||
10: slr %r2,%r2
|
|
||||||
ahi %r15,16
|
|
||||||
CFI_ADJUST_CFA_OFFSET -16
|
|
||||||
CFI_RESTORE 15
|
|
||||||
br %r14
|
|
||||||
CFI_ENDPROC
|
|
||||||
11: .long 1000000000
|
|
||||||
12: .long 274877907
|
|
||||||
13: .long _vdso_data - 0b
|
|
||||||
.size __kernel_gettimeofday,.-__kernel_gettimeofday
|
|
@ -1,13 +0,0 @@
|
|||||||
/* 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
|
|
@ -1,142 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
|
||||||
/*
|
|
||||||
* This is the infamous ld script for the 32 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
|
|
||||||
{
|
|
||||||
. = VDSO32_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)) }
|
|
||||||
|
|
||||||
. = ALIGN(PAGE_SIZE);
|
|
||||||
PROVIDE(_vdso_data = .);
|
|
||||||
|
|
||||||
/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_gettimeofday;
|
|
||||||
__kernel_clock_gettime;
|
|
||||||
__kernel_clock_getres;
|
|
||||||
__kernel_getcpu;
|
|
||||||
|
|
||||||
local: *;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
/* 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
|
|
@ -16,10 +16,8 @@
|
|||||||
.type __kernel_getcpu,@function
|
.type __kernel_getcpu,@function
|
||||||
__kernel_getcpu:
|
__kernel_getcpu:
|
||||||
CFI_STARTPROC
|
CFI_STARTPROC
|
||||||
la %r4,0
|
|
||||||
sacf 256
|
sacf 256
|
||||||
l %r5,__VDSO_CPU_NR(%r4)
|
lm %r4,%r5,__VDSO_GETCPU_VAL(%r0)
|
||||||
l %r4,__VDSO_NODE_ID(%r4)
|
|
||||||
sacf 0
|
sacf 0
|
||||||
ltgr %r2,%r2
|
ltgr %r2,%r2
|
||||||
jz 2f
|
jz 2f
|
||||||
|
@ -11,3 +11,6 @@ lib-$(CONFIG_UPROBES) += probes.o
|
|||||||
# Instrumenting memory accesses to __user data (in different address space)
|
# Instrumenting memory accesses to __user data (in different address space)
|
||||||
# produce false positives
|
# produce false positives
|
||||||
KASAN_SANITIZE_uaccess.o := n
|
KASAN_SANITIZE_uaccess.o := n
|
||||||
|
|
||||||
|
obj-$(CONFIG_S390_UNWIND_SELFTEST) += test_unwind.o
|
||||||
|
CFLAGS_test_unwind.o += -fno-optimize-sibling-calls
|
||||||
|
347
arch/s390/lib/test_unwind.c
Normal file
347
arch/s390/lib/test_unwind.c
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* Test module for unwind_for_each_frame
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "test_unwind: " fmt
|
||||||
|
#include <asm/unwind.h>
|
||||||
|
#include <linux/completion.h>
|
||||||
|
#include <linux/kallsyms.h>
|
||||||
|
#include <linux/kthread.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/wait.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/delay.h>
|
||||||
|
|
||||||
|
#define BT_BUF_SIZE (PAGE_SIZE * 4)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To avoid printk line limit split backtrace by lines
|
||||||
|
*/
|
||||||
|
static void print_backtrace(char *bt)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
p = strsep(&bt, "\n");
|
||||||
|
if (!p)
|
||||||
|
break;
|
||||||
|
pr_err("%s\n", p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calls unwind_for_each_frame(task, regs, sp) and verifies that the result
|
||||||
|
* contains unwindme_func2 followed by unwindme_func1.
|
||||||
|
*/
|
||||||
|
static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
|
||||||
|
unsigned long sp)
|
||||||
|
{
|
||||||
|
int frame_count, prev_is_func2, seen_func2_func1;
|
||||||
|
const int max_frames = 128;
|
||||||
|
struct unwind_state state;
|
||||||
|
size_t bt_pos = 0;
|
||||||
|
int ret = 0;
|
||||||
|
char *bt;
|
||||||
|
|
||||||
|
bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC);
|
||||||
|
if (!bt) {
|
||||||
|
pr_err("failed to allocate backtrace buffer\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
/* Unwind. */
|
||||||
|
frame_count = 0;
|
||||||
|
prev_is_func2 = 0;
|
||||||
|
seen_func2_func1 = 0;
|
||||||
|
unwind_for_each_frame(&state, task, regs, sp) {
|
||||||
|
unsigned long addr = unwind_get_return_address(&state);
|
||||||
|
char sym[KSYM_SYMBOL_LEN];
|
||||||
|
|
||||||
|
if (frame_count++ == max_frames)
|
||||||
|
break;
|
||||||
|
if (state.reliable && !addr) {
|
||||||
|
pr_err("unwind state reliable but addr is 0\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
sprint_symbol(sym, addr);
|
||||||
|
if (bt_pos < BT_BUF_SIZE) {
|
||||||
|
bt_pos += snprintf(bt + bt_pos, BT_BUF_SIZE - bt_pos,
|
||||||
|
state.reliable ? " [%-7s%px] %pSR\n" :
|
||||||
|
"([%-7s%px] %pSR)\n",
|
||||||
|
stack_type_name(state.stack_info.type),
|
||||||
|
(void *)state.sp, (void *)state.ip);
|
||||||
|
if (bt_pos >= BT_BUF_SIZE)
|
||||||
|
pr_err("backtrace buffer is too small\n");
|
||||||
|
}
|
||||||
|
frame_count += 1;
|
||||||
|
if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
|
||||||
|
seen_func2_func1 = 1;
|
||||||
|
prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the results. */
|
||||||
|
if (unwind_error(&state)) {
|
||||||
|
pr_err("unwind error\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
if (!seen_func2_func1) {
|
||||||
|
pr_err("unwindme_func2 and unwindme_func1 not found\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
if (frame_count == max_frames) {
|
||||||
|
pr_err("Maximum number of frames exceeded\n");
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
if (ret)
|
||||||
|
print_backtrace(bt);
|
||||||
|
kfree(bt);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* State of the task being unwound. */
|
||||||
|
struct unwindme {
|
||||||
|
int flags;
|
||||||
|
int ret;
|
||||||
|
struct task_struct *task;
|
||||||
|
struct completion task_ready;
|
||||||
|
wait_queue_head_t task_wq;
|
||||||
|
unsigned long sp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct unwindme *unwindme;
|
||||||
|
|
||||||
|
/* Values of unwindme.flags. */
|
||||||
|
#define UWM_DEFAULT 0x0
|
||||||
|
#define UWM_THREAD 0x1 /* Unwind a separate task. */
|
||||||
|
#define UWM_REGS 0x2 /* Pass regs to test_unwind(). */
|
||||||
|
#define UWM_SP 0x4 /* Pass sp to test_unwind(). */
|
||||||
|
#define UWM_CALLER 0x8 /* Unwind starting from caller. */
|
||||||
|
#define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */
|
||||||
|
#define UWM_IRQ 0x20 /* Unwind from irq context. */
|
||||||
|
#define UWM_PGM 0x40 /* Unwind from program check handler. */
|
||||||
|
|
||||||
|
static __always_inline unsigned long get_psw_addr(void)
|
||||||
|
{
|
||||||
|
unsigned long psw_addr;
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"basr %[psw_addr],0\n"
|
||||||
|
: [psw_addr] "=d" (psw_addr));
|
||||||
|
return psw_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
static int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct unwindme *u = unwindme;
|
||||||
|
|
||||||
|
u->ret = test_unwind(NULL, (u->flags & UWM_REGS) ? regs : NULL,
|
||||||
|
(u->flags & UWM_SP) ? u->sp : 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This function may or may not appear in the backtrace. */
|
||||||
|
static noinline int unwindme_func4(struct unwindme *u)
|
||||||
|
{
|
||||||
|
if (!(u->flags & UWM_CALLER))
|
||||||
|
u->sp = current_frame_address();
|
||||||
|
if (u->flags & UWM_THREAD) {
|
||||||
|
complete(&u->task_ready);
|
||||||
|
wait_event(u->task_wq, kthread_should_park());
|
||||||
|
kthread_parkme();
|
||||||
|
return 0;
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
} else if (u->flags & UWM_PGM) {
|
||||||
|
struct kprobe kp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
unwindme = u;
|
||||||
|
memset(&kp, 0, sizeof(kp));
|
||||||
|
kp.symbol_name = "do_report_trap";
|
||||||
|
kp.pre_handler = pgm_pre_handler;
|
||||||
|
ret = register_kprobe(&kp);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("register_kprobe failed %d\n", ret);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* trigger specification exception
|
||||||
|
*/
|
||||||
|
asm volatile(
|
||||||
|
" mvcl %%r1,%%r1\n"
|
||||||
|
"0: nopr %%r7\n"
|
||||||
|
EX_TABLE(0b, 0b)
|
||||||
|
:);
|
||||||
|
|
||||||
|
unregister_kprobe(&kp);
|
||||||
|
unwindme = NULL;
|
||||||
|
return u->ret;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
struct pt_regs regs;
|
||||||
|
|
||||||
|
memset(®s, 0, sizeof(regs));
|
||||||
|
regs.psw.addr = get_psw_addr();
|
||||||
|
regs.gprs[15] = current_stack_pointer();
|
||||||
|
return test_unwind(NULL,
|
||||||
|
(u->flags & UWM_REGS) ? ®s : NULL,
|
||||||
|
(u->flags & UWM_SP) ? u->sp : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function may or may not appear in the backtrace. */
|
||||||
|
static noinline int unwindme_func3(struct unwindme *u)
|
||||||
|
{
|
||||||
|
u->sp = current_frame_address();
|
||||||
|
return unwindme_func4(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function must appear in the backtrace. */
|
||||||
|
static noinline int unwindme_func2(struct unwindme *u)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (u->flags & UWM_SWITCH_STACK) {
|
||||||
|
preempt_disable();
|
||||||
|
rc = CALL_ON_STACK(unwindme_func3, S390_lowcore.nodat_stack, 1, u);
|
||||||
|
preempt_enable();
|
||||||
|
return rc;
|
||||||
|
} else {
|
||||||
|
return unwindme_func3(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function must follow unwindme_func2 in the backtrace. */
|
||||||
|
static noinline int unwindme_func1(void *u)
|
||||||
|
{
|
||||||
|
return unwindme_func2((struct unwindme *)u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unwindme_irq_handler(struct ext_code ext_code,
|
||||||
|
unsigned int param32,
|
||||||
|
unsigned long param64)
|
||||||
|
{
|
||||||
|
struct unwindme *u = READ_ONCE(unwindme);
|
||||||
|
|
||||||
|
if (u && u->task == current) {
|
||||||
|
unwindme = NULL;
|
||||||
|
u->task = NULL;
|
||||||
|
u->ret = unwindme_func1(u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_unwind_irq(struct unwindme *u)
|
||||||
|
{
|
||||||
|
preempt_disable();
|
||||||
|
if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) {
|
||||||
|
pr_info("Couldn't reqister external interrupt handler");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
u->task = current;
|
||||||
|
unwindme = u;
|
||||||
|
udelay(1);
|
||||||
|
unregister_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler);
|
||||||
|
preempt_enable();
|
||||||
|
return u->ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spawns a task and passes it to test_unwind(). */
|
||||||
|
static int test_unwind_task(struct unwindme *u)
|
||||||
|
{
|
||||||
|
struct task_struct *task;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Initialize thread-related fields. */
|
||||||
|
init_completion(&u->task_ready);
|
||||||
|
init_waitqueue_head(&u->task_wq);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the task and wait until it reaches unwindme_func4() and sleeps
|
||||||
|
* in (task_ready, unwind_done] range.
|
||||||
|
*/
|
||||||
|
task = kthread_run(unwindme_func1, u, "%s", __func__);
|
||||||
|
if (IS_ERR(task)) {
|
||||||
|
pr_err("kthread_run() failed\n");
|
||||||
|
return PTR_ERR(task);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Make sure task reaches unwindme_func4 before parking it,
|
||||||
|
* we might park it before kthread function has been executed otherwise
|
||||||
|
*/
|
||||||
|
wait_for_completion(&u->task_ready);
|
||||||
|
kthread_park(task);
|
||||||
|
/* Unwind. */
|
||||||
|
ret = test_unwind(task, NULL, (u->flags & UWM_SP) ? u->sp : 0);
|
||||||
|
kthread_stop(task);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_unwind_flags(int flags)
|
||||||
|
{
|
||||||
|
struct unwindme u;
|
||||||
|
|
||||||
|
u.flags = flags;
|
||||||
|
if (u.flags & UWM_THREAD)
|
||||||
|
return test_unwind_task(&u);
|
||||||
|
else if (u.flags & UWM_IRQ)
|
||||||
|
return test_unwind_irq(&u);
|
||||||
|
else
|
||||||
|
return unwindme_func1(&u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_unwind_init(void)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
#define TEST(flags) \
|
||||||
|
do { \
|
||||||
|
pr_info("[ RUN ] " #flags "\n"); \
|
||||||
|
if (!test_unwind_flags((flags))) { \
|
||||||
|
pr_info("[ OK ] " #flags "\n"); \
|
||||||
|
} else { \
|
||||||
|
pr_err("[ FAILED ] " #flags "\n"); \
|
||||||
|
ret = -EINVAL; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
TEST(UWM_DEFAULT);
|
||||||
|
TEST(UWM_SP);
|
||||||
|
TEST(UWM_REGS);
|
||||||
|
TEST(UWM_SWITCH_STACK);
|
||||||
|
TEST(UWM_SP | UWM_REGS);
|
||||||
|
TEST(UWM_CALLER | UWM_SP);
|
||||||
|
TEST(UWM_CALLER | UWM_SP | UWM_REGS);
|
||||||
|
TEST(UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK);
|
||||||
|
TEST(UWM_THREAD);
|
||||||
|
TEST(UWM_THREAD | UWM_SP);
|
||||||
|
TEST(UWM_THREAD | UWM_CALLER | UWM_SP);
|
||||||
|
TEST(UWM_IRQ);
|
||||||
|
TEST(UWM_IRQ | UWM_SWITCH_STACK);
|
||||||
|
TEST(UWM_IRQ | UWM_SP);
|
||||||
|
TEST(UWM_IRQ | UWM_REGS);
|
||||||
|
TEST(UWM_IRQ | UWM_SP | UWM_REGS);
|
||||||
|
TEST(UWM_IRQ | UWM_CALLER | UWM_SP);
|
||||||
|
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS);
|
||||||
|
TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK);
|
||||||
|
#ifdef CONFIG_KPROBES
|
||||||
|
TEST(UWM_PGM);
|
||||||
|
TEST(UWM_PGM | UWM_SP);
|
||||||
|
TEST(UWM_PGM | UWM_REGS);
|
||||||
|
TEST(UWM_PGM | UWM_SP | UWM_REGS);
|
||||||
|
#endif
|
||||||
|
#undef TEST
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_unwind_exit(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(test_unwind_init);
|
||||||
|
module_exit(test_unwind_exit);
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -119,9 +119,15 @@ 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)
|
||||||
{
|
{
|
||||||
if (S390_lowcore.nodat_stack != 0)
|
int rc;
|
||||||
return CALL_ON_STACK(_memcpy_real, S390_lowcore.nodat_stack,
|
|
||||||
3, dest, src, count);
|
if (S390_lowcore.nodat_stack != 0) {
|
||||||
|
preempt_disable();
|
||||||
|
rc = CALL_ON_STACK(_memcpy_real, S390_lowcore.nodat_stack, 3,
|
||||||
|
dest, src, count);
|
||||||
|
preempt_enable();
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* This is a really early memcpy_real call, the stacks are
|
* This is a really early memcpy_real call, the stacks are
|
||||||
* not set up yet. Just call _memcpy_real on the early boot
|
* not set up yet. Just call _memcpy_real on the early boot
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <linux/seq_file.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
|
||||||
#include <asm/isc.h>
|
#include <asm/isc.h>
|
||||||
#include <asm/airq.h>
|
#include <asm/airq.h>
|
||||||
@ -659,6 +660,8 @@ static int zpci_alloc_domain(struct zpci_dev *zdev)
|
|||||||
spin_lock(&zpci_domain_lock);
|
spin_lock(&zpci_domain_lock);
|
||||||
if (test_bit(zdev->domain, zpci_domain)) {
|
if (test_bit(zdev->domain, zpci_domain)) {
|
||||||
spin_unlock(&zpci_domain_lock);
|
spin_unlock(&zpci_domain_lock);
|
||||||
|
pr_err("Adding PCI function %08x failed because domain %04x is already assigned\n",
|
||||||
|
zdev->fid, zdev->domain);
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
set_bit(zdev->domain, zpci_domain);
|
set_bit(zdev->domain, zpci_domain);
|
||||||
@ -670,6 +673,8 @@ static int zpci_alloc_domain(struct zpci_dev *zdev)
|
|||||||
zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
|
zdev->domain = find_first_zero_bit(zpci_domain, ZPCI_NR_DEVICES);
|
||||||
if (zdev->domain == ZPCI_NR_DEVICES) {
|
if (zdev->domain == ZPCI_NR_DEVICES) {
|
||||||
spin_unlock(&zpci_domain_lock);
|
spin_unlock(&zpci_domain_lock);
|
||||||
|
pr_err("Adding PCI function %08x failed because the configured limit of %d is reached\n",
|
||||||
|
zdev->fid, ZPCI_NR_DEVICES);
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
set_bit(zdev->domain, zpci_domain);
|
set_bit(zdev->domain, zpci_domain);
|
||||||
|
@ -61,6 +61,7 @@ struct error_hdr {
|
|||||||
#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
|
#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
|
||||||
#define REP82_ERROR_RESERVED_FIELD 0x88
|
#define REP82_ERROR_RESERVED_FIELD 0x88
|
||||||
#define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A
|
#define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A
|
||||||
|
#define REP82_ERROR_FILTERED_BY_HYPERVISOR 0x8B
|
||||||
#define REP82_ERROR_TRANSPORT_FAIL 0x90
|
#define REP82_ERROR_TRANSPORT_FAIL 0x90
|
||||||
#define REP82_ERROR_PACKET_TRUNCATED 0xA0
|
#define REP82_ERROR_PACKET_TRUNCATED 0xA0
|
||||||
#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
|
#define REP82_ERROR_ZERO_BUFFER_LEN 0xB0
|
||||||
@ -91,6 +92,7 @@ static inline int convert_error(struct zcrypt_queue *zq,
|
|||||||
case REP82_ERROR_INVALID_DOMAIN_PRECHECK:
|
case REP82_ERROR_INVALID_DOMAIN_PRECHECK:
|
||||||
case REP82_ERROR_INVALID_DOMAIN_PENDING:
|
case REP82_ERROR_INVALID_DOMAIN_PENDING:
|
||||||
case REP82_ERROR_INVALID_SPECIAL_CMD:
|
case REP82_ERROR_INVALID_SPECIAL_CMD:
|
||||||
|
case REP82_ERROR_FILTERED_BY_HYPERVISOR:
|
||||||
// REP88_ERROR_INVALID_KEY // '82' CEX2A
|
// REP88_ERROR_INVALID_KEY // '82' CEX2A
|
||||||
// REP88_ERROR_OPERAND // '84' CEX2A
|
// REP88_ERROR_OPERAND // '84' CEX2A
|
||||||
// REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A
|
// REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A
|
||||||
|
Loading…
Reference in New Issue
Block a user