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:
Linus Torvalds 2019-12-03 12:50:00 -08:00
commit 01d1dff646
35 changed files with 627 additions and 768 deletions

View File

@ -170,6 +170,7 @@ config S390
select HAVE_PERF_EVENTS
select HAVE_RCU_TABLE_FREE
select HAVE_REGS_AND_STACK_ACCESS_API
select HAVE_RELIABLE_STACKTRACE
select HAVE_RSEQ
select HAVE_SYSCALL_TRACEPOINTS
select HAVE_VIRT_CPU_ACCOUNTING
@ -426,9 +427,6 @@ config COMPAT
(and some other stuff like libraries and such) is needed for
executing 31 bit applications. It is safe to say "Y".
config COMPAT_VDSO
def_bool COMPAT && !CC_IS_CLANG
config SYSVIPC_COMPAT
def_bool y if COMPAT && SYSVIPC
@ -1018,3 +1016,17 @@ config S390_GUEST
the KVM hypervisor.
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

View File

@ -157,7 +157,6 @@ zfcpdump:
vdso_install:
$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso64 $@
$(Q)$(MAKE) $(build)=arch/$(ARCH)/kernel/vdso32 $@
archclean:
$(Q)$(MAKE) $(clean)=$(boot)

View File

@ -170,6 +170,11 @@ void startup_kernel(void)
handle_relocs(__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 */
if (IS_ENABLED(CONFIG_KERNEL_UNCOMPRESSED))
memset(img, 0, vmlinux.image_size);

View File

@ -313,7 +313,7 @@ static inline unsigned long *trailer_entry_ptr(unsigned long v)
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 */
static inline int is_link_entry(unsigned long *s)
{

View File

@ -12,6 +12,7 @@
#include <linux/perf_event.h>
#include <linux/device.h>
#include <asm/stacktrace.h>
/* Per-CPU flags for PMU states */
#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 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 */

View File

@ -310,7 +310,7 @@ void enabled_wait(void);
/*
* 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;

View File

@ -33,12 +33,12 @@ static inline bool on_stack(struct stack_info *info,
return addr >= info->begin && addr + len <= info->end;
}
static inline unsigned long get_stack_pointer(struct task_struct *task,
struct pt_regs *regs)
static __always_inline unsigned long get_stack_pointer(struct task_struct *task,
struct pt_regs *regs)
{
if (regs)
return (unsigned long) kernel_stack_pointer(regs);
if (!task || task == current)
if (task == current)
return current_stack_pointer();
return (unsigned long) task->thread.ksp;
}
@ -62,6 +62,17 @@ struct stack_frame {
};
#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() \
register unsigned long r2 asm("2")
#define CALL_ARGS_1(arg1) \
@ -95,20 +106,33 @@ struct stack_frame {
#define CALL_ON_STACK(fn, stack, nr, args...) \
({ \
unsigned long frame = current_frame_address(); \
CALL_ARGS_##nr(args); \
unsigned long prev; \
\
asm volatile( \
" la %[_prev],0(15)\n" \
" la 15,0(%[_stack])\n" \
" stg %[_prev],%[_bc](15)\n" \
" lg 15,%[_stack]\n" \
" stg %[_frame],%[_bc](15)\n" \
" brasl 14,%[_fn]\n" \
" la 15,0(%[_prev])\n" \
: [_prev] "=&a" (prev), CALL_FMT_##nr \
[_stack] "a" (stack), \
[_stack] "R" (stack), \
[_bc] "i" (offsetof(struct stack_frame, back_chain)), \
[_frame] "d" (frame), \
[_fn] "X" (fn) : CALL_CLOBBER_##nr); \
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 */

View File

@ -35,7 +35,6 @@ struct unwind_state {
struct task_struct *task;
struct pt_regs *regs;
unsigned long sp, ip;
bool reuse_sp;
int graph_idx;
bool reliable;
bool error;
@ -59,10 +58,11 @@ static inline bool unwind_error(struct unwind_state *state)
static inline void unwind_start(struct unwind_state *state,
struct task_struct *task,
struct pt_regs *regs,
unsigned long sp)
unsigned long first_frame)
{
sp = sp ? : get_stack_pointer(task, regs);
__unwind_start(state, task, regs, sp);
task = task ?: current;
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)

View File

@ -41,8 +41,17 @@ struct vdso_data {
struct vdso_per_cpu_data {
__u64 ectg_timer_base;
__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;

View File

@ -81,4 +81,3 @@ obj-$(CONFIG_TRACEPOINTS) += trace.o
# vdso
obj-y += vdso64/
obj-$(CONFIG_COMPAT_VDSO) += vdso32/

View File

@ -78,8 +78,7 @@ int main(void)
OFFSET(__VDSO_TS_END, vdso_data, ts_end);
OFFSET(__VDSO_ECTG_BASE, vdso_per_cpu_data, ectg_timer_base);
OFFSET(__VDSO_ECTG_USER, vdso_per_cpu_data, ectg_user_time);
OFFSET(__VDSO_CPU_NR, vdso_per_cpu_data, cpu_nr);
OFFSET(__VDSO_NODE_ID, vdso_per_cpu_data, node_id);
OFFSET(__VDSO_GETCPU_VAL, vdso_per_cpu_data, getcpu_val);
BLANK();
/* constants used by the vdso */
DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME);

View File

@ -38,6 +38,7 @@ const char *stack_type_name(enum stack_type type)
return "unknown";
}
}
EXPORT_SYMBOL_GPL(stack_type_name);
static inline bool in_stack(unsigned long sp, struct stack_info *info,
enum stack_type type, unsigned long low,
@ -93,7 +94,9 @@ int get_stack_info(unsigned long sp, struct task_struct *task,
if (!sp)
goto unknown;
task = task ? : current;
/* Sanity check: ABI requires SP to be aligned 8 bytes. */
if (sp & 0x7)
goto unknown;
/* Check per-task stack */
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;
printk("Call Trace:\n");
if (!task)
task = current;
unwind_for_each_frame(&state, task, NULL, (unsigned long) stack)
printk(state.reliable ? " [<%016lx>] %pSR \n" :
"([<%016lx>] %pSR)\n",

View File

@ -31,7 +31,7 @@ ENTRY(startup_continue)
#
larl %r14,init_task
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
brasl %r14,kasan_early_init
#endif

View File

@ -164,7 +164,9 @@ static bool kdump_csum_valid(struct kimage *image)
#ifdef CONFIG_CRASH_DUMP
int rc;
preempt_disable();
rc = CALL_ON_STACK(do_start_kdump, S390_lowcore.nodat_stack, 1, image);
preempt_enable();
return rc == 0;
#else
return false;
@ -254,10 +256,10 @@ void arch_crash_save_vmcoreinfo(void)
VMCOREINFO_SYMBOL(lowcore_ptr);
VMCOREINFO_SYMBOL(high_memory);
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("EDMA=%lx\n", __edma);
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
}
void machine_shutdown(void)

View File

@ -156,8 +156,8 @@ static void free_sampling_buffer(struct sf_buffer *sfb)
}
}
debug_sprintf_event(sfdbg, 5, "%s freed sdbt %p\n", __func__,
sfb->sdbt);
debug_sprintf_event(sfdbg, 5, "%s: freed sdbt %#lx\n", __func__,
(unsigned long)sfb->sdbt);
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)
{
int i, rc;
unsigned long *new, *tail;
unsigned long *new, *tail, *tail_prev = NULL;
if (!sfb->sdbt || !sfb->tail)
return -EINVAL;
@ -213,9 +213,10 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
*/
if (sfb->sdbt != get_next_sdbt(tail)) {
debug_sprintf_event(sfdbg, 3, "%s: "
"sampling buffer is not linked: origin %p"
" tail %p\n", __func__,
(void *)sfb->sdbt, (void *)tail);
"sampling buffer is not linked: origin %#lx"
" tail %#lx\n", __func__,
(unsigned long)sfb->sdbt,
(unsigned long)tail);
return -EINVAL;
}
@ -232,6 +233,7 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
sfb->num_sdbt++;
/* Link current page to tail of chain */
*tail = (unsigned long)(void *) new + 1;
tail_prev = tail;
tail = new;
}
@ -241,18 +243,30 @@ static int realloc_sampling_buffer(struct sf_buffer *sfb,
* issue, a new realloc call (if required) might succeed.
*/
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;
}
sfb->num_sdb++;
tail++;
tail_prev = new = NULL; /* Allocated at least one SBD */
}
/* Link sampling buffer to its origin */
*tail = (unsigned long) sfb->sdbt + 1;
sfb->tail = tail;
debug_sprintf_event(sfdbg, 4, "realloc_sampling_buffer: new buffer"
" settings: sdbt %lu sdb %lu\n",
debug_sprintf_event(sfdbg, 4, "%s: new buffer"
" settings: sdbt %lu sdb %lu\n", __func__,
sfb->num_sdbt, sfb->num_sdb);
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);
if (rc) {
free_sampling_buffer(sfb);
debug_sprintf_event(sfdbg, 4, "alloc_sampling_buffer: "
"realloc_sampling_buffer failed with rc %i\n", rc);
debug_sprintf_event(sfdbg, 4, "%s: "
"realloc_sampling_buffer failed with rc %i\n",
__func__, rc);
} else
debug_sprintf_event(sfdbg, 4,
"alloc_sampling_buffer: tear %p dear %p\n",
sfb->sdbt, (void *)*sfb->sdbt);
"%s: tear %#lx dear %#lx\n", __func__,
(unsigned long)sfb->sdbt, (unsigned long)*sfb->sdbt);
return rc;
}
@ -465,8 +480,8 @@ static void sfb_account_overflows(struct cpu_hw_sf *cpuhw,
if (num)
sfb_account_allocs(num, hwc);
debug_sprintf_event(sfdbg, 5, "sfb: overflow: overflow %llu ratio %lu"
" num %lu\n", OVERFLOW_REG(hwc), ratio, num);
debug_sprintf_event(sfdbg, 5, "%s: overflow %llu ratio %lu num %lu\n",
__func__, OVERFLOW_REG(hwc), ratio, num);
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);
if (rc)
debug_sprintf_event(sfdbg, 5, "sfb: extend: realloc "
"failed with rc %i\n", rc);
debug_sprintf_event(sfdbg, 5, "%s: realloc failed with rc %i\n",
__func__, rc);
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",
num, sfb->num_sdb - num_old,
__func__, num, sfb->num_sdb - num_old,
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);
}
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,
unsigned long rate)
{
@ -698,9 +706,9 @@ static unsigned long getrate(bool freq, unsigned long sample,
*/
if (sample_rate_to_freq(si, rate) >
sysctl_perf_event_sample_rate) {
debug_sprintf_event(sfdbg, 1,
debug_sprintf_event(sfdbg, 1, "%s: "
"Sampling rate exceeds maximum "
"perf sample rate\n");
"perf sample rate\n", __func__);
rate = 0;
}
}
@ -745,10 +753,9 @@ static int __hw_perf_event_init_rate(struct perf_event *event,
attr->sample_period = rate;
SAMPL_RATE(hwc) = rate;
hw_init_period(hwc, SAMPL_RATE(hwc));
debug_sprintf_event(sfdbg, 4, "__hw_perf_event_init_rate:"
"cpu:%d period:%#llx freq:%d,%#lx\n", event->cpu,
event->attr.sample_period, event->attr.freq,
SAMPLE_FREQ_MODE(hwc));
debug_sprintf_event(sfdbg, 4, "%s: cpu %d period %#llx freq %d,%#lx\n",
__func__, event->cpu, event->attr.sample_period,
event->attr.freq, SAMPLE_FREQ_MODE(hwc));
return 0;
}
@ -951,8 +958,7 @@ static void cpumsf_pmu_enable(struct pmu *pmu)
* buffer extents
*/
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() */
cpuhw->lsctl.interval = SAMPL_RATE(&cpuhw->event->hw);
@ -973,12 +979,11 @@ static void cpumsf_pmu_enable(struct pmu *pmu)
/* Load current program parameter */
lpp(&S390_lowcore.lpp);
debug_sprintf_event(sfdbg, 6, "pmu_enable: es %i cs %i ed %i cd %i "
"interval %#lx tear %p dear %p\n",
debug_sprintf_event(sfdbg, 6, "%s: es %i cs %i ed %i cd %i "
"interval %#lx tear %#lx dear %#lx\n", __func__,
cpuhw->lsctl.es, cpuhw->lsctl.cs, cpuhw->lsctl.ed,
cpuhw->lsctl.cd, cpuhw->lsctl.interval,
(void *) cpuhw->lsctl.tear,
(void *) cpuhw->lsctl.dear);
cpuhw->lsctl.tear, cpuhw->lsctl.dear);
}
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;
}
} else
debug_sprintf_event(sfdbg, 3, "cpumsf_pmu_disable: "
"qsi() failed with err %i\n", err);
debug_sprintf_event(sfdbg, 3, "%s: qsi() failed with err %i\n",
__func__, err);
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;
/* 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",
__func__, sdbt, te->overflow,
__func__, (unsigned long)sdbt, te->overflow,
(te->f) ? trailer_timestamp(te) : 0ULL);
/* 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);
if (sampl_overflow || event_overflow)
debug_sprintf_event(sfdbg, 4, "%s: "
"overflow stats: sample %llu event %llu\n",
__func__, sampl_overflow, event_overflow);
"overflows: sample %llu event %llu"
" 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)
@ -1424,10 +1431,10 @@ static int aux_output_begin(struct perf_output_handle *handle,
cpuhw->lsctl.tear = base + offset * sizeof(unsigned long);
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)"
"[%#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_SDB_NUM_ALERT(aux), range,
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 "
"diagnostic-sampling mode is full\n",
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;
}
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);
pr_err("Sample data caused the AUX buffer with %lu "
"pages to overflow\n", num_sdb);
debug_sprintf_event(sfdbg, 1, "head %#lx range %#lx "
"overflow %#llx\n",
debug_sprintf_event(sfdbg, 1, "%s: head %#lx range %#lx "
"overflow %#llx\n", __func__,
aux->head, range, overflow);
} else {
size = AUX_SDB_NUM_ALERT(aux) << PAGE_SHIFT;
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",
__func__,
aux->head, aux->alert_mark);
}
}
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",
aux->head, aux->alert_mark, aux->empty_mark,
AUX_SDB_NUM_ALERT(aux), range);
__func__, aux->head, aux->alert_mark,
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);
debug_sprintf_event(sfdbg, 4, "aux_buffer_free: free "
"%lu SDBTs\n", num_sdbt);
debug_sprintf_event(sfdbg, 4, "%s: free "
"%lu SDBTs\n", __func__, num_sdbt);
}
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;
debug_sprintf_event(sfdbg, 4, "aux_buffer_setup: setup %lu SDBTs"
" and %lu SDBs\n",
sfb->num_sdbt, sfb->num_sdb);
debug_sprintf_event(sfdbg, 4, "%s: setup %lu SDBTs and %lu SDBs\n",
__func__, sfb->num_sdbt, sfb->num_sdb);
return aux;
@ -1797,9 +1807,9 @@ static int cpumsf_pmu_check_period(struct perf_event *event, u64 value)
event->attr.sample_period = rate;
SAMPL_RATE(&event->hw) = rate;
hw_init_period(&event->hw, SAMPL_RATE(&event->hw));
debug_sprintf_event(sfdbg, 4, "cpumsf_pmu_check_period:"
"cpu:%d value:%#llx period:%#llx freq:%d\n",
event->cpu, value,
debug_sprintf_event(sfdbg, 4, "%s:"
" cpu %d value %#llx period %#llx freq %d\n",
__func__, event->cpu, value,
event->attr.sample_period, do_freq);
return 0;
}
@ -1875,7 +1885,7 @@ static int cpumsf_pmu_add(struct perf_event *event, int flags)
if (!SAMPL_DIAG_MODE(&event->hw)) {
cpuhw->lsctl.tear = (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,
@ -2030,7 +2040,7 @@ static void cpumf_measurement_alert(struct ext_code ext_code,
/* Report measurement alerts only for non-PRA codes */
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);
/* Sampling authorization change request */

View File

@ -355,7 +355,6 @@ early_initcall(async_stack_realloc);
void __init arch_call_rest_init(void)
{
struct stack_frame *frame;
unsigned long stack;
stack = stack_alloc();
@ -368,13 +367,7 @@ void __init arch_call_rest_init(void)
set_task_stack_end_magic(current);
stack += STACK_INIT_OFFSET;
S390_lowcore.kernel_stack = stack;
frame = (struct stack_frame *) 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));
CALL_ON_STACK_NORETURN(rest_init, stack);
}
static void __init setup_lowcore_dat_off(void)

View File

@ -262,10 +262,13 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
lc->spinlock_index = 0;
lc->percpu_offset = __per_cpu_offset[cpu];
lc->kernel_asce = S390_lowcore.kernel_asce;
lc->user_asce = S390_lowcore.kernel_asce;
lc->machine_flags = S390_lowcore.machine_flags;
lc->user_timer = lc->system_timer =
lc->steal_timer = lc->avg_steal_timer = 0;
__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);
memcpy(lc->stfle_fac_list, S390_lowcore.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();
restore_access_regs(S390_lowcore.access_regs_save_area);
set_cpu_flag(CIF_ASCE_PRIMARY);
set_cpu_flag(CIF_ASCE_SECONDARY);
cpu_init();
preempt_disable();
init_cpu_timer();
@ -871,7 +876,7 @@ static void __no_sanitize_address smp_start_secondary(void *cpuvoid)
S390_lowcore.restart_source = -1UL;
__ctl_load(S390_lowcore.cregs_save_area, 0, 15);
__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 */

View File

@ -9,6 +9,7 @@
#include <linux/stacktrace.h>
#include <asm/stacktrace.h>
#include <asm/unwind.h>
#include <asm/kprobes.h>
void arch_stack_walk(stack_trace_consume_fn consume_entry, void *cookie,
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;
}
}
/*
* 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;
}

View File

@ -36,6 +36,12 @@ static bool update_stack_info(struct unwind_state *state, unsigned long sp)
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)
{
struct stack_info *info = &state->stack_info;
@ -46,20 +52,16 @@ bool unwind_next_frame(struct unwind_state *state)
regs = state->regs;
if (unlikely(regs)) {
if (state->reuse_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;
}
}
sp = state->sp;
sf = (struct stack_frame *) sp;
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
reliable = false;
regs = NULL;
if (!__kernel_text_address(ip)) {
/* skip bogus %r14 */
state->regs = NULL;
return unwind_next_frame(state);
}
} else {
sf = (struct stack_frame *) state->sp;
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 */
sp = state->sp + STACK_FRAME_OVERHEAD;
if (!on_stack(info, sp, sizeof(struct pt_regs)))
goto out_stop;
goto out_err;
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;
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;
}
}
/* 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);
/* Update unwind state */
@ -103,13 +114,11 @@ bool unwind_next_frame(struct unwind_state *state)
EXPORT_SYMBOL_GPL(unwind_next_frame);
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;
unsigned long *mask = &state->stack_mask;
bool reliable, reuse_sp;
struct stack_frame *sf;
unsigned long ip;
unsigned long ip, sp;
memset(state, 0, sizeof(*state));
state->task = task;
@ -121,25 +130,28 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
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 */
if (get_stack_info(sp, task, info, mask) != 0 ||
!on_stack(info, sp, sizeof(struct stack_frame))) {
if (!update_stack_info(state, sp)) {
/* Something is wrong with the stack pointer */
info->type = STACK_TYPE_UNKNOWN;
state->error = true;
return;
}
/* Get the instruction pointer from pt_regs or the stack frame */
if (regs) {
ip = READ_ONCE_NOCHECK(regs->psw.addr);
reliable = true;
reuse_sp = true;
} else {
sf = (struct stack_frame *) sp;
if (!regs) {
/* Stack frame is within valid stack */
sf = (struct stack_frame *)sp;
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
reliable = false;
reuse_sp = false;
}
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 */
state->sp = sp;
state->ip = ip;
state->reliable = reliable;
state->reuse_sp = reuse_sp;
state->reliable = true;
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);

View File

@ -29,13 +29,6 @@
#include <asm/vdso.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;
static void *vdso64_kbase = &vdso64_start;
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_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)
return VM_FAULT_SIGBUS;
@ -76,10 +63,6 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
unsigned long vdso_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)
return -EINVAL;
@ -209,12 +192,10 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
if (!vdso_enabled)
return 0;
if (is_compat_task())
return 0;
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
* the process
@ -267,23 +248,6 @@ static int __init vdso_init(void)
int i;
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 */
vdso64_pages = ((&vdso64_end - &vdso64_start

View File

@ -1 +0,0 @@
vdso32.lds

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: *;
};
}

View File

@ -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

View File

@ -16,10 +16,8 @@
.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)
lm %r4,%r5,__VDSO_GETCPU_VAL(%r0)
sacf 0
ltgr %r2,%r2
jz 2f

View File

@ -11,3 +11,6 @@ lib-$(CONFIG_UPROBES) += probes.o
# Instrumenting memory accesses to __user data (in different address space)
# produce false positives
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
View 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(&regs, 0, sizeof(regs));
regs.psw.addr = get_psw_addr();
regs.gprs[15] = current_stack_pointer();
return test_unwind(NULL,
(u->flags & UWM_REGS) ? &regs : 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");

View File

@ -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)
{
if (S390_lowcore.nodat_stack != 0)
return CALL_ON_STACK(_memcpy_real, S390_lowcore.nodat_stack,
3, dest, src, count);
int rc;
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
* not set up yet. Just call _memcpy_real on the early boot

View File

@ -27,6 +27,7 @@
#include <linux/seq_file.h>
#include <linux/jump_label.h>
#include <linux/pci.h>
#include <linux/printk.h>
#include <asm/isc.h>
#include <asm/airq.h>
@ -659,6 +660,8 @@ static int zpci_alloc_domain(struct zpci_dev *zdev)
spin_lock(&zpci_domain_lock);
if (test_bit(zdev->domain, zpci_domain)) {
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;
}
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);
if (zdev->domain == ZPCI_NR_DEVICES) {
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;
}
set_bit(zdev->domain, zpci_domain);

View File

@ -61,6 +61,7 @@ struct error_hdr {
#define REP82_ERROR_EVEN_MOD_IN_OPND 0x85
#define REP82_ERROR_RESERVED_FIELD 0x88
#define REP82_ERROR_INVALID_DOMAIN_PENDING 0x8A
#define REP82_ERROR_FILTERED_BY_HYPERVISOR 0x8B
#define REP82_ERROR_TRANSPORT_FAIL 0x90
#define REP82_ERROR_PACKET_TRUNCATED 0xA0
#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_PENDING:
case REP82_ERROR_INVALID_SPECIAL_CMD:
case REP82_ERROR_FILTERED_BY_HYPERVISOR:
// REP88_ERROR_INVALID_KEY // '82' CEX2A
// REP88_ERROR_OPERAND // '84' CEX2A
// REP88_ERROR_OPERAND_EVEN_MOD // '85' CEX2A